diff --git a/src/core/kexiproject.h b/src/core/kexiproject.h index c5b4ea927..a360a4095 100644 --- a/src/core/kexiproject.h +++ b/src/core/kexiproject.h @@ -1,400 +1,400 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2012 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIPROJECT_H #define KEXIPROJECT_H #include #include #include #include #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 class QFileInfo; class KDbConnection; class KDbParser; class KexiMainWindow; class KexiWindow; namespace KexiPart { class Part; class Info; struct MissingPart { QString name; QString id; }; typedef QList MissingPartsList; } /** * @brief A single project's controller and data structure. * It contains data connection, state, etc. */ class KEXICORE_EXPORT KexiProject : public QObject, public KDbObject, public KDbResultable { Q_OBJECT public: /*! Constructor 1. Creates a new object using \a pdata. \a handler can be provided to receive error messages during entire KexiProject object's lifetime. */ explicit KexiProject(const KexiProjectData& pdata, KDbMessageHandler* 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(const KexiProjectData& pdata, KDbMessageHandler* handler, KDbConnection* conn); ~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. KDbConnection is created (accessible then with KexiProject::dbConnection()). Since KexiProject inherits KDbObject, 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 a we are connected to a database */ bool isConnected(); /** * @return internal numeric type identifier for plugin ID @a pluginId. * -1 is returned if the ID is unknown. * While the plugin IDs are unique strings like "org.kexi-project.table", * the type identifiers are specific to the given physically stored project * because sets of plugins can differ from between various Kexi installations * and configurations. * @see pluginIdForTypeId() */ int typeIdForPluginId(const QString &pluginId) const; /** * @return plugin ID for a numeric type ID @a typeId. * Empty string is returned if the plugin ID is unknown. * @see typeIdForPluginId() */ QString pluginIdForTypeId(int typeId) const; /** * @return all items of a type \a i in this project */ KexiPart::ItemDict* items(KexiPart::Info *i); /** * @return all items of a plugin ID \a pluginId in this project * It is a convenience function. */ KexiPart::ItemDict* itemsForPluginId(const QString &pluginId); /** * 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 that use a plugin ID \a pluginId into \a list. * You can then sort this list using KexiPart::ItemList::sort(). */ void getSortedItemsForPluginId(KexiPart::ItemList *list, const QString &pluginId); /** * @return item corresponding with plugin ID \a pluginId and name \a name */ KexiPart::Item* itemForPluginId(const QString &pluginId, 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 */ KDbConnection *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). The new widget will be a child of \a parent. */ KexiWindow* openObject(QWidget* parent, KexiPart::Item *item, Kexi::ViewMode viewMode = Kexi::DataViewMode, QMap* staticObjectArgs = 0); //! For convenience KexiWindow* openObject(QWidget* parent, const QString &pluginId, const QString& name, Kexi::ViewMode viewMode = Kexi::DataViewMode); /*! Remove a part instance pointed by \a item. \return true on success. */ bool removeObject(KexiPart::Item* item); /*! Renames a part instance pointed by \a item to a new name \a newName. \return true on success. */ bool renameObject(KexiPart::Item* item, const QString& newName); /*! Renames a part instance pointed by \a item to a new name \a newName. \return true on success. */ bool setObjectCaption(KexiPart::Item* item, const QString& newCaption); /*! 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 KDb::stringToIdentifier(). 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()); //! Added for convenience. KexiPart::Item* createPartItem(KexiPart::Part *part, const QString& suggestedCaption = QString()); /*! Adds item \a item after it is successfully stored as an instance of part pointed by \a info. Also clears 'neverSaved' flag if \a item. Used by KexiWindow::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 previously created with createPartItem(). */ void deleteUnstoredItem(KexiPart::Item *item); /** * @returns parts metioned in the project meta tables but not available locally */ KexiPart::MissingPartsList missingParts() const; KDbParser* 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. + is checked or KexiStartupHandler::global()->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, const KexiProjectData& data, KDbMessageHandler* 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(const KexiProjectData& data, KDbMessageHandler* handler, bool dontAsk = false); //! Helper method to ask user "Could not open file for reading and writing. Do you want to //! open the file as read only?". @return true if user agrees, false if user cancels opening. static bool askForOpeningNonWritableFileAsReadOnly(QWidget *parent, const QFileInfo &finfo); /*! 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(); //! Closes connection. @return true on success. bool closeConnection(); /*! Loads current user's data block, referenced by \a objectID and \a dataID and puts it to \a dataString. \return true on success, false on failure and cancelled when there is no such data block \sa storeUserDataBlock() removeUserDataBlock() copyUserDataBlock() KDbConnection::loadDataBlock(). */ tristate loadUserDataBlock(int objectID, const QString& dataID, QString *dataString); /*! Stores current user's data block \a dataString, referenced by \a objectID and \a dataID. The block will be stored in "kexi__userdata" table If there is already such record in the table, it's simply overwritten. \return true on success \sa loadUserDataBlock() removeUserDataBlock() copyUserDataBlock() KDbConnection::storeDataBlock(). */ bool storeUserDataBlock(int objectID, const QString& dataID, const QString &dataString); /*! Copies urrent user's data blocks referenced by \a sourceObjectID and pointed by optional \a dataID. \return true on success. Does not fail if blocks do not exist. Prior to copying, existing user data blocks are removed even if there is nothing to copy. Copied data blocks will have \a destObjectID object identifier assigned. Note that if \a dataID is not specified, all user data blocks found for the \a sourceObjectID will be copied. \sa loadUserDataBlock() storeUserDataBlock() removeUserDataBlock() KDbConnection::copyDataBlock(). */ bool copyUserDataBlock(int sourceObjectID, int destObjectID, const QString &dataID = QString()); /*! Removes current user's data block referenced by \a objectID and \a dataID. \return true on success. Does not fail if the block does not exist. Note that if \a dataID is not specified, all data blocks for this user and object will be removed. \sa loadUserDataBlock() storeUserDataBlock() copyUserDataBlock() KDbConnection::removeDataBlock(). */ bool removeUserDataBlock(int objectID, const QString& dataID = QString()); 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 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 KDb 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 the KDb 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(const KexiPart::Item& item); //! Closes connection without altering the m_result if is actually set. @return true on success. //! Used on failed operations such as useDatabase(). bool closeConnectionInternal(); Q_SIGNALS: /** signal emitted on error */ void error(const QString &title, KDbObject *obj); /** signal emitted on error (not KDb-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 QString& oldName); /** caption for instance pointed by \a item is changed */ void itemCaptionChanged(const KexiPart::Item &item, const QString& oldCaption); protected: bool createIdForPart(const KexiPart::Info& info); private: /*! Checks whether the project's connection is read-only. If so, error message is set and false is returned. */ bool checkWritable(); /*! Retrieves basic information (pluginId, typeId, name, caption) about all items of all types for this project. @return true on success. */ bool retrieveItems(); /** * Checks project's kexi__part table. * @a singlePluginId can be provided to only check specified part. * Internal identifiers of part(s) are remembered. * * Use @ref missingParts() to get a list of missing parts. * @see typeIdForPluginId() */ bool checkProject(const QString& singlePluginId = QString()); class Private; Private * const d; friend class KexiMainWindow; friend class KexiWindow; friend class Private; }; #endif diff --git a/src/core/kexistartupdata.cpp b/src/core/kexistartupdata.cpp index 6d7362c89..0c16c67a9 100644 --- a/src/core/kexistartupdata.cpp +++ b/src/core/kexistartupdata.cpp @@ -1,242 +1,258 @@ /* This file is part of the KDE project - Copyright (C) 2004-2015 Jarosław Staniek + Copyright (C) 2004-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexistartupdata.h" #include "kexi.h" #include "KexiCommandLineOptions.h" #include "config-kexi.h" #include #include #include #include +// Don't use Q_GLOBAL_STATIC as destroys the object *after* QApplication is gone but we have to cleanup before -> use qAddPostRoutine +static KexiStartupData* s_startupData = nullptr; + class Q_DECL_HIDDEN KexiStartupData::Private { public: Private(); ~Private(); QCommandLineParser parser; KexiCommandLineOptions options; KexiProjectData *projectData; Action action; KexiStartupData::Import importActionData; bool forcedUserMode; bool forcedDesignMode; bool isProjectNavigatorVisible; bool isMainMenuVisible; bool createDB; bool dropDB; bool alsoOpenDB; bool forcedFullScreen; }; KexiStartupData::Private::Private() : options(&parser) , projectData(0), action(KexiStartupData::DoNothing), forcedUserMode(false) , forcedDesignMode(false), isProjectNavigatorVisible(false), forcedFullScreen(false) { } KexiStartupData::Private::~Private() { delete projectData; } KexiStartupData::KexiStartupData() : d(new Private) { + s_startupData = this; } KexiStartupData::~KexiStartupData() { + s_startupData = nullptr; delete d; } +//static +KexiStartupData* KexiStartupData::global() +{ + return s_startupData; +} + KexiProjectData *KexiStartupData::projectData() { return d->projectData; } void KexiStartupData::setProjectData(KexiProjectData *data) { if (data != d->projectData) { delete d->projectData; } d->projectData = data; } KexiStartupData::Action KexiStartupData::action() const { return d->action; } void KexiStartupData::setAction(KexiStartupData::Action action) { d->action = action; } bool KexiStartupData::forcedUserMode() const { return d->forcedUserMode; } void KexiStartupData::setForcedUserMode(bool set) { d->forcedUserMode = set; } bool KexiStartupData::forcedDesignMode() const { return d->forcedDesignMode; } void KexiStartupData::setForcedDesignMode(bool set) { d->forcedDesignMode = set; } bool KexiStartupData::isProjectNavigatorVisible() const { if (d->forcedUserMode && !d->forcedDesignMode) return d->isProjectNavigatorVisible; return true; } void KexiStartupData::setProjectNavigatorVisible(bool set) { d->isProjectNavigatorVisible = set; } bool KexiStartupData::isMainMenuVisible() const { return d->isMainMenuVisible; } void KexiStartupData::setMainMenuVisible(bool set) { d->isMainMenuVisible = set; } KexiStartupData::Import KexiStartupData::importActionData() const { return d->importActionData; } void KexiStartupData::setImportActionData(KexiStartupData::Import import) { d->importActionData = import; } KexiStartupData::Import::Import() { } KexiStartupData::Import::operator bool() const { return !fileName.isEmpty() && !mimeType.isEmpty(); } bool KexiStartupData::forcedFullScreen() const { return d->forcedFullScreen; } void KexiStartupData::setForcedFullScreen(bool set) { d->forcedFullScreen = set; } //! Command line options KexiCommandLineOptions KexiStartupData::options() const { return d->options; } -tristate KexiStartupData::parseOptions() +tristate KexiStartupData::parseOptions(const QStringList &arguments, + const QList &extraOptions) { d->parser.setApplicationDescription(KAboutData::applicationData().shortDescription()); KAboutData::applicationData().setupCommandLine(&d->parser); // adds -h and -v too -#define ADD_OPTION(o) \ - if (!d->parser.addOption(d->options.o)) { \ - qWarning() << "Could not add option" << d->options.o.names(); \ +#define ADD_OPTION_BASE(o) \ + if (!d->parser.addOption(o)) { \ + qWarning() << "Could not add option" << o.names(); \ return false; \ } +#define ADD_OPTION(o) ADD_OPTION_BASE(d->options.o) ADD_OPTION(createDb) ADD_OPTION(createAndOpenDb) ADD_OPTION(dropDb) ADD_OPTION(dbDriver) ADD_OPTION(fileType) ADD_OPTION(connectionShortcut) ADD_OPTION(readOnly) ADD_OPTION(userMode) ADD_OPTION(designMode) ADD_OPTION(showNavigator) ADD_OPTION(hideMenu) ADD_OPTION(open) ADD_OPTION(design) ADD_OPTION(editText) ADD_OPTION(execute) ADD_OPTION(newObject) #ifdef KEXI_QUICK_PRINTING_SUPPORT ADD_OPTION(print) ADD_OPTION(printPreview) #endif ADD_OPTION(user) ADD_OPTION(host) ADD_OPTION(port) ADD_OPTION(localSocket) ADD_OPTION(skipConnDialog) ADD_OPTION(fullScreen) ADD_OPTION(listPlugins) + for(const QCommandLineOption& option : extraOptions) { + ADD_OPTION_BASE(option) + } +#undef ADD_OPTION_BASE #undef ADD_OPTION d->parser.addPositionalArgument("file", xi18nc(" argument description for the command line", "Kexi database project filename, Kexi shortcut filename, or name of a Kexi " "database project on a server to open.")); - //qDebug() << QCoreApplication::instance()->arguments(); - d->parser.process(*qApp); + d->parser.process(arguments); KAboutData::applicationData().processCommandLine(&d->parser); return true; } bool KexiStartupData::isSet(const QCommandLineOption & option) const { return d->parser.isSet(option); } QString KexiStartupData::value(const QCommandLineOption & option) const { return d->parser.value(option); } QStringList KexiStartupData::positionalArguments() const { return d->parser.positionalArguments(); } QString KexiStartupData::helpText() const { return d->parser.helpText(); } diff --git a/src/core/kexistartupdata.h b/src/core/kexistartupdata.h index 7d424fdcc..13e794b29 100644 --- a/src/core/kexistartupdata.h +++ b/src/core/kexistartupdata.h @@ -1,133 +1,138 @@ /* This file is part of the KDE project - Copyright (C) 2004-2015 Jarosław Staniek + Copyright (C) 2004-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXI_STARTUPDATA_H #define KEXI_STARTUPDATA_H #include +#include #include "kexicore_export.h" #include -class QCommandLineOption; class KexiProjectData; class KexiCommandLineOptions; //! Startup data used for storing results of startup operations in Kexi. //! @see KexiStartupHandler class KEXICORE_EXPORT KexiStartupData { public: enum Action { DoNothing, CreateBlankProject, CreateFromTemplate, OpenProject, ImportProject, ShowWelcomeScreen, 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(); + //! @return singleton Startup Handler singleton. + //! @see KexiStartupHandler::global() + static KexiStartupData* global(); + Action action() const; //! \return project data of a project that should be opened (for action()==OpenProject) KexiProjectData *projectData(); //! \return import action's data needed to perform import (for action()==ImportProject) KexiStartupData::Import importActionData() const; /*! \return true if the Design Mode is forced for this project. Used on startup (by --design-mode comman line switch). */ bool forcedDesignMode() const; /*! \return true if 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; /*! \return true if the main menu (usually displayed as the tabbed toolbar) should be visible. */ bool isMainMenuVisible() const; /*! \return true if Kexi started fullscreen. Used on startup (by --fullscreen commandline switch). */ bool forcedFullScreen() const; //! @return command line options KexiCommandLineOptions options() const; - //! Parses the options + //! Parses the options and arguments //! @return true on success - tristate parseOptions(); + tristate parseOptions(const QStringList &arguments, + const QList &extraOptions = QList()); //! @return true if the option @a option was passed to the application bool isSet(const QCommandLineOption & option) const; //! @return the option value found for the given option @a option, or an empty string //! if not found. QString value(const QCommandLineOption & option) const; //! @return a list of positional arguments. //! These are all of the arguments that were not recognized as part of an option. QStringList positionalArguments() const; QString helpText() const; protected: void setAction(Action action); //! Set project data of a project that should be opened (for action()==OpenProject). //! The ownership is passed. void setProjectData(KexiProjectData *data); void setImportActionData(KexiStartupData::Import import); void setForcedDesignMode(bool set); void setForcedUserMode(bool set); void setProjectNavigatorVisible(bool set); void setMainMenuVisible(bool set); void setForcedFullScreen(bool set); private: class Private; Private* const d; }; #endif diff --git a/src/kexiutils/CMakeLists.txt b/src/kexiutils/CMakeLists.txt index f91c712d3..ba5644514 100644 --- a/src/kexiutils/CMakeLists.txt +++ b/src/kexiutils/CMakeLists.txt @@ -1,79 +1,92 @@ add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44024) include_directories(completer) set(kexiutils_LIB_SRCS utils.cpp FontSettings_p.cpp InternalPropertyMap.cpp SmallToolButton.cpp KexiCommandLinkButton.cpp FlowLayout.cpp transliteration_table.cpp kmessagewidget.cpp KexiContextMessage.cpp KexiTitleLabel.cpp KexiLinkWidget.cpp KexiLinkButton.cpp KexiCloseButton.cpp KexiAssistantPage.cpp KexiAssistantWidget.cpp KexiAnimatedLayout.cpp KexiCategorizedView.cpp KexiTester.cpp KexiJsonTrader.cpp KexiPushButton.cpp KexiFadeWidgetEffect.cpp KexiPluginMetaData.cpp completer/KexiCompleter.cpp ) if (KEXI_MOBILE) else () if (KEXI_DEBUG_GUI) list(APPEND kexiutils_LIB_SRCS debuggui.cpp ) endif () endif () +if(BUILD_TESTING) + list(APPEND kexiutils_LIB_SRCS + KexiTestHandler.cpp + ) +endif() + kexi_add_library(kexiutils SHARED ${kexiutils_LIB_SRCS}) set(kexiutils_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ) target_include_directories(kexiutils PUBLIC "$" ) target_link_libraries(kexiutils PUBLIC KF5::KIOWidgets #for KRun... KF5::IconThemes KF5::WidgetsAddons KF5::ConfigWidgets # KStandardAction KColorScheme KF5::I18n KF5::ItemViews # KCategorizedView KCategoryDrawer KDb ) #target_link_libraries(kexiutils LINK_INTERFACE_LIBRARIES KF5::KIOWidgets) +if(BUILD_TESTING) + target_link_libraries(kexiutils + PRIVATE + Qt5::Test + ) +endif() + generate_export_header(kexiutils) install(TARGETS kexiutils ${INSTALL_TARGETS_DEFAULT_ARGS}) if(FALSE) # TODO: install when we move to independent place install( FILES tristate.h utils.h kexiutils_export.h kexiutils_global.h InternalPropertyMap.h SmallToolButton.h FlowLayout.h kmessagewidget.h KexiContextMessage.h KexiTitleLabel.h KexiAssistantPage.h KexiAssistantWidget.h KexiAnimatedLayout.h DESTINATION ${INCLUDE_INSTALL_DIR}/kexiutils COMPONENT Devel) endif() if(BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/src/main/autotests/GlobalSearchTest.h b/src/kexiutils/KexiTestHandler.cpp similarity index 51% rename from src/main/autotests/GlobalSearchTest.h rename to src/kexiutils/KexiTestHandler.cpp index 305a01fc1..6be967d2f 100644 --- a/src/main/autotests/GlobalSearchTest.h +++ b/src/kexiutils/KexiTestHandler.cpp @@ -1,40 +1,57 @@ /* This file is part of the KDE project - Copyright (C) 2012 Jarosław Staniek + Copyright (C) 2012-2017 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ -#ifndef GLOBALSEARCHTEST_H -#define GLOBALSEARCHTEST_H +#include "KexiTestHandler.h" +#include -#include - -class GlobalSearchTest : public QObject +class KexiTestHandler::Private { - Q_OBJECT public: - GlobalSearchTest(int &argc, char **argv, bool goToEventLoop); -private Q_SLOTS: - void initTestCase(); - void testGlobalSearch(); - void cleanupTestCase(); -private: - const int &m_argc; - char **m_argv; - bool m_goToEventLoop; + Private() {} + QList extraOptions; }; -#endif +KexiTestHandler::KexiTestHandler() + : d(new Private) +{ +} + +KexiTestHandler::~KexiTestHandler() +{ + delete d; +} + +QList KexiTestHandler::extraOptions() const +{ + return d->extraOptions; +} + +void KexiTestHandler::addExtraOption(const QCommandLineOption &option) +{ + d->extraOptions.append(option); +} + +void KexiTestHandler::removeOwnOptions(QStringList *args) +{ + for(const QCommandLineOption &extraOption : d->extraOptions) { + for(const QString &name : extraOption.names()) { + args->removeOne("--" + name); + } + } +} diff --git a/src/kexiutils/KexiTestHandler.h b/src/kexiutils/KexiTestHandler.h new file mode 100644 index 000000000..1d19b4bd7 --- /dev/null +++ b/src/kexiutils/KexiTestHandler.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2012-2017 Jarosław Staniek + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXITESTHANDLER_H +#define KEXITESTHANDLER_H + +#include + +#include +#include +#include +#include +#include + +//! A handler for Kexi test objects +class KEXIUTILS_EXPORT KexiTestHandler : public QObject +{ + Q_OBJECT +public: + KexiTestHandler(); + + ~KexiTestHandler(); + + void addExtraOption(const QCommandLineOption &option); + + QList extraOptions() const; + + void removeOwnOptions(QStringList *args); + +private: + class Private; + Private * const d; +}; + +// Override but still use the same macro so Qt Creator lists the test(s) +// Note: don't link agains QtTest lib, otherwise Creator will mark KexiUtils as tests +#undef QTEST_MAIN +#define QTEST_MAIN(TestObject) \ + QT_BEGIN_NAMESPACE \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS \ + QT_END_NAMESPACE \ + int main(int argc, char *argv[]) \ + { \ + QApplication app(argc, argv); \ + app.setAttribute(Qt::AA_Use96Dpi, true); \ + QTEST_DISABLE_KEYPAD_NAVIGATION \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ + TestObject tc; \ + QStringList args(QCoreApplication::arguments()); \ + tc.removeOwnOptions(&args); \ + return QTest::qExec(&tc, args); \ + } + +#endif diff --git a/src/main.cpp b/src/main.cpp index 69b3817cf..46642185d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,37 +1,37 @@ /* This file is part of the KDE project begin : Sun Jun 9 12:15:11 CEST 2002 Copyright (C) 2003 Lucijan Busch Copyright (C) 2003 Joseph Wenninger - Copyright (C) 2003-2005 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this 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 #include
int main(int argc, char *argv[]) { - int result = KexiMainWindow::create(argc, argv); - if (!qApp) + //! @todo use non-GUI app when needed + QApplication app(argc, argv); + const int result = KexiMainWindow::create(QCoreApplication::arguments()); + if (result != 0) { return result; - - result = qApp->exec(); - delete qApp; - return result; + } + return app.exec(); } diff --git a/src/main/KexiMainWindow.cpp b/src/main/KexiMainWindow.cpp index ac3a14f51..d4a2a234f 100644 --- a/src/main/KexiMainWindow.cpp +++ b/src/main/KexiMainWindow.cpp @@ -1,4391 +1,4385 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiMainWindow.h" #include "KexiMainWindow_p.h" #include "kexiactionproxy.h" #include "kexipartmanager.h" #include "kexipart.h" #include "kexipartinfo.h" #include "kexipartguiclient.h" #include "kexiproject.h" #include "kexiprojectdata.h" #include "kexi.h" #include "kexiinternalpart.h" #include "kexiactioncategories.h" #include "kexifinddialog.h" #include "kexisearchandreplaceiface.h" #include "KexiBugReportDialog.h" #define KEXI_SKIP_REGISTERICONSRESOURCE #define KEXI_SKIP_SETUPPRIVATEICONSRESOURCE #include "KexiRegisterResource_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "startup/KexiStartup.h" #include "startup/KexiNewProjectAssistant.h" #include "startup/KexiOpenProjectAssistant.h" #include "startup/KexiWelcomeAssistant.h" #include "startup/KexiImportExportAssistant.h" #include "startup/KexiStartupDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(KexiVDebug) # define KexiVDebug if (0) qDebug() #endif #ifdef HAVE_KCRASH #include //! @todo else, add Breakpad? https://phabricator.kde.org/T1642 #endif KexiDockWidgetStyle::KexiDockWidgetStyle(const QString &baseStyleName) : QProxyStyle(baseStyleName) { } KexiDockWidgetStyle::~KexiDockWidgetStyle() { } void KexiDockWidgetStyle::polish(QWidget* widget) { baseStyle()->polish(widget); widget->setContentsMargins(0, 0, 0, 0); } class Q_DECL_HIDDEN KexiDockWidget::Private { public: Private() {} QSize hint; }; KexiDockWidget::KexiDockWidget(const QString &_tabText, QWidget *parent) : QDockWidget(parent), tabText(_tabText), d(new Private) { // No floatable dockers, Dolphin had problems, we don't want the same... // https://bugs.kde.org/show_bug.cgi?id=288629 // https://bugs.kde.org/show_bug.cgi?id=322299 setFeatures(QDockWidget::NoDockWidgetFeatures);//DockWidgetClosable); setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); setFocusPolicy(Qt::NoFocus); if (style()->objectName().compare("windowsvista", Qt::CaseInsensitive) == 0) { // windowsvista style has broken accelerator visualization support KAcceleratorManager::setNoAccel(this); } KexiDockWidgetStyle *customStyle = new KexiDockWidgetStyle(style()->objectName()); customStyle->setParent(this); setStyle(customStyle); setTitleBarWidget(new QWidget(this)); // hide the title layout()->setContentsMargins(0, 0, 0, 0); layout()->setSpacing(0); } KexiDockWidget::~KexiDockWidget() { delete d; } void KexiDockWidget::paintEvent(QPaintEvent *pe) { Q_UNUSED(pe); QStylePainter p(this); if (isFloating()) { QStyleOptionFrame framOpt; framOpt.initFrom(this); p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt); } // Title must be painted after the frame, since the areas overlap, and // the title may wish to extend out to all sides (eg. XP style) QStyleOptionDockWidgetV2 titleOpt; initStyleOption(&titleOpt); p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt); } void KexiDockWidget::setSizeHint(const QSize& hint) { d->hint = hint; } QSize KexiDockWidget::sizeHint() const { return d->hint.isValid() ? d->hint : QDockWidget::sizeHint(); } //------------------------------------------------- KexiMainWindowTabWidget::KexiMainWindowTabWidget(QWidget *parent, KexiMainWidget* mainWidget) : QTabWidget(parent) , m_mainWidget(mainWidget) , m_tabIndex(-1) { m_closeAction = new QAction(koIcon("tab-close"), xi18n("&Close Tab"), this); m_closeAction->setToolTip(xi18n("Close the current tab")); m_closeAction->setWhatsThis(xi18n("Closes the current tab.")); m_closeAllTabsAction = new QAction(xi18n("Cl&ose All Tabs"), this); m_closeAllTabsAction->setToolTip(xi18n("Close all tabs")); m_closeAllTabsAction->setWhatsThis(xi18n("Closes all tabs.")); connect(m_closeAction, SIGNAL(triggered()), this, SLOT(closeTab())); connect(m_closeAllTabsAction, SIGNAL(triggered()), this, SLOT(closeAllTabs())); //! @todo insert window list in the corner widget as in firefox #if 0 // close-tab button: QToolButton* rightWidget = new QToolButton(this); rightWidget->setDefaultAction(m_closeAction); rightWidget->setText(QString()); rightWidget->setAutoRaise(true); rightWidget->adjustSize(); setCornerWidget(rightWidget, Qt::TopRightCorner); #endif setMovable(true); setDocumentMode(true); tabBar()->setExpanding(true); } KexiMainWindowTabWidget::~KexiMainWindowTabWidget() { } void KexiMainWindowTabWidget::paintEvent(QPaintEvent * event) { if (count() > 0) QTabWidget::paintEvent(event); else QWidget::paintEvent(event); } void KexiMainWindowTabWidget::mousePressEvent(QMouseEvent *event) { //! @todo KEXI3 test KexiMainWindowTabWidget's contextMenu event port from KTabWidget if (event->button() == Qt::RightButton) { int tab = tabBar()->tabAt(event->pos()); const QPoint realPos(tabBar()->mapToGlobal(event->pos())); if (QRect(tabBar()->mapToGlobal(QPoint(0,0)), tabBar()->mapToGlobal(QPoint(tabBar()->width()-1, tabBar()->height()-1))).contains(realPos)) { showContextMenuForTab(tab, tabBar()->mapToGlobal(event->pos())); return; } } QTabWidget::mousePressEvent(event); } void KexiMainWindowTabWidget::closeTab() { KexiMainWindow *main = dynamic_cast(KexiMainWindowIface::global()); if (main) { main->closeWindowForTab(m_tabIndex); } } tristate KexiMainWindowTabWidget::closeAllTabs() { tristate alternateResult = true; QList windowList; KexiMainWindow *main = dynamic_cast(KexiMainWindowIface::global()); if (!main) { return alternateResult; } for (int i = 0; i < count(); i++) { KexiWindow *window = main->windowForTab(i); if (window) { windowList.append(window); } } foreach (KexiWindow *window, windowList) { tristate result = main->closeWindow(window); if (result != true && result != false) { return result; } if (result == false) { alternateResult = false; } } return alternateResult; } void KexiMainWindowTabWidget::showContextMenuForTab(int index, const QPoint& point) { QMenu menu; if (index >= 0) { menu.addAction(m_closeAction); } if (count() > 0) { menu.addAction(m_closeAllTabsAction); } //! @todo add "&Detach Tab" if (menu.actions().isEmpty()) { return; } setTabIndexFromContextMenu(index); menu.exec(point); } void KexiMainWindowTabWidget::setTabIndexFromContextMenu(int clickedIndex) { if (currentIndex() == -1) { m_tabIndex = -1; return; } m_tabIndex = clickedIndex; } //------------------------------------------------- static bool setupIconTheme(KLocalizedString *errorMessage, KLocalizedString *detailsErrorMessage) { // Register kexi resource first to have priority over the standard breeze theme. // For example "table" icon exists in both resources. if (!registerResource("icons/kexi_breeze.rcc", QStandardPaths::AppDataLocation, QString(), QString(), errorMessage, detailsErrorMessage) || !registerGlobalBreezeIconsResource(errorMessage, detailsErrorMessage)) { return false; } setupBreezeIconTheme(); // tell KIconLoader an co. about the theme KConfigGroup cg(KSharedConfig::openConfig(), "Icons"); cg.writeEntry("Theme", "breeze"); cg.sync(); return true; } //! @todo 3.1 replace with KexiStyle bool setupApplication() { #if defined Q_OS_WIN || defined Q_OS_MAC // Only this style matches current Kexi theme and can be supported/tested const char *name = "breeze"; QScopedPointer style(QStyleFactory::create(name)); if (!style || style->objectName() != name) { qWarning() << qPrintable(QString("Could not find application style %1. " "Kexi will not start. Please check if Kexi is properly installed. ") .arg(name)); return false; } qApp->setStyle(style.take()); #endif return true; } //static -int KexiMainWindow::create(int &argc, char *argv[], const QString &componentName) +int KexiMainWindow::create(const QStringList &arguments, const QString &componentName, + const QList &extraOptions) { - QApplication *app = qApp; - QScopedPointer guard; - if (!app) { - //! @todo use non-GUI app when needed - guard.reset(app = new QApplication(argc, argv)); - } - app->setQuitOnLastWindowClosed(false); + qApp->setQuitOnLastWindowClosed(false); KLocalizedString::setApplicationDomain("kexi"); //! @todo KEXI3 app->setAttribute(Qt::AA_UseHighDpiPixmaps, true); KexiAboutData aboutData; if (!componentName.isEmpty()) { aboutData.setComponentName(componentName); } KAboutData::setApplicationData(aboutData); if (!setupApplication()) { return 1; } #ifdef HAVE_KCRASH KCrash::initialize(); #endif KLocalizedString errorMessage; KLocalizedString detailsErrorMessage; if (!setupIconTheme(&errorMessage, &detailsErrorMessage)) { if (detailsErrorMessage.isEmpty()) { KMessageBox::error(nullptr, errorMessage.toString()); } else { KMessageBox::detailedError(nullptr, errorMessage.toString(), detailsErrorMessage.toString()); } qWarning() << qPrintable(errorMessage.toString(Kuit::PlainText)); return 1; } QApplication::setWindowIcon(koIcon("kexi")); - tristate res = Kexi::startupHandler().init(); + const tristate res = KexiStartupHandler::global()->init(arguments, extraOptions); if (!res || ~res) { return (~res) ? 0 : 1; } //qDebug() << "startupActions OK"; /* Exit requested, e.g. after database removing. */ - if (Kexi::startupHandler().action() == KexiStartupData::Exit) { + if (KexiStartupHandler::global()->action() == KexiStartupData::Exit) { return 0; } KexiMainWindow *win = new KexiMainWindow(); #ifdef KEXI_DEBUG_GUI QWidget* debugWindow = 0; KConfigGroup generalGroup = KSharedConfig::openConfig()->group("General"); if (generalGroup.readEntry("ShowInternalDebugger", false)) { debugWindow = KexiUtils::createDebugWindow(win); debugWindow->show(); } #endif if (true != win->startup()) { delete win; return 1; } win->restoreSettings(); win->show(); #ifdef KEXI_DEBUG_GUI win->raise(); static_cast(win)->activateWindow(); #endif /*foreach (QWidget *widget, QApplication::topLevelWidgets()) { qDebug() << widget; }*/ - guard.take(); return 0; } //------------------------------------------------- KexiMainMenuActionShortcut::KexiMainMenuActionShortcut(const QKeySequence& key, QAction *action, QWidget *parent) : QShortcut(key, parent) , m_action(action) { connect(this, SIGNAL(activated()), this, SLOT(slotActivated())); } KexiMainMenuActionShortcut::~KexiMainMenuActionShortcut() { } void KexiMainMenuActionShortcut::slotActivated() { if (!m_action->isEnabled()) { return; } m_action->trigger(); } //------------------------------------------------- KexiMainWindow::KexiMainWindow(QWidget *parent) : KexiMainWindowSuper(parent) , KexiMainWindowIface() , KexiGUIMessageHandler(this) , d(new KexiMainWindow::Private(this)) { setObjectName("KexiMainWindow"); setAttribute(Qt::WA_DeleteOnClose); kexiTester() << KexiTestObject(this); if (d->userMode) qDebug() << "starting up in the User Mode"; setAsDefaultHost(); //this is default host now. //get informed connect(&Kexi::partManager(), SIGNAL(partLoaded(KexiPart::Part*)), this, SLOT(slotPartLoaded(KexiPart::Part*))); connect(&Kexi::partManager(), SIGNAL(newObjectRequested(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); setAcceptDrops(true); setupActions(); setupMainWidget(); updateAppCaption(); if (!d->userMode) { setupContextHelp(); setupPropertyEditor(); } invalidateActions(); d->timer.singleShot(0, this, SLOT(slotLastActions())); - if (Kexi::startupHandler().forcedFullScreen()) { + if (KexiStartupHandler::global()->forcedFullScreen()) { toggleFullScreen(true); } // --- global config //! @todo move to specialized KexiConfig class KConfigGroup tablesGroup(d->config->group("Tables")); const int defaultMaxLengthForTextFields = tablesGroup.readEntry("DefaultMaxLengthForTextFields", int(-1)); if (defaultMaxLengthForTextFields >= 0) { KDbField::setDefaultMaxLength(defaultMaxLengthForTextFields); } // --- /global config } KexiMainWindow::~KexiMainWindow() { d->forceWindowClosing = true; closeProject(); delete d; Kexi::deleteGlobalObjects(); } KexiProject *KexiMainWindow::project() { return d->prj; } QList KexiMainWindow::allActions() const { return actionCollection()->actions(); } KActionCollection *KexiMainWindow::actionCollection() const { return d->actionCollection; } KexiWindow* KexiMainWindow::currentWindow() const { return windowForTab(d->mainWidget->tabWidget()->currentIndex()); } KexiWindow* KexiMainWindow::windowForTab(int tabIndex) const { if (!d->mainWidget->tabWidget()) return 0; KexiWindowContainer *windowContainer = dynamic_cast(d->mainWidget->tabWidget()->widget(tabIndex)); if (!windowContainer) return 0; return windowContainer->window; } void KexiMainWindow::setupMainMenuActionShortcut(QAction * action) { if (!action->shortcut().isEmpty()) { foreach(const QKeySequence &shortcut, action->shortcuts()) { (void)new KexiMainMenuActionShortcut(shortcut, action, this); } } } static void addThreeDotsToActionText(QAction* action) { action->setText(xi18nc("Action name with three dots...", "%1...", action->text())); } QAction * KexiMainWindow::addAction(const char *name, const QIcon &icon, const QString& text, const char *shortcut) { QAction *action = icon.isNull() ? new QAction(text, this) : new QAction(icon, text, this); actionCollection()->addAction(name, action); if (shortcut) { action->setShortcut(QKeySequence(shortcut)); QShortcut *s = new QShortcut(action->shortcut(), this); connect(s, SIGNAL(activated()), action, SLOT(trigger())); } return action; } QAction * KexiMainWindow::addAction(const char *name, const QString& text, const char *shortcut) { return addAction(name, QIcon(), text, shortcut); } void KexiMainWindow::setupActions() { KActionCollection *ac = actionCollection(); // PROJECT MENU QAction *action; ac->addAction("project_new", action = new KexiMenuWidgetAction(KStandardAction::New, this)); addThreeDotsToActionText(action); action->setShortcuts(KStandardShortcut::openNew()); action->setToolTip(xi18n("Create a new project")); action->setWhatsThis( xi18n("Creates a new project. Currently opened project is not affected.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectNew())); setupMainMenuActionShortcut(action); ac->addAction("project_open", action = new KexiMenuWidgetAction(KStandardAction::Open, this)); action->setToolTip(xi18n("Open an existing project")); action->setWhatsThis( xi18n("Opens an existing project. Currently opened project is not affected.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectOpen())); setupMainMenuActionShortcut(action); { ac->addAction("project_welcome", action = d->action_project_welcome = new KexiMenuWidgetAction( QIcon(), xi18n("Welcome"), this)); addThreeDotsToActionText(action); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectWelcome())); setupMainMenuActionShortcut(action); action->setToolTip(xi18n("Show Welcome page")); action->setWhatsThis( xi18n("Shows Welcome page with list of recently opened projects and other information. ")); } ac->addAction("project_save", d->action_save = KStandardAction::save(this, SLOT(slotProjectSave()), this)); d->action_save->setToolTip(xi18n("Save object changes")); d->action_save->setWhatsThis(xi18n("Saves object changes from currently selected window.")); setupMainMenuActionShortcut(d->action_save); d->action_save_as = addAction("project_saveas", koIcon("document-save-as"), xi18n("Save &As...")); d->action_save_as->setToolTip(xi18n("Save object as")); d->action_save_as->setWhatsThis( xi18n("Saves object from currently selected window under a new name (within the same project).")); connect(d->action_save_as, SIGNAL(triggered()), this, SLOT(slotProjectSaveAs())); #ifdef KEXI_SHOW_UNIMPLEMENTED ac->addAction("project_properties", action = d->action_project_properties = new KexiMenuWidgetAction( koIcon("document-properties"), futureI18n("Project Properties"), this)); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectProperties())); setupMainMenuActionShortcut(action); #else d->action_project_properties = d->dummy_action; #endif //! @todo replace document-import icon with something other ac->addAction("project_import_export_send", action = d->action_project_import_export_send = new KexiMenuWidgetAction( koIcon("document-import"), xi18n("&Import, Export or Send..."), this)); action->setToolTip(xi18n("Import, export or send project")); action->setWhatsThis( xi18n("Imports, exports or sends project.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectImportExportOrSend())); setupMainMenuActionShortcut(action); ac->addAction("project_close", action = d->action_close = new KexiMenuWidgetAction( koIcon("window-close"), xi18nc("Close Project", "&Close"), this)); action->setToolTip(xi18n("Close the current project")); action->setWhatsThis(xi18n("Closes the current project.")); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectClose())); setupMainMenuActionShortcut(action); ac->addAction("quit", action = new KexiMenuWidgetAction(KStandardAction::Quit, this)); connect(action, SIGNAL(triggered()), this, SLOT(slotProjectQuit())); action->setWhatsThis(xi18n("Quits Kexi application.")); setupMainMenuActionShortcut(action); #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_project_relations = addAction("project_relations", KexiIcon("database-relations"), futureI18n("&Relationships..."), "Ctrl+R"); d->action_project_relations->setToolTip(futureI18n("Project relationships")); d->action_project_relations->setWhatsThis(futureI18n("Shows project relationships.")); connect(d->action_project_relations, SIGNAL(triggered()), this, SLOT(slotProjectRelations())); #else d->action_project_relations = d->dummy_action; #endif d->action_tools_import_project = addAction("tools_import_project", KexiIcon("database-import"), xi18n("&Import Database...")); d->action_tools_import_project->setToolTip(xi18n("Import entire database as a Kexi project")); d->action_tools_import_project->setWhatsThis( xi18n("Imports entire database as a Kexi project.")); connect(d->action_tools_import_project, SIGNAL(triggered()), this, SLOT(slotToolsImportProject())); d->action_tools_data_import = addAction("tools_import_tables", koIcon("document-import"), xi18n("Import Tables...")); d->action_tools_data_import->setToolTip(xi18n("Import data from an external source into this project")); d->action_tools_data_import->setWhatsThis(xi18n("Imports data from an external source into this project.")); connect(d->action_tools_data_import, SIGNAL(triggered()), this, SLOT(slotToolsImportTables())); d->action_tools_compact_database = addAction("tools_compact_database", //! @todo icon koIcon("application-x-compress"), xi18n("&Compact Database...")); d->action_tools_compact_database->setToolTip(xi18n("Compact the current database project")); d->action_tools_compact_database->setWhatsThis( xi18n("Compacts the current database project, so it will take less space and work faster.")); connect(d->action_tools_compact_database, SIGNAL(triggered()), this, SLOT(slotToolsCompactDatabase())); if (d->userMode) d->action_project_import_data_table = 0; else { d->action_project_import_data_table = addAction("project_import_data_table", KexiIcon("document-empty"), /*! @todo: change to "file_import" with a table or so */ xi18nc("Import->Table Data From File...", "Import Data From &File...")); d->action_project_import_data_table->setToolTip(xi18n("Import table data from a file")); d->action_project_import_data_table->setWhatsThis(xi18n("Imports table data from a file.")); connect(d->action_project_import_data_table, SIGNAL(triggered()), this, SLOT(slotProjectImportDataTable())); } d->action_project_export_data_table = addAction("project_export_data_table", KexiIcon("table"), /*! @todo: change to "file_export" with a table or so */ xi18nc("Export->Table or Query Data to File...", "Export Data to &File...")); d->action_project_export_data_table->setToolTip( xi18n("Export data from the active table or query to a file")); d->action_project_export_data_table->setWhatsThis( xi18n("Exports data from the active table or query to a file.")); connect(d->action_project_export_data_table, SIGNAL(triggered()), this, SLOT(slotProjectExportDataTable())); //! @todo new QAction(xi18n("From File..."), "document-open", 0, //! this, SLOT(slotImportFile()), actionCollection(), "project_import_file"); //! @todo new QAction(xi18n("From Server..."), "network-server-database", 0, //! this, SLOT(slotImportServer()), actionCollection(), "project_import_server"); #ifdef KEXI_QUICK_PRINTING_SUPPORT ac->addAction("project_print", d->action_project_print = KStandardAction::print(this, SLOT(slotProjectPrint()), this)); d->action_project_print->setToolTip(futureI18n("Print data from the active table or query")); d->action_project_print->setWhatsThis(futureI18n("Prints data from the active table or query.")); ac->addAction("project_print_preview", d->action_project_print_preview = KStandardAction::printPreview( this, SLOT(slotProjectPrintPreview()), this)); d->action_project_print_preview->setToolTip( futureI18n("Show print preview for the active table or query")); d->action_project_print_preview->setWhatsThis( futureI18n("Shows print preview for the active table or query.")); d->action_project_print_setup = addAction("project_print_setup", koIcon("configure"), futureI18n("Print Set&up...")); //!< @todo document-page-setup could be a better icon d->action_project_print_setup->setToolTip( futureI18n("Show print setup for the active table or query")); d->action_project_print_setup->setWhatsThis( futureI18n("Shows print setup for the active table or query.")); connect(d->action_project_print_setup, SIGNAL(triggered()), this, SLOT(slotProjectPageSetup())); #endif //EDIT MENU d->action_edit_cut = createSharedAction(KStandardAction::Cut); d->action_edit_copy = createSharedAction(KStandardAction::Copy); d->action_edit_paste = createSharedAction(KStandardAction::Paste); if (d->userMode) d->action_edit_paste_special_data_table = 0; else { d->action_edit_paste_special_data_table = addAction( "edit_paste_special_data_table", d->action_edit_paste->icon(), xi18nc("Paste Special->As Data &Table...", "Paste Special...")); d->action_edit_paste_special_data_table->setToolTip( xi18n("Paste clipboard data as a table")); d->action_edit_paste_special_data_table->setWhatsThis( xi18n("Pastes clipboard data as a table.")); connect(d->action_edit_paste_special_data_table, SIGNAL(triggered()), this, SLOT(slotEditPasteSpecialDataTable())); } d->action_edit_copy_special_data_table = addAction( "edit_copy_special_data_table", KexiIcon("table"), xi18nc("Copy Special->Table or Query Data...", "Copy Special...")); d->action_edit_copy_special_data_table->setToolTip( xi18n("Copy selected table or query data to clipboard")); d->action_edit_copy_special_data_table->setWhatsThis( xi18n("Copies selected table or query data to clipboard.")); connect(d->action_edit_copy_special_data_table, SIGNAL(triggered()), this, SLOT(slotEditCopySpecialDataTable())); d->action_edit_undo = createSharedAction(KStandardAction::Undo); d->action_edit_undo->setWhatsThis(xi18n("Reverts the most recent editing action.")); d->action_edit_redo = createSharedAction(KStandardAction::Redo); d->action_edit_redo->setWhatsThis(xi18n("Reverts the most recent undo action.")); ac->addAction("edit_find", d->action_edit_find = KStandardAction::find( this, SLOT(slotEditFind()), this)); d->action_edit_find->setToolTip(xi18n("Find text")); d->action_edit_find->setWhatsThis(xi18n("Looks up the first occurrence of a piece of text.")); ac->addAction("edit_findnext", d->action_edit_findnext = KStandardAction::findNext( this, SLOT(slotEditFindNext()), this)); ac->addAction("edit_findprevious", d->action_edit_findprev = KStandardAction::findPrev( this, SLOT(slotEditFindPrevious()), this)); d->action_edit_replace = 0; //! @todo d->action_edit_replace = KStandardAction::replace( //! this, SLOT(slotEditReplace()), actionCollection(), "project_print_preview" ); d->action_edit_replace_all = 0; //! @todo d->action_edit_replace_all = new QAction( xi18n("Replace All"), "", 0, //! this, SLOT(slotEditReplaceAll()), actionCollection(), "edit_replaceall"); d->action_edit_select_all = createSharedAction(KStandardAction::SelectAll); d->action_edit_delete = createSharedAction(xi18n("&Delete"), koIconName("edit-delete"), QKeySequence(), "edit_delete"); d->action_edit_delete->setToolTip(xi18n("Delete selected object")); d->action_edit_delete->setWhatsThis(xi18n("Deletes currently selected object.")); d->action_edit_delete_row = createSharedAction(xi18n("Delete Record"), KexiIconName("edit-table-delete-row"), QKeySequence(Qt::CTRL + Qt::Key_Delete), "edit_delete_row"); d->action_edit_delete_row->setToolTip(xi18n("Delete the current record")); d->action_edit_delete_row->setWhatsThis(xi18n("Deletes the current record.")); d->action_edit_clear_table = createSharedAction(xi18n("Clear Table Contents..."), KexiIconName("edit-table-clear"), QKeySequence(), "edit_clear_table"); d->action_edit_clear_table->setToolTip(xi18n("Clear table contents")); d->action_edit_clear_table->setWhatsThis(xi18n("Clears table contents.")); setActionVolatile(d->action_edit_clear_table, true); d->action_edit_edititem = createSharedAction(xi18n("Edit Item"), QString(), QKeySequence(), /* CONFLICT in TV: Qt::Key_F2, */ "edit_edititem"); d->action_edit_edititem->setToolTip(xi18n("Edit currently selected item")); d->action_edit_edititem->setWhatsThis(xi18n("Edits currently selected item.")); d->action_edit_insert_empty_row = createSharedAction(xi18n("&Insert Empty Row"), KexiIconName("edit-table-insert-row"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Insert), "edit_insert_empty_row"); setActionVolatile(d->action_edit_insert_empty_row, true); d->action_edit_insert_empty_row->setToolTip(xi18n("Insert one empty row above")); d->action_edit_insert_empty_row->setWhatsThis( xi18n("Inserts one empty row above currently selected table row.")); //VIEW MENU /* UNUSED, see KexiToggleViewModeAction if (!d->userMode) { d->action_view_mode = new QActionGroup(this); ac->addAction( "view_data_mode", d->action_view_data_mode = new KToggleAction( KexiIcon("data-view"), xi18n("&Data View"), d->action_view_mode) ); // d->action_view_data_mode->setObjectName("view_data_mode"); d->action_view_data_mode->setShortcut(QKeySequence("F6")); //d->action_view_data_mode->setExclusiveGroup("view_mode"); d->action_view_data_mode->setToolTip(xi18n("Switch to data view")); d->action_view_data_mode->setWhatsThis(xi18n("Switches to data view.")); d->actions_for_view_modes.insert( Kexi::DataViewMode, d->action_view_data_mode ); connect(d->action_view_data_mode, SIGNAL(triggered()), this, SLOT(slotViewDataMode())); } else { d->action_view_mode = 0; d->action_view_data_mode = 0; } if (!d->userMode) { ac->addAction( "view_design_mode", d->action_view_design_mode = new KToggleAction( KexiIcon("design-view"), xi18n("D&esign View"), d->action_view_mode) ); // d->action_view_design_mode->setObjectName("view_design_mode"); d->action_view_design_mode->setShortcut(QKeySequence("F7")); //d->action_view_design_mode->setExclusiveGroup("view_mode"); d->action_view_design_mode->setToolTip(xi18n("Switch to design view")); d->action_view_design_mode->setWhatsThis(xi18n("Switches to design view.")); d->actions_for_view_modes.insert( Kexi::DesignViewMode, d->action_view_design_mode ); connect(d->action_view_design_mode, SIGNAL(triggered()), this, SLOT(slotViewDesignMode())); } else d->action_view_design_mode = 0; if (!d->userMode) { ac->addAction( "view_text_mode", d->action_view_text_mode = new KToggleAction( KexiIcon("sql-view"), xi18n("&Text View"), d->action_view_mode) ); d->action_view_text_mode->setObjectName("view_text_mode"); d->action_view_text_mode->setShortcut(QKeySequence("F8")); //d->action_view_text_mode->setExclusiveGroup("view_mode"); d->action_view_text_mode->setToolTip(xi18n("Switch to text view")); d->action_view_text_mode->setWhatsThis(xi18n("Switches to text view.")); d->actions_for_view_modes.insert( Kexi::TextViewMode, d->action_view_text_mode ); connect(d->action_view_text_mode, SIGNAL(triggered()), this, SLOT(slotViewTextMode())); } else d->action_view_text_mode = 0; */ if (d->isProjectNavigatorVisible) { d->action_show_nav = addAction("view_navigator", xi18n("Show Project Navigator"), "Alt+0"); d->action_show_nav->setToolTip(xi18n("Show the Project Navigator pane")); d->action_show_nav->setWhatsThis(xi18n("Shows the Project Navigator pane.")); connect(d->action_show_nav, SIGNAL(triggered()), this, SLOT(slotShowNavigator())); } else { d->action_show_nav = 0; } if (d->isProjectNavigatorVisible) { // Shortcut taken from "Activate Projects pane" http://doc.qt.io/qtcreator/creator-keyboard-shortcuts.html d->action_activate_nav = addAction("activate_navigator", xi18n("Activate Project Navigator"), "Alt+X"); d->action_activate_nav->setToolTip(xi18n("Activate the Project Navigator pane")); d->action_activate_nav->setWhatsThis(xi18n("Activates the Project Navigator pane. If it is hidden, shows it first.")); connect(d->action_activate_nav, SIGNAL(triggered()), this, SLOT(slotActivateNavigator())); } else { d->action_activate_nav = 0; } d->action_activate_mainarea = addAction("activate_mainarea", xi18n("Activate main area") // , "Alt+2"? //! @todo activate_mainarea: pressing Esc in project nav or propeditor should move back to the main area ); d->action_activate_mainarea->setToolTip(xi18n("Activate the main area")); d->action_activate_mainarea->setWhatsThis(xi18n("Activates the main area.")); connect(d->action_activate_mainarea, SIGNAL(triggered()), this, SLOT(slotActivateMainArea())); //! @todo windows with "_3" prefix have conflicting auto shortcut set to Alt+3 -> remove that! if (!d->userMode) { d->action_show_propeditor = addAction("view_propeditor", xi18n("Show Property Editor"), "Alt+3"); d->action_show_propeditor->setToolTip(xi18n("Show the Property Editor pane")); d->action_show_propeditor->setWhatsThis(xi18n("Shows the Property Editor pane.")); connect(d->action_show_propeditor, SIGNAL(triggered()), this, SLOT(slotShowPropertyEditor())); } else { d->action_show_propeditor = 0; } if (!d->userMode) { d->action_activate_propeditor = addAction("activate_propeditor", xi18n("Activate Property Editor"), "Alt+-"); d->action_activate_propeditor->setToolTip(xi18n("Activate the Property Editor pane")); d->action_activate_propeditor->setWhatsThis(xi18n("Activates the Property Editor pane. If it is hidden, shows it first.")); connect(d->action_activate_propeditor, SIGNAL(triggered()), this, SLOT(slotActivatePropertyEditor())); } else { d->action_activate_propeditor = 0; } d->action_view_global_search = addAction("view_global_search", xi18n("Switch to Global Search"), "Ctrl+K"); d->action_view_global_search->setToolTip(xi18n("Switch to Global Search box")); d->action_view_global_search->setWhatsThis(xi18n("Switches to Global Search box.")); // (connection is added elsewhere) //DATA MENU d->action_data_save_row = createSharedAction(xi18n("&Save Record"), koIconName("dialog-ok"), QKeySequence(Qt::SHIFT + Qt::Key_Return), "data_save_row"); d->action_data_save_row->setToolTip(xi18n("Save changes made to the current record")); d->action_data_save_row->setWhatsThis(xi18n("Saves changes made to the current record.")); //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_save_row, true ); d->action_data_cancel_row_changes = createSharedAction(xi18n("&Cancel Record Changes"), koIconName("dialog-cancel"), QKeySequence(Qt::Key_Escape), "data_cancel_row_changes"); d->action_data_cancel_row_changes->setToolTip( xi18n("Cancel changes made to the current record")); d->action_data_cancel_row_changes->setWhatsThis( xi18n("Cancels changes made to the current record.")); //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_cancel_row_changes, true ); d->action_data_execute = createSharedAction( xi18n("&Execute"), koIconName("media-playback-start"), QKeySequence(), "data_execute"); //! @todo d->action_data_execute->setToolTip(xi18n("")); //! @todo d->action_data_execute->setWhatsThis(xi18n("")); #ifdef KEXI_SHOW_UNIMPLEMENTED action = createSharedAction(futureI18n("&Filter"), koIconName("view-filter"), QKeySequence(), "data_filter"); setActionVolatile(action, true); #endif //! @todo action->setToolTip(xi18n("")); //! @todo action->setWhatsThis(xi18n("")); // - record-navigation related actions createSharedAction(KexiRecordNavigator::Actions::moveToFirstRecord(), QKeySequence(), "data_go_to_first_record"); createSharedAction(KexiRecordNavigator::Actions::moveToPreviousRecord(), QKeySequence(), "data_go_to_previous_record"); createSharedAction(KexiRecordNavigator::Actions::moveToNextRecord(), QKeySequence(), "data_go_to_next_record"); createSharedAction(KexiRecordNavigator::Actions::moveToLastRecord(), QKeySequence(), "data_go_to_last_record"); createSharedAction(KexiRecordNavigator::Actions::moveToNewRecord(), QKeySequence(), "data_go_to_new_record"); //FORMAT MENU d->action_format_font = createSharedAction(xi18n("&Font..."), koIconName("fonts-package"), QKeySequence(), "format_font"); d->action_format_font->setToolTip(xi18n("Change font for selected object")); d->action_format_font->setWhatsThis(xi18n("Changes font for selected object.")); //TOOLS MENU //WINDOW MENU //additional 'Window' menu items d->action_window_next = addAction("window_next", xi18n("&Next Window"), #ifdef Q_OS_WIN "Ctrl+Tab" #else "Alt+Right" #endif ); d->action_window_next->setToolTip(xi18n("Next window")); d->action_window_next->setWhatsThis(xi18n("Switches to the next window.")); connect(d->action_window_next, SIGNAL(triggered()), this, SLOT(activateNextWindow())); d->action_window_previous = addAction("window_previous", xi18n("&Previous Window"), #ifdef Q_OS_WIN "Ctrl+Shift+Tab" #else "Alt+Left" #endif ); d->action_window_previous->setToolTip(xi18n("Previous window")); d->action_window_previous->setWhatsThis(xi18n("Switches to the previous window.")); connect(d->action_window_previous, SIGNAL(triggered()), this, SLOT(activatePreviousWindow())); d->action_window_fullscreen = KStandardAction::fullScreen(this, SLOT(toggleFullScreen(bool)), this, ac); ac->addAction("full_screen", d->action_window_fullscreen); QList shortcuts; shortcuts << d->action_window_fullscreen->shortcut() << QKeySequence("F11"); d->action_window_fullscreen->setShortcuts(shortcuts); QShortcut *s = new QShortcut(d->action_window_fullscreen->shortcut(), this); connect(s, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger())); if (d->action_window_fullscreen->shortcuts().count() > 1) { QShortcut *sa = new QShortcut(d->action_window_fullscreen->shortcuts().value(1), this); connect(sa, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger())); } //SETTINGS MENU //! @todo put 'configure keys' into settings view #ifdef KEXI_SHOW_UNIMPLEMENTED //! @todo toolbars configuration will be handled in a special way #endif #ifdef KEXI_MACROS_SUPPORT Kexi::tempShowMacros() = true; #else Kexi::tempShowMacros() = false; #endif #ifdef KEXI_SCRIPTS_SUPPORT Kexi::tempShowScripts() = true; #else Kexi::tempShowScripts() = false; #endif #ifdef KEXI_SHOW_UNIMPLEMENTED //! @todo implement settings window in a specific way ac->addAction("settings", action = d->action_settings = new KexiMenuWidgetAction( KStandardAction::Preferences, this)); action->setObjectName("settings"); action->setText(futureI18n("Settings...")); action->setToolTip(futureI18n("Show Kexi settings")); action->setWhatsThis(futureI18n("Shows Kexi settings.")); connect(action, SIGNAL(triggered()), this, SLOT(slotSettings())); setupMainMenuActionShortcut(action); #else d->action_settings = d->dummy_action; #endif //! @todo reenable 'tip of the day' later #if 0 KStandardAction::tipOfDay(this, SLOT(slotTipOfTheDayAction()), actionCollection()) ->setWhatsThis(xi18n("This shows useful tips on the use of this application.")); #endif // GLOBAL d->action_show_help_menu = addAction("help_show_menu", xi18nc("Help Menu", "Help"), "Alt+H"); d->action_show_help_menu->setToolTip(xi18n("Show Help menu")); d->action_show_help_menu->setWhatsThis(xi18n("Shows Help menu.")); // (connection is added elsewhere) // ----- declare action categories, so form's "assign action to button" // (and macros in the future) will be able to recognize category // of actions and filter them ----------------------------------- //! @todo shouldn't we move this to core? Kexi::ActionCategories *acat = Kexi::actionCategories(); acat->addAction("data_execute", Kexi::PartItemActionCategory); //! @todo unused for now acat->addWindowAction("data_filter", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("data_save_row", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("data_cancel_row_changes", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addWindowAction("delete_table_row", KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("data_sort_az", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("data_sort_za", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("edit_clear_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in KexiPart::FormObjectType as well acat->addWindowAction("edit_copy_special_data_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType as well acat->addWindowAction("project_export_data_table", KexiPart::TableObjectType, KexiPart::QueryObjectType); // GlobalActions, etc. acat->addAction("edit_copy", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_cut", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_paste", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory); acat->addAction("edit_delete", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_delete_row", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_edititem", Kexi::PartItemActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); acat->addAction("edit_find", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_findnext", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_findprevious", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_replace", Kexi::GlobalActionCategory | Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("edit_paste_special_data_table", Kexi::GlobalActionCategory); acat->addAction("help_about_app", Kexi::GlobalActionCategory); acat->addAction("help_about_kde", Kexi::GlobalActionCategory); acat->addAction("help_contents", Kexi::GlobalActionCategory); acat->addAction("help_report_bug", Kexi::GlobalActionCategory); acat->addAction("help_whats_this", Kexi::GlobalActionCategory); acat->addAction("help_donate", Kexi::GlobalActionCategory); // disabled for now acat->addAction("switch_application_language", Kexi::GlobalActionCategory); acat->addAction("options_configure_keybinding", Kexi::GlobalActionCategory); acat->addAction("project_close", Kexi::GlobalActionCategory); acat->addAction("project_import_data_table", Kexi::GlobalActionCategory); acat->addAction("project_new", Kexi::GlobalActionCategory); acat->addAction("project_open", Kexi::GlobalActionCategory); #ifdef KEXI_QUICK_PRINTING_SUPPORT //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print_preview", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); //! @todo support this in FormObjectType, ReportObjectType as well as others acat->addAction("project_print_setup", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType); #endif acat->addAction("quit", Kexi::GlobalActionCategory); acat->addAction("tools_compact_database", Kexi::GlobalActionCategory); acat->addAction("tools_import_project", Kexi::GlobalActionCategory); acat->addAction("tools_import_tables", Kexi::GlobalActionCategory); acat->addAction("view_data_mode", Kexi::GlobalActionCategory); acat->addAction("view_design_mode", Kexi::GlobalActionCategory); acat->addAction("view_text_mode", Kexi::GlobalActionCategory); acat->addAction("view_mainarea", Kexi::GlobalActionCategory); acat->addAction("view_navigator", Kexi::GlobalActionCategory); acat->addAction("activate_navigator", Kexi::GlobalActionCategory); acat->addAction("view_propeditor", Kexi::GlobalActionCategory); acat->addAction("activate_mainarea", Kexi::GlobalActionCategory); acat->addAction("activate_propeditor", Kexi::GlobalActionCategory); acat->addAction("window_close", Kexi::GlobalActionCategory | Kexi::WindowActionCategory); acat->setAllObjectTypesSupported("window_close", true); acat->addAction("window_next", Kexi::GlobalActionCategory); acat->addAction("window_previous", Kexi::GlobalActionCategory); acat->addAction("full_screen", Kexi::GlobalActionCategory); //skipped - design view only acat->addAction("format_font", Kexi::NoActionCategory); acat->addAction("project_save", Kexi::NoActionCategory); acat->addAction("edit_insert_empty_row", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType later acat->addAction("edit_select_all", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later acat->addAction("edit_redo", Kexi::NoActionCategory); //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later acat->addAction("edit_undo", Kexi::NoActionCategory); //record-navigation related actions acat->addAction("data_go_to_first_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_previous_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_next_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_last_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); acat->addAction("data_go_to_new_record", Kexi::WindowActionCategory, KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType); //skipped - internal: acat->addAction("tablepart_create", Kexi::NoActionCategory); acat->addAction("querypart_create", Kexi::NoActionCategory); acat->addAction("formpart_create", Kexi::NoActionCategory); acat->addAction("reportpart_create", Kexi::NoActionCategory); acat->addAction("macropart_create", Kexi::NoActionCategory); acat->addAction("scriptpart_create", Kexi::NoActionCategory); } void KexiMainWindow::invalidateActions() { invalidateProjectWideActions(); invalidateSharedActions(); } void KexiMainWindow::invalidateSharedActions(QObject *o) { //! @todo enabling is more complex... /* d->action_edit_cut->setEnabled(true); d->action_edit_copy->setEnabled(true); d->action_edit_paste->setEnabled(true);*/ if (!o) o = focusWindow(); KexiSharedActionHost::invalidateSharedActions(o); } void KexiMainWindow::invalidateSharedActions() { invalidateSharedActions(0); } // unused, I think void KexiMainWindow::invalidateSharedActionsLater() { QTimer::singleShot(1, this, SLOT(invalidateSharedActions())); } void KexiMainWindow::invalidateProjectWideActions() { const bool has_window = currentWindow(); const bool window_dirty = currentWindow() && currentWindow()->isDirty(); const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly(); //PROJECT MENU d->action_save->setEnabled(has_window && window_dirty && !readOnly); d->action_save_as->setEnabled(has_window && !readOnly); d->action_project_properties->setEnabled(d->prj); d->action_close->setEnabled(d->prj); d->action_project_relations->setEnabled(d->prj); //DATA MENU if (d->action_project_import_data_table) d->action_project_import_data_table->setEnabled(d->prj && !readOnly); if (d->action_tools_data_import) d->action_tools_data_import->setEnabled(d->prj && !readOnly); d->action_project_export_data_table->setEnabled( currentWindow() && currentWindow()->part()->info()->isDataExportSupported()); if (d->action_edit_paste_special_data_table) d->action_edit_paste_special_data_table->setEnabled(d->prj && !readOnly); #ifdef KEXI_QUICK_PRINTING_SUPPORT const bool printingActionsEnabled = currentWindow() && currentWindow()->part()->info()->isPrintingSupported() && !currentWindow()->neverSaved(); d->action_project_print->setEnabled(printingActionsEnabled); d->action_project_print_preview->setEnabled(printingActionsEnabled); d->action_project_print_setup->setEnabled(printingActionsEnabled); #endif //EDIT MENU //! @todo "copy special" is currently enabled only for data view mode; //! what about allowing it to enable in design view for "kexi/table" ? if (currentWindow() && currentWindow()->currentViewMode() == Kexi::DataViewMode) { KexiPart::Info *activePartInfo = currentWindow()->part()->info(); d->action_edit_copy_special_data_table->setEnabled( activePartInfo ? activePartInfo->isDataExportSupported() : false); } else { d->action_edit_copy_special_data_table->setEnabled(false); } d->action_edit_find->setEnabled(d->prj); //VIEW MENU if (d->action_show_nav) d->action_show_nav->setEnabled(d->prj); d->action_activate_mainarea->setEnabled(d->prj); if (d->action_show_propeditor) d->action_show_propeditor->setEnabled(d->prj); #ifdef KEXI_SHOW_CONTEXT_HELP d->action_show_helper->setEnabled(d->prj); #endif //CREATE MENU if (d->tabbedToolBar && d->tabbedToolBar->createWidgetToolBar()) d->tabbedToolBar->createWidgetToolBar()->setEnabled(d->prj); // DATA MENU //TOOLS MENU // "compact db" supported if there's no db or the current db supports compacting and is opened r/w: d->action_tools_compact_database->setEnabled( !d->prj || (!readOnly && d->prj && d->prj->dbConnection() && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported)) ); //DOCKS if (d->navigator) { d->navigator->setEnabled(d->prj); } if (d->propEditor) d->propEditorTabWidget->setEnabled(d->prj); } tristate KexiMainWindow::startup() { tristate result = true; - switch (Kexi::startupHandler().action()) { + switch (KexiStartupHandler::global()->action()) { case KexiStartupHandler::CreateBlankProject: d->updatePropEditorVisibility(Kexi::NoViewMode); break; #ifdef KEXI_PROJECT_TEMPLATES case KexiStartupHandler::CreateFromTemplate: - result = createProjectFromTemplate(*Kexi::startupHandler().projectData()); + result = createProjectFromTemplate(*KexiStartupHandler::global()->projectData()); break; #endif case KexiStartupHandler::OpenProject: - result = openProject(*Kexi::startupHandler().projectData()); + result = openProject(*KexiStartupHandler::global()->projectData()); break; case KexiStartupHandler::ImportProject: result = showProjectMigrationWizard( - Kexi::startupHandler().importActionData().mimeType, - Kexi::startupHandler().importActionData().fileName + KexiStartupHandler::global()->importActionData().mimeType, + KexiStartupHandler::global()->importActionData().fileName ); break; case KexiStartupHandler::ShowWelcomeScreen: //! @todo show welcome screen as soon as is available QTimer::singleShot(100, this, SLOT(slotProjectWelcome())); break; default: d->updatePropEditorVisibility(Kexi::NoViewMode); } return result; } static QString internalReason(const KDbResult &result) { const QString msg = result.message(); if (msg.isEmpty()) { return QString(); } return xi18n("
(reason: %1)", msg); } tristate KexiMainWindow::openProject(const KexiProjectData& projectData) { //qDebug() << projectData; QScopedPointer prj(createKexiProjectObject(projectData)); if (~KexiDBPasswordDialog::getPasswordIfNeeded(prj->data()->connectionData(), this)) { return cancelled; } bool incompatibleWithKexi; tristate res = prj->open(&incompatibleWithKexi); if (prj->data()->connectionData()->isPasswordNeeded()) { // password was supplied in this session, and shouldn't be stored or reused afterwards, // so let's remove it prj->data()->connectionData()->setPassword(QString()); } if (~res) { return cancelled; } else if (!res) { if (incompatibleWithKexi) { if (KMessageBox::Yes == KMessageBox::questionYesNo(this, xi18nc("@info (don't add tags around %1, it's done already)", "Database project %1 does not appear to have been created using Kexi." "Do you want to import it as a new Kexi project?", projectData.infoString()), QString(), KGuiItem(xi18nc("@action:button Import Database", "&Import..."), KexiIconName("database-import")), KStandardGuiItem::cancel())) { const bool anotherProjectAlreadyOpened = prj; tristate res = showProjectMigrationWizard("application/x-kexi-connectiondata", projectData.databaseName(), *projectData.connectionData()); if (!anotherProjectAlreadyOpened) //the project could have been opened within this instance return res; //always return cancelled because even if migration succeeded, new Kexi instance //will be started if user wanted to open the imported db return cancelled; } return cancelled; } return false; } // success d->prj = prj.take(); setupProjectNavigator(); d->prj->data()->setLastOpened(QDateTime::currentDateTime()); Kexi::recentProjects()->addProjectData(*d->prj->data()); updateReadOnlyState(); invalidateActions(); setMessagesEnabled(false); QTimer::singleShot(1, this, SLOT(slotAutoOpenObjectsLater())); if (d->tabbedToolBar) { d->tabbedToolBar->showTab("create");// not needed since create toolbar already shows toolbar! move when kexi starts d->tabbedToolBar->showTab("data"); d->tabbedToolBar->showTab("external"); d->tabbedToolBar->showTab("tools"); d->tabbedToolBar->hideTab("form");//temporalily until createToolbar is split d->tabbedToolBar->hideTab("report");//temporalily until createToolbar is split // make sure any tab is activated d->tabbedToolBar->setCurrentTab(0); } return true; } tristate KexiMainWindow::openProject(const KexiProjectData& data, const QString& shortcutPath, bool *opened) { if (!shortcutPath.isEmpty() && d->prj) { const tristate result = openProjectInExternalKexiInstance( shortcutPath, QString(), QString()); if (result == true) { *opened = true; } return result; } return openProject(data); } tristate KexiMainWindow::createProjectFromTemplate(const KexiProjectData& projectData) { Q_UNUSED(projectData); #ifdef KEXI_PROJECT_TEMPLATES QStringList mimetypes; mimetypes.append(KDb::defaultFileBasedDriverMimeType()); QString fname; //! @todo KEXI3 add equivalent of kfiledialog:/// const QString startDir("kfiledialog:///OpenExistingOrCreateNewProject"/*as in KexiNewProjectWizard*/); const QString caption(xi18nc("@window:title", "Select New Project's Location")); while (true) { if (fname.isEmpty() && !projectData.connectionData()->databaseName().isEmpty()) { //propose filename from db template name fname = projectData.connectionData()->databaseName(); } const bool specialDir = fname.isEmpty(); qDebug() << fname << "............."; QFileDialog dlg(specialDir ? QUrl(startDir) : QUrl(), QString(), this); dlg.setModal(true); dlg.setMimeFilter(mimetypes); if (!specialDir) dlg.selectUrl(QUrl::fromLocalFile(fname); // may also be a filename dlg.setFileMode(QFileDialog::ExistingFile); dlg.setFileMode(QFileDialog::AcceptOpen); dlg.setWindowTitle(caption); if (QDialog::Accepted != dlg.exec()) { return cancelled; } if (dlg.selectedFiles().isEmpty() { return cancelled; } fname = dlg.selectedFiles().first(); if (fname.isEmpty()) { return cancelled; } if (KexiUtils::askForFileOverwriting(fname, this)) { break; } } QFile sourceFile(projectData.connectionData()->fileName()); if (!sourceFile.copy(fname)) { //! @todo show error from with QFile::FileError return false; } return openProject(fname, 0, QString(), projectData.autoopenObjects/*copy*/); #else return false; #endif } void KexiMainWindow::updateReadOnlyState() { const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly(); //! @todo KEXI3 show read-only flag in the GUI because we have no statusbar if (d->navigator) { d->navigator->setReadOnly(readOnly); } // update "insert ....." actions for every part KexiPart::PartInfoList *plist = Kexi::partManager().infoList(); if (plist) { foreach(KexiPart::Info *info, *plist) { QAction *a = info->newObjectAction(); if (a) a->setEnabled(!readOnly); } } } void KexiMainWindow::slotAutoOpenObjectsLater() { QString not_found_msg; bool openingCancelled; //ok, now open "autoopen: objects if (d->prj) { foreach(KexiProjectData::ObjectInfo* info, d->prj->data()->autoopenObjects) { KexiPart::Info *i = Kexi::partManager().infoForPluginId(info->value("type")); if (!i) { not_found_msg += "
  • "; if (!info->value("name").isEmpty()) not_found_msg += (QString("\"") + info->value("name") + "\" - "); if (info->value("action") == "new") not_found_msg += xi18n("cannot create object - unknown object type \"%1\"", info->value("type")); else not_found_msg += xi18n("unknown object type \"%1\"", info->value("type")); not_found_msg += internalReason(Kexi::partManager().result()) + "
  • "; continue; } // * NEW if (info->value("action") == "new") { if (!newObject(i, &openingCancelled) && !openingCancelled) { not_found_msg += "
  • "; not_found_msg += (xi18n("cannot create object of type \"%1\"", info->value("type")) + internalReason(d->prj->result()) + "
  • "); } else d->wasAutoOpen = true; continue; } KexiPart::Item *item = d->prj->item(i, info->value("name")); if (!item) { QString taskName; if (info->value("action") == "execute") taskName = xi18nc("\"executing object\" action", "executing"); #ifdef KEXI_QUICK_PRINTING_SUPPORT else if (info->value("action") == "print-preview") taskName = futureI18n("making print preview for"); else if (info->value("action") == "print") taskName = futureI18n("printing"); #endif else taskName = xi18n("opening"); not_found_msg += (QString("
  • ") + taskName + " \"" + info->value("name") + "\" - "); if ("table" == info->value("type").toLower()) not_found_msg += xi18n("table not found"); else if ("query" == info->value("type").toLower()) not_found_msg += xi18n("query not found"); else if ("macro" == info->value("type").toLower()) not_found_msg += xi18n("macro not found"); else if ("script" == info->value("type").toLower()) not_found_msg += xi18n("script not found"); else not_found_msg += xi18n("object not found"); not_found_msg += (internalReason(d->prj->result()) + "
  • "); continue; } // * EXECUTE, PRINT, PRINT PREVIEW if (info->value("action") == "execute") { tristate res = executeItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + xi18n("cannot execute object") + internalReason(d->prj->result()) + "
  • "); } continue; } #ifdef KEXI_QUICK_PRINTING_SUPPORT else if (info->value("action") == "print") { tristate res = printItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + futureI18n("cannot print object") + internalReason(d->prj->result()) + "
  • "); } continue; } else if (info->value("action") == "print-preview") { tristate res = printPreviewForItem(item); if (false == res) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - " + futureI18n("cannot make print preview of object") + internalReason(d->prj->result()) + "
  • "); } continue; } #endif Kexi::ViewMode viewMode; if (info->value("action") == "open") viewMode = Kexi::DataViewMode; else if (info->value("action") == "design") viewMode = Kexi::DesignViewMode; else if (info->value("action") == "edittext") viewMode = Kexi::TextViewMode; else continue; //sanity QString openObjectMessage; if (!openObject(item, viewMode, &openingCancelled, 0, &openObjectMessage) && (!openingCancelled || !openObjectMessage.isEmpty())) { not_found_msg += (QString("
  • \"") + info->value("name") + "\" - "); if (openObjectMessage.isEmpty()) not_found_msg += xi18n("cannot open object"); else not_found_msg += openObjectMessage; not_found_msg += internalReason(d->prj->result()) + "
  • "; continue; } else { d->wasAutoOpen = true; } } } setMessagesEnabled(true); if (!not_found_msg.isEmpty()) showErrorMessage(xi18n("You have requested selected objects to be automatically opened " "or processed on startup. Several objects cannot be opened or processed."), QString("
      %1
    ").arg(not_found_msg)); d->updatePropEditorVisibility(currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode); #if defined(KDOCKWIDGET_P) if (d->propEditor) { KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) ds->setSeparatorPosInPercent(d->config->readEntry("RightDockPosition", 80/* % */)); } #endif updateAppCaption(); if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } qApp->processEvents(); emit projectOpened(); } tristate KexiMainWindow::closeProject() { if (d->tabbedToolBar) d->tabbedToolBar->hideMainMenu(); #ifndef KEXI_NO_PENDING_DIALOGS if (d->pendingWindowsExist()) { qDebug() << "pendingWindowsExist..."; d->actionToExecuteWhenPendingJobsAreFinished = Private::CloseProjectAction; return cancelled; } #endif //only save nav. visibility setting if there is project opened d->saveSettingsForShowProjectNavigator = d->prj && d->isProjectNavigatorVisible; if (!d->prj) return true; { // make sure the project can be closed bool cancel = false; emit acceptProjectClosingRequested(&cancel); if (cancel) return cancelled; } d->windowExistedBeforeCloseProject = currentWindow(); #if defined(KDOCKWIDGET_P) //remember docks position - will be used on storeSettings() if (d->propEditor) { KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) d->propEditorDockSeparatorPos = ds->separatorPosInPercent(); } if (d->nav) { if (d->propEditor) { //! @todo KEXI3 if (d->openedWindowsCount() == 0) //! @todo KEXI3 makeWidgetDockVisible(d->propEditorTabWidget); KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) ds->setSeparatorPosInPercent(80); } KDockWidget *dw = (KDockWidget *)d->nav->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); int dwWidth = dw->width(); if (ds) { if (d->openedWindowsCount() != 0 && d->propEditorTabWidget && d->propEditorTabWidget->isVisible()) { d->navDockSeparatorPos = ds->separatorPosInPercent(); } else { d->navDockSeparatorPos = (100 * dwWidth) / width(); } } } #endif //close each window, optionally asking if user wants to close (if data changed) while (currentWindow()) { tristate res = closeWindow(currentWindow()); if (!res || ~res) return res; } // now we will close for sure emit beforeProjectClosing(); if (!d->prj->closeConnection()) return false; if (d->navigator) { d->navWasVisibleBeforeProjectClosing = d->navDockWidget->isVisible(); d->navDockWidget->hide(); d->navigator->setProject(0); slotProjectNavigatorVisibilityChanged(true); // hide side tab } if (d->propEditorDockWidget) d->propEditorDockWidget->hide(); d->clearWindows(); //sanity! delete d->prj; d->prj = 0; updateReadOnlyState(); invalidateActions(); updateAppCaption(); emit projectClosed(); return true; } void KexiMainWindow::setupContextHelp() { #ifdef KEXI_SHOW_CONTEXT_HELP d->ctxHelp = new KexiContextHelp(d->mainWidget, this); //! @todo /* d->ctxHelp->setContextHelp(xi18n("Welcome"),xi18n("The KEXI team wishes you a lot of productive work, " "with this product.


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


    If you would like to join our effort, the development documentation " "at www.kexi-project.org is a good starting point."),0); */ addToolWindow(d->ctxHelp, KDockWidget::DockBottom | KDockWidget::DockLeft, getMainDockWidget(), 20); #endif } void KexiMainWindow::setupMainWidget() { QVBoxLayout *vlyr = new QVBoxLayout(this); vlyr->setContentsMargins(0, 0, 0, 0); vlyr->setSpacing(0); if (d->isMainMenuVisible) { QWidget *tabbedToolBarContainer = new QWidget(this); vlyr->addWidget(tabbedToolBarContainer); QVBoxLayout *tabbedToolBarContainerLyr = new QVBoxLayout(tabbedToolBarContainer); tabbedToolBarContainerLyr->setSpacing(0); tabbedToolBarContainerLyr->setContentsMargins( KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2); d->tabbedToolBar = new KexiTabbedToolBar(tabbedToolBarContainer); Q_ASSERT(d->action_view_global_search); connect(d->action_view_global_search, SIGNAL(triggered()), d->tabbedToolBar, SLOT(activateSearchLineEdit())); tabbedToolBarContainerLyr->addWidget(d->tabbedToolBar); d->tabbedToolBar->hideTab("form"); //temporarily until createToolbar is split d->tabbedToolBar->hideTab("report"); //temporarily until createToolbar is split } else { d->tabbedToolBar = 0; } QWidget *mainWidgetContainer = new QWidget(); vlyr->addWidget(mainWidgetContainer, 1); QHBoxLayout *mainWidgetContainerLyr = new QHBoxLayout(mainWidgetContainer); mainWidgetContainerLyr->setContentsMargins(0, 0, 0, 0); mainWidgetContainerLyr->setSpacing(0); KMultiTabBar *mtbar = new KMultiTabBar(KMultiTabBar::Left); mtbar->setStyle(KMultiTabBar::VSNET); mainWidgetContainerLyr->addWidget(mtbar); d->multiTabBars.insert(mtbar->position(), mtbar); d->mainWidget = new KexiMainWidget(); d->mainWidget->setParent(this); d->mainWidget->tabWidget()->setTabsClosable(true); connect(d->mainWidget->tabWidget(), SIGNAL(tabCloseRequested(int)), this, SLOT(closeWindowForTab(int))); mainWidgetContainerLyr->addWidget(d->mainWidget, 1); mtbar = new KMultiTabBar(KMultiTabBar::Right); mtbar->setStyle(KMultiTabBar::VSNET); mainWidgetContainerLyr->addWidget(mtbar); d->multiTabBars.insert(mtbar->position(), mtbar); } void KexiMainWindow::slotSetProjectNavigatorVisible(bool set) { if (d->navDockWidget) d->navDockWidget->setVisible(set); } void KexiMainWindow::slotSetPropertyEditorVisible(bool set) { if (d->propEditorDockWidget) d->propEditorDockWidget->setVisible(set); } void KexiMainWindow::slotProjectNavigatorVisibilityChanged(bool visible) { d->setTabBarVisible(KMultiTabBar::Left, PROJECT_NAVIGATOR_TABBAR_ID, d->navDockWidget, !visible); } void KexiMainWindow::slotPropertyEditorVisibilityChanged(bool visible) { if (!d->enable_slotPropertyEditorVisibilityChanged) return; d->setPropertyEditorTabBarVisible(!visible); if (!visible) d->propertyEditorCollapsed = true; } void KexiMainWindow::slotMultiTabBarTabClicked(int id) { if (id == PROJECT_NAVIGATOR_TABBAR_ID) { slotProjectNavigatorVisibilityChanged(true); d->navDockWidget->show(); } else if (id == PROPERTY_EDITOR_TABBAR_ID) { slotPropertyEditorVisibilityChanged(true); d->propEditorDockWidget->show(); d->propertyEditorCollapsed = false; } } static Qt::DockWidgetArea applyRightToLeftToDockArea(Qt::DockWidgetArea area) { if (QApplication::layoutDirection() == Qt::RightToLeft) { if (area == Qt::LeftDockWidgetArea) { return Qt::RightDockWidgetArea; } else if (area == Qt::RightDockWidgetArea) { return Qt::LeftDockWidgetArea; } } return area; } void KexiMainWindow::setupProjectNavigator() { if (!d->isProjectNavigatorVisible) return; if (d->navigator) { d->navDockWidget->show(); } else { KexiDockableWidget* navDockableWidget = new KexiDockableWidget; d->navigator = new KexiProjectNavigator(navDockableWidget); kexiTester() << KexiTestObject(d->navigator, "KexiProjectNavigator"); navDockableWidget->setWidget(d->navigator); d->navDockWidget = new KexiDockWidget(d->navigator->windowTitle(), d->mainWidget); d->navDockWidget->setObjectName("ProjectNavigatorDockWidget"); d->mainWidget->addDockWidget( applyRightToLeftToDockArea(Qt::LeftDockWidgetArea), d->navDockWidget, Qt::Vertical); navDockableWidget->setParent(d->navDockWidget); d->navDockWidget->setWidget(navDockableWidget); KConfigGroup mainWindowGroup(d->config->group("MainWindow")); const QSize projectNavigatorSize = mainWindowGroup.readEntry("ProjectNavigatorSize", QSize()); if (!projectNavigatorSize.isNull()) { navDockableWidget->setSizeHint(projectNavigatorSize); } connect(d->navDockWidget, SIGNAL(visibilityChanged(bool)), this, SLOT(slotProjectNavigatorVisibilityChanged(bool))); //Nav2 Signals connect(d->navigator, SIGNAL(openItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObject(KexiPart::Item*,Kexi::ViewMode))); connect(d->navigator, SIGNAL(openOrActivateItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(openObjectFromNavigator(KexiPart::Item*,Kexi::ViewMode))); connect(d->navigator, SIGNAL(newItem(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); connect(d->navigator, SIGNAL(removeItem(KexiPart::Item*)), this, SLOT(removeObject(KexiPart::Item*))); connect(d->navigator->model(), SIGNAL(renameItem(KexiPart::Item*,QString,bool*)), this, SLOT(renameObject(KexiPart::Item*,QString,bool*))); connect(d->navigator->model(), SIGNAL(changeItemCaption(KexiPart::Item*,QString,bool*)), this, SLOT(setObjectCaption(KexiPart::Item*,QString,bool*))); connect(d->navigator, SIGNAL(executeItem(KexiPart::Item*)), this, SLOT(executeItem(KexiPart::Item*))); connect(d->navigator, SIGNAL(exportItemToClipboardAsDataTable(KexiPart::Item*)), this, SLOT(copyItemToClipboardAsDataTable(KexiPart::Item*))); connect(d->navigator, SIGNAL(exportItemToFileAsDataTable(KexiPart::Item*)), this, SLOT(exportItemAsDataTable(KexiPart::Item*))); #ifdef KEXI_QUICK_PRINTING_SUPPORT connect(d->navigator, SIGNAL(printItem(KexiPart::Item*)), this, SLOT(printItem(KexiPart::Item*))); connect(d->navigator, SIGNAL(pageSetupForItem(KexiPart::Item*)), this, SLOT(showPageSetupForItem(KexiPart::Item*))); #endif connect(d->navigator, SIGNAL(selectionChanged(KexiPart::Item*)), this, SLOT(slotPartItemSelectedInNavigator(KexiPart::Item*))); } if (d->prj->isConnected()) { QString partManagerErrorMessages; if (!partManagerErrorMessages.isEmpty()) { showWarningContinueMessage(partManagerErrorMessages, QString(), "ShowWarningsRelatedToPluginsLoading"); } d->navigator->setProject(d->prj, QString()/*all classes*/, &partManagerErrorMessages); } connect(d->prj, SIGNAL(newItemStored(KexiPart::Item*)), d->navigator->model(), SLOT(slotAddItem(KexiPart::Item*))); connect(d->prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->model(), SLOT(slotRemoveItem(KexiPart::Item))); d->navigator->setFocus(); if (d->forceShowProjectNavigatorOnCreation) { slotShowNavigator(); d->forceShowProjectNavigatorOnCreation = false; } else if (d->forceHideProjectNavigatorOnCreation) { d->forceHideProjectNavigatorOnCreation = false; } invalidateActions(); } void KexiMainWindow::slotLastActions() { } void KexiMainWindow::setupPropertyEditor() { if (!d->propEditor) { KConfigGroup mainWindowGroup(d->config->group("MainWindow")); //! @todo FIX LAYOUT PROBLEMS d->propEditorDockWidget = new KexiDockWidget(xi18n("Property Editor"), d->mainWidget); d->propEditorDockWidget->setObjectName("PropertyEditorDockWidget"); d->mainWidget->addDockWidget( applyRightToLeftToDockArea(Qt::RightDockWidgetArea), d->propEditorDockWidget, Qt::Vertical ); connect(d->propEditorDockWidget, SIGNAL(visibilityChanged(bool)), this, SLOT(slotPropertyEditorVisibilityChanged(bool))); d->propEditorDockableWidget = new KexiDockableWidget(d->propEditorDockWidget); d->propEditorDockWidget->setWidget(d->propEditorDockableWidget); const QSize propertyEditorSize = mainWindowGroup.readEntry("PropertyEditorSize", QSize()); if (!propertyEditorSize.isNull()) { d->propEditorDockableWidget->setSizeHint(propertyEditorSize); } QWidget *propEditorDockWidgetContents = new QWidget(d->propEditorDockableWidget); d->propEditorDockableWidget->setWidget(propEditorDockWidgetContents); QVBoxLayout *propEditorDockWidgetContentsLyr = new QVBoxLayout(propEditorDockWidgetContents); propEditorDockWidgetContentsLyr->setContentsMargins(0, 0, 0, 0); d->propEditorTabWidget = new QTabWidget(propEditorDockWidgetContents); d->propEditorTabWidget->setDocumentMode(true); propEditorDockWidgetContentsLyr->addWidget(d->propEditorTabWidget); d->propEditor = new KexiPropertyEditorView(d->propEditorTabWidget); d->propEditorTabWidget->setWindowTitle(d->propEditor->windowTitle()); d->propEditorTabWidget->addTab(d->propEditor, xi18n("Properties")); //! @todo REMOVE? d->propEditor->installEventFilter(this); KConfigGroup propertyEditorGroup(d->config->group("PropertyEditor")); QFont f(KexiUtils::smallestReadableFont()); const qreal pointSizeF = propertyEditorGroup.readEntry("FontPointSize", -1.0f); // points are more accurate if (pointSizeF > 0.0) { f.setPointSizeF(pointSizeF); } else { const int pixelSize = propertyEditorGroup.readEntry("FontSize", -1); // compatibility with Kexi 2.x if (pixelSize > 0) { f.setPixelSize(pixelSize); } } d->propEditorTabWidget->setFont(f); d->enable_slotPropertyEditorVisibilityChanged = false; d->propEditorDockWidget->setVisible(false); d->enable_slotPropertyEditorVisibilityChanged = true; } } void KexiMainWindow::slotPartLoaded(KexiPart::Part* p) { if (!p) return; p->createGUIClients(); } void KexiMainWindow::updateAppCaption() { //! @todo allow to set custom "static" app caption d->appCaptionPrefix.clear(); if (d->prj && d->prj->data()) {//add project name d->appCaptionPrefix = d->prj->data()->caption(); if (d->appCaptionPrefix.isEmpty()) { d->appCaptionPrefix = d->prj->data()->databaseName(); } if (d->prj->dbConnection()->options()->isReadOnly()) { d->appCaptionPrefix = xi18nc(" (read only)", "%1 (read only)", d->appCaptionPrefix); } } setWindowTitle(d->appCaptionPrefix); } bool KexiMainWindow::queryClose() { #ifndef KEXI_NO_PENDING_DIALOGS if (d->pendingWindowsExist()) { qDebug() << "pendingWindowsExist..."; d->actionToExecuteWhenPendingJobsAreFinished = Private::QuitAction; return false; } #endif const tristate res = closeProject(); if (~res) return false; if (res == true) storeSettings(); if (! ~res) { Kexi::deleteGlobalObjects(); qApp->quit(); } return ! ~res; } void KexiMainWindow::closeEvent(QCloseEvent *ev) { d->mainWidget->closeEvent(ev); } static const QSize KEXI_MIN_WINDOW_SIZE(1024, 768); void KexiMainWindow::restoreSettings() { KConfigGroup mainWindowGroup(d->config->group("MainWindow")); const bool maximize = mainWindowGroup.readEntry("Maximized", false); const QRect geometry(mainWindowGroup.readEntry("Geometry", QRect())); if (geometry.isValid()) setGeometry(geometry); else if (maximize) setWindowState(windowState() | Qt::WindowMaximized); else { QRect desk = QApplication::desktop()->screenGeometry( QApplication::desktop()->screenNumber(this)); if (desk.width() <= KEXI_MIN_WINDOW_SIZE.width() || desk.height() <= KEXI_MIN_WINDOW_SIZE.height()) { setWindowState(windowState() | Qt::WindowMaximized); } else { resize(KEXI_MIN_WINDOW_SIZE); } } // Saved settings } void KexiMainWindow::storeSettings() { //qDebug(); KConfigGroup mainWindowGroup(d->config->group("MainWindow")); if (isMaximized()) { mainWindowGroup.writeEntry("Maximized", true); mainWindowGroup.deleteEntry("Geometry"); } else { mainWindowGroup.deleteEntry("Maximized"); mainWindowGroup.writeEntry("Geometry", geometry()); } if (d->navigator) mainWindowGroup.writeEntry("ProjectNavigatorSize", d->navigator->parentWidget()->size()); if (d->propEditorDockableWidget) mainWindowGroup.writeEntry("PropertyEditorSize", d->propEditorDockableWidget->size()); d->config->sync(); } void KexiMainWindow::registerChild(KexiWindow *window) { //qDebug(); connect(window, SIGNAL(dirtyChanged(KexiWindow*)), this, SLOT(slotDirtyFlagChanged(KexiWindow*))); if (window->id() != -1) { d->insertWindow(window); } //qDebug() << "ID=" << window->id(); } void KexiMainWindow::updateCustomPropertyPanelTabs(KexiWindow *prevWindow, Kexi::ViewMode prevViewMode) { updateCustomPropertyPanelTabs( prevWindow ? prevWindow->part() : 0, prevWindow ? prevWindow->currentViewMode() : prevViewMode, currentWindow() ? currentWindow()->part() : 0, currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode ); } void KexiMainWindow::updateCustomPropertyPanelTabs( KexiPart::Part *prevWindowPart, Kexi::ViewMode prevViewMode, KexiPart::Part *curWindowPart, Kexi::ViewMode curViewMode) { if (!d->propEditorTabWidget) return; if ( !curWindowPart || (/*prevWindowPart &&*/ curWindowPart && (prevWindowPart != curWindowPart || prevViewMode != curViewMode) ) ) { if (d->partForPreviouslySetupPropertyPanelTabs) { //remember current page number for this part if (( prevViewMode == Kexi::DesignViewMode && static_cast(d->partForPreviouslySetupPropertyPanelTabs) != curWindowPart) //part changed || curViewMode != Kexi::DesignViewMode) { //..or switching to other view mode d->recentlySelectedPropertyPanelPages.insert( d->partForPreviouslySetupPropertyPanelTabs, d->propEditorTabWidget->currentIndex()); } } //delete old custom tabs (other than 'property' tab) const int count = d->propEditorTabWidget->count(); for (int i = 1; i < count; i++) d->propEditorTabWidget->removeTab(1); } //don't change anything if part is not switched nor view mode changed if ((!prevWindowPart && !curWindowPart) || (prevWindowPart == curWindowPart && prevViewMode == curViewMode) || (curWindowPart && curViewMode != Kexi::DesignViewMode)) { //new part for 'previously setup tabs' d->partForPreviouslySetupPropertyPanelTabs = curWindowPart; return; } if (curWindowPart) { //recreate custom tabs curWindowPart->setupCustomPropertyPanelTabs(d->propEditorTabWidget); //restore current page number for this part if (d->recentlySelectedPropertyPanelPages.contains(curWindowPart)) { d->propEditorTabWidget->setCurrentIndex( d->recentlySelectedPropertyPanelPages[ curWindowPart ] ); } } //new part for 'previously setup tabs' d->partForPreviouslySetupPropertyPanelTabs = curWindowPart; } void KexiMainWindow::activeWindowChanged(KexiWindow *window, KexiWindow *prevWindow) { //qDebug() << "to=" << (window ? window->windowTitle() : ""); bool windowChanged = prevWindow != window; if (windowChanged) { if (prevWindow) { //inform previously activated dialog about deactivation prevWindow->deactivate(); } } updateCustomPropertyPanelTabs(prevWindow, prevWindow ? prevWindow->currentViewMode() : Kexi::NoViewMode); // inform the current view of the new dialog about property switching // (this will also call KexiMainWindow::propertySetSwitched() to update the current property editor's set if (windowChanged && currentWindow()) currentWindow()->selectedView()->propertySetSwitched(); if (windowChanged) { if (currentWindow() && currentWindow()->currentViewMode() != 0 && window) { //on opening new dialog it can be 0; we don't want this d->updatePropEditorVisibility(currentWindow()->currentViewMode()); restoreDesignTabIfNeeded(window->partItem()->pluginId(), window->currentViewMode(), prevWindow ? prevWindow->partItem()->identifier() : 0); activateDesignTabIfNeeded(window->partItem()->pluginId(), window->currentViewMode()); } } invalidateActions(); d->updateFindDialogContents(); if (window) window->setFocus(); } bool KexiMainWindow::activateWindow(int id) { qDebug(); #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; return activateWindow(*d->openedWindowFor(id, pendingType)); #else return activateWindow(*d->openedWindowFor(id)); #endif } bool KexiMainWindow::activateWindow(KexiWindow& window) { //qDebug(); d->focus_before_popup = &window; d->mainWidget->tabWidget()->setCurrentWidget(window.parentWidget()/*container*/); window.activate(); return true; } void KexiMainWindow::activateNextWindow() { //! @todo activateNextWindow() } void KexiMainWindow::activatePreviousWindow() { //! @todo activatePreviousWindow() } void KexiMainWindow::slotSettings() { if (d->tabbedToolBar) { d->tabbedToolBar->showMainMenu("settings"); // dummy QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("settings")->text()); d->tabbedToolBar->setMainMenuContent(dummy); } } void KexiMainWindow::slotConfigureKeys() { KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsDisallowed, this); } void KexiMainWindow::slotConfigureToolbars() { KEditToolBar edit(actionCollection()); (void) edit.exec(); } void KexiMainWindow::slotProjectNew() { createNewProject(); } KexiProject* KexiMainWindow::createKexiProjectObject(const KexiProjectData &data) { KexiProject *prj = new KexiProject(data, this); connect(prj, SIGNAL(itemRenamed(KexiPart::Item,QString)), this, SLOT(slotObjectRenamed(KexiPart::Item,QString))); if (d->navigator){ connect(prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->model(), SLOT(slotRemoveItem(KexiPart::Item))); } return prj; } void KexiMainWindow::createNewProject() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_new"); KexiNewProjectAssistant* assistant = new KexiNewProjectAssistant; connect(assistant, SIGNAL(createProject(KexiProjectData)), this, SLOT(createNewProject(KexiProjectData))); d->tabbedToolBar->setMainMenuContent(assistant); } tristate KexiMainWindow::createNewProject(const KexiProjectData &projectData) { QScopedPointer prj(createKexiProjectObject(projectData)); tristate res = prj->create(true /*overwrite*/); if (res != true) { return res; } //qDebug() << "new project created ---"; if (d->prj) { res = openProjectInExternalKexiInstance( prj->data()->connectionData()->databaseName(), prj->data()->connectionData(), prj->data()->databaseName()); Kexi::recentProjects()->addProjectData(*prj->data()); if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } return res; } if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } d->prj = prj.take(); setupProjectNavigator(); d->prj->data()->setLastOpened(QDateTime::currentDateTime()); Kexi::recentProjects()->addProjectData(*d->prj->data()); invalidateActions(); updateAppCaption(); return true; } void KexiMainWindow::slotProjectOpen() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_open"); KexiOpenProjectAssistant* assistant = new KexiOpenProjectAssistant; connect(assistant, SIGNAL(openProject(KexiProjectData)), this, SLOT(openProject(KexiProjectData))); connect(assistant, SIGNAL(openProject(QString)), this, SLOT(openProject(QString))); d->tabbedToolBar->setMainMenuContent(assistant); } tristate KexiMainWindow::openProject(const QString& aFileName) { return openProject(aFileName, QString(), QString()); } tristate KexiMainWindow::openProject(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName) { if (d->prj) return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName); KDbConnectionData *cdata = 0; if (!fileNameForConnectionData.isEmpty()) { cdata = Kexi::connset().connectionDataForFileName(fileNameForConnectionData); if (!cdata) { qWarning() << "cdata?"; return false; } } return openProject(aFileName, cdata, dbName); } tristate KexiMainWindow::openProject(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName, const KexiProjectData::AutoOpenObjects& autoopenObjects) { if (d->prj) { return openProjectInExternalKexiInstance(aFileName, cdata, dbName); } KexiProjectData* projectData = 0; - KexiStartupHandler &h = Kexi::startupHandler(); - bool readOnly = h.isSet(h.options().readOnly); + const KexiStartupHandler *h = KexiStartupHandler::global(); + bool readOnly = h->isSet(h->options().readOnly); bool deleteAfterOpen = false; if (cdata) { //server-based project if (dbName.isEmpty()) {//no database name given, ask user bool cancel; - projectData = Kexi::startupHandler().selectProject(cdata, &cancel, this); + projectData = KexiStartupHandler::global()->selectProject(cdata, &cancel, this); if (cancel) return cancelled; } else { //! @todo caption arg? projectData = new KexiProjectData(*cdata, dbName); deleteAfterOpen = true; } } else { if (aFileName.isEmpty()) { qWarning() << "aFileName.isEmpty()"; return false; } //file-based project qDebug() << "Project File: " << aFileName; KDbConnectionData fileConnData; fileConnData.setDatabaseName(aFileName); QString detectedDriverId; int detectOptions = 0; if (readOnly) { detectOptions |= KexiStartupHandler::OpenReadOnly; } KexiStartupData::Import importActionData; bool forceReadOnly; const tristate res = KexiStartupHandler::detectActionForFile( &importActionData, &detectedDriverId, fileConnData.driverId(), aFileName, this, detectOptions, &forceReadOnly); if (forceReadOnly) { readOnly = true; } if (true != res) return res; if (importActionData) { //importing requested return showProjectMigrationWizard(importActionData.mimeType, importActionData.fileName); } fileConnData.setDriverId(detectedDriverId); if (fileConnData.driverId().isEmpty()) return false; //opening requested projectData = new KexiProjectData(fileConnData); deleteAfterOpen = true; } if (!projectData) return false; projectData->setReadOnly(readOnly); projectData->autoopenObjects = autoopenObjects; const tristate res = openProject(*projectData); if (deleteAfterOpen) //projectData object has been copied delete projectData; return res; } tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName) { QString fileNameForConnectionData; if (aFileName.isEmpty()) { //try .kexic file if (cdata) fileNameForConnectionData = Kexi::connset().fileNameForConnectionData(*cdata); } return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName); } tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName) { QString fileName(aFileName); QStringList args; // open a file-based project or a server connection provided as a .kexic file // (we have no other simple way to provide the startup data to a new process) if (fileName.isEmpty()) { //try .kexic file if (!fileNameForConnectionData.isEmpty()) args << "--skip-conn-dialog"; //user does not expect conn. dialog to be shown here if (dbName.isEmpty()) { //use 'kexi --skip-conn-dialog file.kexic' fileName = fileNameForConnectionData; } else { //use 'kexi --skip-conn-dialog --connection file.kexic dbName' if (fileNameForConnectionData.isEmpty()) { qWarning() << "fileNameForConnectionData?"; return false; } args << "--connection" << fileNameForConnectionData; fileName = dbName; } } if (fileName.isEmpty()) { qWarning() << "fileName?"; return false; } //! @todo use KRun //! @todo untested //Can arguments be supplied to KRun like is used here? AP args << fileName; const bool ok = QProcess::startDetached( qApp->applicationFilePath(), args, QFileInfo(fileName).absoluteDir().absolutePath()); if (!ok) { d->showStartProcessMsg(args); } if (d->tabbedToolBar) { d->tabbedToolBar->hideMainMenu(); } return ok; } void KexiMainWindow::slotProjectWelcome() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_welcome"); KexiWelcomeAssistant* assistant = new KexiWelcomeAssistant( Kexi::recentProjects(), this); connect(assistant, SIGNAL(openProject(KexiProjectData,QString,bool*)), this, SLOT(openProject(KexiProjectData,QString,bool*))); d->tabbedToolBar->setMainMenuContent(assistant); } void KexiMainWindow::slotProjectSave() { if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) { return; } saveObject(currentWindow()); updateAppCaption(); invalidateActions(); } void KexiMainWindow::slotProjectSaveAs() { if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) { return; } saveObject(currentWindow(), QString(), SaveObjectAs); updateAppCaption(); invalidateActions(); } void KexiMainWindow::slotProjectPrint() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) printItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectPrintPreview() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) printPreviewForItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectPageSetup() { #ifdef KEXI_QUICK_PRINTING_SUPPORT if (currentWindow() && currentWindow()->partItem()) showPageSetupForItem(currentWindow()->partItem()); #endif } void KexiMainWindow::slotProjectExportDataTable() { if (currentWindow() && currentWindow()->partItem()) exportItemAsDataTable(currentWindow()->partItem()); } void KexiMainWindow::slotProjectProperties() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_properties"); // dummy QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("project_properties")->text()); d->tabbedToolBar->setMainMenuContent(dummy); //! @todo load the implementation not the ui :) // ProjectSettingsUI u(this); // u.exec(); } void KexiMainWindow::slotProjectImportExportOrSend() { if (!d->tabbedToolBar) return; d->tabbedToolBar->showMainMenu("project_import_export_send"); KexiImportExportAssistant* assistant = new KexiImportExportAssistant( d->action_project_import_export_send, d->action_tools_import_project); connect(assistant, SIGNAL(importProject()), this, SLOT(slotToolsImportProject())); d->tabbedToolBar->setMainMenuContent(assistant); } void KexiMainWindow::slotProjectClose() { closeProject(); } void KexiMainWindow::slotProjectRelations() { if (!d->prj) return; KexiWindow *w = KexiInternalPart::createKexiWindowInstance("org.kexi-project.relations", this); activateWindow(*w); } void KexiMainWindow::slotImportFile() { KEXI_UNFINISHED("Import: " + xi18n("From File...")); } void KexiMainWindow::slotImportServer() { KEXI_UNFINISHED("Import: " + xi18n("From Server...")); } void KexiMainWindow::slotProjectQuit() { if (~ closeProject()) return; close(); } void KexiMainWindow::slotActivateNavigator() { if (!d->navigator) { return; } d->navigator->setFocus(); } void KexiMainWindow::slotActivateMainArea() { if (currentWindow()) currentWindow()->setFocus(); } void KexiMainWindow::slotActivatePropertyEditor() { if (!d->propEditor) { return; } if (d->propEditorTabWidget->currentWidget()) d->propEditorTabWidget->currentWidget()->setFocus(); } void KexiMainWindow::slotShowNavigator() { if (d->navDockWidget) d->navDockWidget->setVisible(!d->navDockWidget->isVisible()); } void KexiMainWindow::slotShowPropertyEditor() { if (d->propEditorDockWidget) d->propEditorDockWidget->setVisible(!d->propEditorDockWidget->isVisible()); } tristate KexiMainWindow::switchToViewMode(KexiWindow& window, Kexi::ViewMode viewMode) { const Kexi::ViewMode prevViewMode = currentWindow()->currentViewMode(); if (prevViewMode == viewMode) return true; if (!activateWindow(window)) return false; if (!currentWindow()) { return false; } if (&window != currentWindow()) return false; if (!currentWindow()->supportsViewMode(viewMode)) { showErrorMessage(xi18nc("@info", "Selected view is not supported for %1 object.", currentWindow()->partItem()->name()), xi18nc("@info", "Selected view (%1) is not supported by this object type (%2).", Kexi::nameForViewMode(viewMode), currentWindow()->part()->info()->name())); return false; } updateCustomPropertyPanelTabs(currentWindow()->part(), prevViewMode, currentWindow()->part(), viewMode); tristate res = currentWindow()->switchToViewMode(viewMode); if (!res) { updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert showErrorMessage(xi18n("Switching to other view failed (%1).", Kexi::nameForViewMode(viewMode)), currentWindow()); return false; } if (~res) { updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert return cancelled; } activateWindow(window); invalidateSharedActions(); invalidateProjectWideActions(); d->updateFindDialogContents(); d->updatePropEditorVisibility(viewMode); QString origTabToActivate; if (viewMode == Kexi::DesignViewMode) { // Save the orig tab: we want to back to design tab // when user moved to data view and then immediately to design view. origTabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier()); } restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), viewMode, currentWindow()->partItem()->identifier()); if (viewMode == Kexi::DesignViewMode) { activateDesignTab(currentWindow()->partItem()->pluginId()); // Restore the saved tab to the orig one. restoreDesignTabIfNeeded() saved tools tab probably. d->tabsToActivateOnShow.insert(currentWindow()->partItem()->identifier(), origTabToActivate); } return true; } void KexiMainWindow::slotViewDataMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::DataViewMode); } void KexiMainWindow::slotViewDesignMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::DesignViewMode); } void KexiMainWindow::slotViewTextMode() { if (currentWindow()) switchToViewMode(*currentWindow(), Kexi::TextViewMode); } //! Used to control if we're not Saving-As object under the original name class SaveAsObjectNameValidator : public KexiNameDialogValidator { public: SaveAsObjectNameValidator(const QString &originalObjectName) : m_originalObjectName(originalObjectName) { } virtual bool validate(KexiNameDialog *dialog) const { if (dialog->widget()->nameText() == m_originalObjectName) { KMessageBox::information(dialog, xi18nc("Could not save object under the original name.", "Could not save under the original name.")); return false; } return true; } private: QString m_originalObjectName; }; tristate KexiMainWindow::getNewObjectInfo( KexiPart::Item *partItem, const QString &originalName, KexiPart::Part *part, bool allowOverwriting, bool *overwriteNeeded, const QString& messageWhenAskingForName) { //data was never saved in the past -we need to create a new object at the backend KexiPart::Info *info = part->info(); if (!d->nameDialog) { d->nameDialog = new KexiNameDialog( messageWhenAskingForName, this); //check if that name is allowed d->nameDialog->widget()->addNameSubvalidator( new KDbObjectNameValidator(project()->dbConnection()->driver())); d->nameDialog->buttonBox()->button(QDialogButtonBox::Ok)->setText(xi18nc("@action:button Save object", "Save")); } else { d->nameDialog->widget()->setMessageText(messageWhenAskingForName); } d->nameDialog->widget()->setCaptionText(partItem->caption()); d->nameDialog->widget()->setNameText(partItem->name()); d->nameDialog->setWindowTitle(xi18nc("@title:window", "Save Object As")); d->nameDialog->setDialogIcon(info->iconName()); d->nameDialog->setAllowOverwriting(allowOverwriting); if (!originalName.isEmpty()) { d->nameDialog->setValidator(new SaveAsObjectNameValidator(originalName)); } if (d->nameDialog->execAndCheckIfObjectExists(*project(), *part, overwriteNeeded) != QDialog::Accepted) { return cancelled; } // close window of object that will be overwritten if (*overwriteNeeded) { KexiPart::Item* overwrittenItem = project()->item(info, d->nameDialog->widget()->nameText()); if (overwrittenItem) { KexiWindow * openedWindow = d->openedWindowFor(overwrittenItem->identifier()); if (openedWindow) { const tristate res = closeWindow(openedWindow); if (res != true) { return res; } } } } //update name and caption partItem->setName(d->nameDialog->widget()->nameText()); partItem->setCaption(d->nameDialog->widget()->captionText()); return true; } //! Used to delete part item on exit from block class PartItemDeleter : public QScopedPointer { public: explicit PartItemDeleter(KexiProject *prj) : m_prj(prj) {} ~PartItemDeleter() { if (!isNull()) { m_prj->deleteUnstoredItem(take()); } } private: KexiProject *m_prj; }; static void showSavingObjectFailedErrorMessage(KexiMainWindow *wnd, KexiPart::Item *item) { wnd->showErrorMessage( xi18nc("@info Saving object failed", "Saving %1 object failed.", item->name()), wnd->currentWindow()); } tristate KexiMainWindow::saveObject(KexiWindow *window, const QString& messageWhenAskingForName, SaveObjectOptions options) { tristate res; bool saveAs = options & SaveObjectAs; if (!saveAs && !window->neverSaved()) { //data was saved in the past -just save again res = window->storeData(options & DoNotAsk); if (!res) { showSavingObjectFailedErrorMessage(this, window->partItem()); } return res; } if (saveAs && window->neverSaved()) { //if never saved, saveAs == save saveAs = false; } const int oldItemID = window->partItem()->identifier(); KexiPart::Item *partItem; KexiView::StoreNewDataOptions storeNewDataOptions; PartItemDeleter itemDeleter(d->prj); if (saveAs) { partItem = d->prj->createPartItem(window->part()); if (!partItem) { //! @todo error return false; } itemDeleter.reset(partItem); } else { partItem = window->partItem(); } bool overwriteNeeded; res = getNewObjectInfo(partItem, saveAs ? window->partItem()->name() : QString(), window->part(), true /*allowOverwriting*/, &overwriteNeeded, messageWhenAskingForName); if (res != true) return res; if (overwriteNeeded) { storeNewDataOptions |= KexiView::OverwriteExistingData; } if (saveAs) { res = window->storeDataAs(partItem, storeNewDataOptions); } else { res = window->storeNewData(storeNewDataOptions); } if (~res) return cancelled; if (!res) { showSavingObjectFailedErrorMessage(this, partItem); return false; } d->updateWindowId(window, oldItemID); invalidateProjectWideActions(); itemDeleter.take(); return true; } tristate KexiMainWindow::closeWindow(KexiWindow *window) { return closeWindow(window ? window : currentWindow(), true); } tristate KexiMainWindow::closeCurrentWindow() { return closeWindow(0); } tristate KexiMainWindow::closeWindowForTab(int tabIndex) { KexiWindow* window = windowForTab(tabIndex); if (!window) return false; return closeWindow(window); } tristate KexiMainWindow::closeWindow(KexiWindow *window, bool layoutTaskBar, bool doNotSaveChanges) { //! @todo KEXI3 KexiMainWindow::closeWindow() ///@note Q_UNUSED layoutTaskBar Q_UNUSED(layoutTaskBar); if (!window) return true; if (d->insideCloseWindow) return true; const int previousItemId = window->partItem()->identifier(); #ifndef KEXI_NO_PENDING_DIALOGS d->addItemToPendingWindows(window->partItem(), Private::WindowClosingJob); #endif d->insideCloseWindow = true; if (window == currentWindow() && !window->isAttached()) { if (d->propEditor) { // ah, closing detached window - better switch off property buffer right now... d->propertySet = 0; d->propEditor->editor()->changeSet(0); } } bool remove_on_closing = window->partItem() ? window->partItem()->neverSaved() : false; if (window->isDirty() && !d->forceWindowClosing && !doNotSaveChanges) { //more accurate tool tips and what's this KGuiItem saveChanges(KStandardGuiItem::save()); saveChanges.setToolTip(xi18n("Save changes")); saveChanges.setWhatsThis( xi18nc("@info", "Saves all recent changes made in %1 object.", window->partItem()->name())); KGuiItem discardChanges(KStandardGuiItem::discard()); discardChanges.setWhatsThis( xi18nc("@info", "Discards all recent changes made in %1 object.", window->partItem()->name())); //dialog's data is dirty: //--adidional message, e.g. table designer will return // "Note: This table is already filled with data which will be removed." // if the window is in design view mode. const KLocalizedString additionalMessage( window->part()->i18nMessage(":additional message before saving design", window)); QString additionalMessageString; if (!additionalMessage.isEmpty()) additionalMessageString = additionalMessage.toString(); if (additionalMessageString.startsWith(':')) additionalMessageString.clear(); if (!additionalMessageString.isEmpty()) additionalMessageString = "

    " + additionalMessageString + "

    "; const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this, "

    " + window->part()->i18nMessage("Design of object %1 has been modified.", window) .subs(window->partItem()->name()).toString() + "

    " + xi18n("Do you want to save changes?") + "

    " + additionalMessageString /*may be empty*/, QString(), saveChanges, discardChanges); if (questionRes == KMessageBox::Cancel) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return cancelled; } if (questionRes == KMessageBox::Yes) { //save it tristate res = saveObject(window, QString(), DoNotAsk); if (!res || ~res) { //! @todo show error info; (retry/ignore/cancel) #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return res; } remove_on_closing = false; } } const int window_id = window->id(); //remember now, because removeObject() can destruct partitem object if (remove_on_closing) { //we won't save this object, and it was never saved -remove it if (!removeObject(window->partItem(), true)) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); #endif //msg? //! @todo ask if we'd continue and return true/false d->insideCloseWindow = false; d->windowsToClose.clear(); //give up with 'close all' return false; } } else { //not dirty now if (d->navigator) { d->navigator->updateItemName(*window->partItem(), false); } } hideDesignTab(previousItemId, QString()); d->removeWindow(window_id); d->setWindowContainerExistsFor(window->partItem()->identifier(), false); QWidget *windowContainer = window->parentWidget(); d->mainWidget->tabWidget()->removeTab( d->mainWidget->tabWidget()->indexOf(windowContainer)); #ifdef KEXI_QUICK_PRINTING_SUPPORT //also remove from 'print setup dialogs' cache, if needed int printedObjectID = 0; if (d->pageSetupWindowItemID2dataItemID_map.contains(window_id)) printedObjectID = d->pageSetupWindowItemID2dataItemID_map[ window_id ]; d->pageSetupWindows.remove(printedObjectID); #endif delete windowContainer; //focus navigator if nothing else available if (d->openedWindowsCount() == 0) { if (d->navigator) { d->navigator->setFocus(); } d->updatePropEditorVisibility(Kexi::NoViewMode); } invalidateActions(); d->insideCloseWindow = false; if (!d->windowsToClose.isEmpty()) {//continue 'close all' KexiWindow* w = d->windowsToClose.takeAt(0); closeWindow(w, true); } #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window_id); //perform pending global action that was suspended: if (!d->pendingWindowsExist()) { d->executeActionWhenPendingJobsAreFinished(); } #endif d->mainWidget->slotCurrentTabIndexChanged(d->mainWidget->tabWidget()->currentIndex()); showDesignTabIfNeeded(0); if (currentWindow()) { restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), currentWindow()->currentViewMode(), 0); } d->tabsToActivateOnShow.remove(previousItemId); return true; } QWidget* KexiMainWindow::findWindow(QWidget *w) { while (w && !acceptsSharedActions(w)) { if (w == d->propEditorDockWidget) return currentWindow(); w = w->parentWidget(); } return w; } KexiWindow* KexiMainWindow::openedWindowFor(int identifier) { return d->openedWindowFor(identifier); } KexiWindow* KexiMainWindow::openedWindowFor(const KexiPart::Item* item) { return item ? openedWindowFor(item->identifier()) : 0; } KDbQuerySchema* KexiMainWindow::unsavedQuery(int queryId) { KexiWindow * queryWindow = openedWindowFor(queryId); if (!queryWindow || !queryWindow->isDirty()) { return 0; } return queryWindow->part()->currentQuery(queryWindow->viewForMode(Kexi::DataViewMode)); } QList KexiMainWindow::currentParametersForQuery(int queryId) const { KexiWindow *queryWindow = d->openedWindowFor(queryId); if (!queryWindow) { return QList(); } KexiView *view = queryWindow->viewForMode(Kexi::DataViewMode); if (!view) { return QList(); } return view->currentParameters(); } bool KexiMainWindow::acceptsSharedActions(QObject *w) { return w->inherits("KexiWindow") || w->inherits("KexiView"); } bool KexiMainWindow::openingAllowed(KexiPart::Item* item, Kexi::ViewMode viewMode, QString* errorMessage) { //qDebug() << viewMode; //! @todo this can be more complex once we deliver ACLs... if (!d->userMode) return true; KexiPart::Part * part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) { if (errorMessage) { *errorMessage = Kexi::partManager().result().message(); } } //qDebug() << part << item->pluginId(); //if (part) // qDebug() << item->pluginId() << part->info()->supportedUserViewModes(); return part && (part->info()->supportedUserViewModes() & viewMode); } KexiWindow * KexiMainWindow::openObject(const QString& pluginId, const QString& name, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs) { KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name); if (!item) return 0; return openObject(item, viewMode, openingCancelled, staticObjectArgs); } KexiWindow * KexiMainWindow::openObject(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs, QString* errorMessage) { Q_ASSERT(openingCancelled); if (!d->prj || !item) { return 0; } if (!openingAllowed(item, viewMode, errorMessage)) { if (errorMessage) *errorMessage = xi18nc( "opening is not allowed in \"data view/design view/text view\" mode", "opening is not allowed in \"%1\" mode", Kexi::nameForViewMode(viewMode)); *openingCancelled = true; return 0; } //qDebug() << d->prj << item; KexiWindow *prevWindow = currentWindow(); KexiUtils::WaitCursor wait; #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { *openingCancelled = true; return 0; } #else KexiWindow *window = openedWindowFor(item); #endif int previousItemId = currentWindow() ? currentWindow()->partItem()->identifier() : 0; *openingCancelled = false; bool alreadyOpened = false; KexiWindowContainer *windowContainer = 0; if (window) { if (viewMode != window->currentViewMode()) { if (true != switchToViewMode(*window, viewMode)) return 0; } else activateWindow(*window); alreadyOpened = true; } else { if (d->windowContainerExistsFor(item->identifier())) { // window not yet present but window container exists: return 0 and wait return 0; } KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); d->updatePropEditorVisibility(viewMode, part ? part->info() : 0); //update tabs before opening updateCustomPropertyPanelTabs(currentWindow() ? currentWindow()->part() : 0, currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode, part, viewMode); // open new tab earlier windowContainer = new KexiWindowContainer(d->mainWidget->tabWidget()); d->setWindowContainerExistsFor(item->identifier(), true); const int tabIndex = d->mainWidget->tabWidget()->addTab( windowContainer, QIcon::fromTheme(part ? part->info()->iconName() : QString()), KexiWindow::windowTitleForItem(*item)); d->mainWidget->tabWidget()->setTabToolTip(tabIndex, KexiPart::fullCaptionForItem(item, part)); QString whatsThisText; if (part) { whatsThisText = xi18nc("@info", "Tab for %1 (%2).", item->captionOrName(), part->info()->name()); } else { whatsThisText = xi18nc("@info", "Tab for %1.", item->captionOrName()); } d->mainWidget->tabWidget()->setTabWhatsThis(tabIndex, whatsThisText); d->mainWidget->tabWidget()->setCurrentWidget(windowContainer); #ifndef KEXI_NO_PENDING_DIALOGS d->addItemToPendingWindows(item, Private::WindowOpeningJob); #endif window = d->prj->openObject(windowContainer, item, viewMode, staticObjectArgs); if (window) { windowContainer->setWindow(window); // update text and icon d->mainWidget->tabWidget()->setTabText( d->mainWidget->tabWidget()->indexOf(windowContainer), window->windowTitle()); d->mainWidget->tabWidget()->setTabIcon( d->mainWidget->tabWidget()->indexOf(windowContainer), window->windowIcon()); } } if (!window || !activateWindow(*window)) { #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(item->identifier()); #endif d->setWindowContainerExistsFor(item->identifier(), false); d->mainWidget->tabWidget()->removeTab( d->mainWidget->tabWidget()->indexOf(windowContainer)); delete windowContainer; updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert //! @todo add error msg... return 0; } if (viewMode != window->currentViewMode()) invalidateSharedActions(); #ifndef KEXI_NO_PENDING_DIALOGS d->removePendingWindow(window->id()); //perform pending global action that was suspended: if (!d->pendingWindowsExist()) { d->executeActionWhenPendingJobsAreFinished(); } #endif if (window && !alreadyOpened) { // Call switchToViewMode() and propertySetSwitched() again here because // this is the time when then new window is the current one - previous call did nothing. switchToViewMode(*window, window->currentViewMode()); currentWindow()->selectedView()->propertySetSwitched(); } invalidateProjectWideActions(); restoreDesignTabIfNeeded(item->pluginId(), viewMode, previousItemId); activateDesignTabIfNeeded(item->pluginId(), viewMode); QString origTabToActivate; if (prevWindow) { // Save the orig tab for prevWindow that was stored in the restoreDesignTabIfNeeded() call above origTabToActivate = d->tabsToActivateOnShow.value(prevWindow->partItem()->identifier()); } activeWindowChanged(window, prevWindow); if (prevWindow) { // Restore the orig tab d->tabsToActivateOnShow.insert(prevWindow->partItem()->identifier(), origTabToActivate); } return window; } KexiWindow * KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode) { bool openingCancelled; return openObjectFromNavigator(item, viewMode, &openingCancelled); } KexiWindow * KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled) { Q_ASSERT(openingCancelled); if (!openingAllowed(item, viewMode)) { *openingCancelled = true; return 0; } if (!d->prj || !item) return 0; #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { *openingCancelled = true; return 0; } #else KexiWindow *window = openedWindowFor(item); #endif *openingCancelled = false; if (window) { if (activateWindow(*window)) { return window; } } //if DataViewMode is not supported, try Design, then Text mode (currently useful for script part) KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) return 0; if (viewMode == Kexi::DataViewMode && !(part->info()->supportedViewModes() & Kexi::DataViewMode)) { if (part->info()->supportedViewModes() & Kexi::DesignViewMode) return openObjectFromNavigator(item, Kexi::DesignViewMode, openingCancelled); else if (part->info()->supportedViewModes() & Kexi::TextViewMode) return openObjectFromNavigator(item, Kexi::TextViewMode, openingCancelled); } //do the same as in openObject() return openObject(item, viewMode, openingCancelled); } tristate KexiMainWindow::closeObject(KexiPart::Item* item) { #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType == Private::WindowClosingJob) return true; else if (pendingType == Private::WindowOpeningJob) return cancelled; #else KexiWindow *window = openedWindowFor(item); #endif if (!window) return cancelled; return closeWindow(window); } bool KexiMainWindow::newObject(KexiPart::Info *info, bool* openingCancelled) { Q_ASSERT(openingCancelled); if (d->userMode) { *openingCancelled = true; return false; } *openingCancelled = false; if (!d->prj || !info) return false; KexiPart::Part *part = Kexi::partManager().part(info); if (!part) return false; KexiPart::Item *it = d->prj->createPartItem(info); if (!it) { //! @todo error return false; } if (!it->neverSaved()) { //only add stored objects to the browser d->navigator->model()->slotAddItem(it); } return openObject(it, Kexi::DesignViewMode, openingCancelled); } tristate KexiMainWindow::removeObject(KexiPart::Item *item, bool dontAsk) { if (d->userMode) return cancelled; if (!d->prj || !item) return false; KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId()); if (!part) return false; if (!dontAsk) { if (KMessageBox::No == KMessageBox::questionYesNo(this, xi18nc("@info Remove ?", "Do you want to permanently delete the following object?" "%1 %2" "If you click Delete, " "you will not be able to undo the deletion.", part->info()->name(), item->name()), xi18nc("@title:window Delete Object %1.", "Delete %1?", item->name()), KStandardGuiItem::del(), KStandardGuiItem::no(), QString(), KMessageBox::Notify | KMessageBox::Dangerous)) { return cancelled; } } tristate res = true; #ifdef KEXI_QUICK_PRINTING_SUPPORT //also close 'print setup' dialog for this item, if any KexiWindow * pageSetupWindow = d->pageSetupWindows[ item->identifier()]; const bool oldInsideCloseWindow = d->insideCloseWindow; { d->insideCloseWindow = false; if (pageSetupWindow) res = closeWindow(pageSetupWindow); } d->insideCloseWindow = oldInsideCloseWindow; if (!res || ~res) { return res; } #endif #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) { return cancelled; } #else KexiWindow *window = openedWindowFor(item); #endif if (window) {//close existing window const bool tmp = d->forceWindowClosing; d->forceWindowClosing = true; res = closeWindow(window); d->forceWindowClosing = tmp; //restore if (!res || ~res) { return res; } } #ifdef KEXI_QUICK_PRINTING_SUPPORT //in case the dialog is a 'print setup' dialog, also update d->pageSetupWindows int dataItemID = d->pageSetupWindowItemID2dataItemID_map[item->identifier()]; d->pageSetupWindowItemID2dataItemID_map.remove(item->identifier()); d->pageSetupWindows.remove(dataItemID); #endif if (!d->prj->removeObject(item)) { //! @todo better msg showSorryMessage(xi18n("Could not remove object.")); return false; } return true; } void KexiMainWindow::renameObject(KexiPart::Item *item, const QString& _newName, bool *success) { Q_ASSERT(success); if (d->userMode) { *success = false; return; } QString newName = _newName.trimmed(); if (newName.isEmpty()) { showSorryMessage(xi18n("Could not set empty name for this object.")); *success = false; return; } KexiWindow *window = openedWindowFor(item); if (window) { QString msg = xi18nc("@info", "Before renaming object %1 it should be closed." "Do you want to close it?", item->name()); int r = KMessageBox::questionYesNo(this, msg, QString(), KStandardGuiItem::closeWindow(), KStandardGuiItem::cancel()); if (r != KMessageBox::Yes) { *success = false; return; } } setMessagesEnabled(false); //to avoid double messages const bool res = d->prj->renameObject(item, newName); setMessagesEnabled(true); if (!res) { showErrorMessage(xi18nc("@info", "Renaming object %1 failed.", newName), d->prj); *success = false; return; } } void KexiMainWindow::setObjectCaption(KexiPart::Item *item, const QString& _newCaption, bool *success) { Q_ASSERT(success); if (d->userMode) { *success = false; return; } QString newCaption = _newCaption.trimmed(); setMessagesEnabled(false); //to avoid double messages const bool res = d->prj->setObjectCaption(item, newCaption); setMessagesEnabled(true); if (!res) { showErrorMessage(xi18nc("@info", "Setting caption for object %1 failed.", newCaption), d->prj); *success = false; return; } } void KexiMainWindow::slotObjectRenamed(const KexiPart::Item &item, const QString& oldName) { Q_UNUSED(oldName); #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(&item, pendingType); if (pendingType != Private::NoJob) return; #else KexiWindow *window = openedWindowFor(&item); #endif if (!window) return; //change item window->updateCaption(); if (static_cast(currentWindow()) == window)//optionally, update app. caption updateAppCaption(); } void KexiMainWindow::acceptPropertySetEditing() { if (d->propEditor) d->propEditor->editor()->acceptInput(); } void KexiMainWindow::propertySetSwitched(KexiWindow *window, bool force, bool preservePrevSelection, bool sortedProperties, const QByteArray& propertyToSelect) { KexiWindow* _currentWindow = currentWindow(); //qDebug() << "currentWindow(): " // << (_currentWindow ? _currentWindow->windowTitle() : QString("NULL")) // << " window: " << (window ? window->windowTitle() : QString("NULL")); if (_currentWindow && _currentWindow != window) { d->propertySet = 0; //we'll need to move to another prop. set return; } if (d->propEditor) { KPropertySet *newSet = _currentWindow ? _currentWindow->propertySet() : 0; if (!newSet || (force || static_cast(d->propertySet) != newSet)) { d->propertySet = newSet; if (preservePrevSelection || force) { KPropertyEditorView::SetOptions options; if (preservePrevSelection) { options |= KPropertyEditorView::PreservePreviousSelection; } if (sortedProperties) { options |= KPropertyEditorView::AlphabeticalOrder; } if (propertyToSelect.isEmpty()) { d->propEditor->editor()->changeSet(d->propertySet, options); } else { d->propEditor->editor()->changeSet(d->propertySet, propertyToSelect, options); } } } } } void KexiMainWindow::slotDirtyFlagChanged(KexiWindow* window) { KexiPart::Item *item = window->partItem(); //update text in navigator and app. caption if (!d->userMode) { d->navigator->updateItemName(*item, window->isDirty()); } invalidateActions(); updateAppCaption(); d->mainWidget->tabWidget()->setTabText( d->mainWidget->tabWidget()->indexOf(window->parentWidget()), window->windowTitle()); } void KexiMainWindow::slotTipOfTheDay() { //! @todo } void KexiMainWindow::slotReportBug() { KexiBugReportDialog bugReport(this); bugReport.exec(); } bool KexiMainWindow::userMode() const { return d->userMode; } void KexiMainWindow::setupUserActions() { } void KexiMainWindow::slotToolsImportProject() { if (d->tabbedToolBar) d->tabbedToolBar->hideMainMenu(); showProjectMigrationWizard(QString(), QString()); } void KexiMainWindow::slotToolsImportTables() { if (project()) { QMap args; QDialog *dlg = KexiInternalPart::createModalDialogInstance("org.kexi-project.migration", "importtable", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart const int result = dlg->exec(); delete dlg; if (result != QDialog::Accepted) return; QString destinationTableName(args["destinationTableName"]); if (!destinationTableName.isEmpty()) { QString pluginId = "org.kexi-project.table"; bool openingCancelled; KexiMainWindow::openObject(pluginId, destinationTableName, Kexi::DataViewMode, &openingCancelled); } } } void KexiMainWindow::slotToolsCompactDatabase() { KexiProjectData *data = 0; KDbDriver *drv = 0; const bool projectWasOpened = d->prj; if (!d->prj) { KexiStartupDialog dlg( KexiStartupDialog::OpenExisting, 0, Kexi::connset(), this); if (dlg.exec() != QDialog::Accepted) return; if (dlg.selectedFileName().isEmpty()) { //! @todo add support for server based if needed? return; } KDbConnectionData cdata; cdata.setDatabaseName(dlg.selectedFileName()); //detect driver name for the selected file KexiStartupData::Import detectedImportAction; QString detectedDriverId; tristate res = KexiStartupHandler::detectActionForFile( &detectedImportAction, &detectedDriverId, QString() /*suggestedDriverId*/, cdata.databaseName(), 0, KexiStartupHandler::SkipMessages | KexiStartupHandler::ThisIsAProjectFile | KexiStartupHandler::DontConvert); if (true == res && !detectedImportAction) { cdata.setDriverId(detectedDriverId); drv = Kexi::driverManager().driver(cdata.driverId()); } if (!drv || !(drv->features() & KDbDriver::CompactingDatabaseSupported)) { KMessageBox::information(this, xi18n("Compacting database file %1 is not supported.", QDir::toNativeSeparators(cdata.databaseName()))); return; } data = new KexiProjectData(cdata); } else { //sanity if (!(d->prj && d->prj->dbConnection() && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported))) return; KGuiItem yesItem(KStandardGuiItem::cont()); yesItem.setText(xi18nc("@action:button Compact database", "Compact")); if (KMessageBox::Yes != KMessageBox::questionYesNo(this, xi18n("The current project has to be closed before compacting the database. " "It will be open again after compacting.\n\nDo you want to continue?"), QString(), yesItem, KStandardGuiItem::cancel())) { return; } data = new KexiProjectData(*d->prj->data()); // a copy drv = d->prj->dbConnection()->driver(); const tristate res = closeProject(); if (~res || !res) { delete data; return; } } if (!drv->adminTools().vacuum(*data->connectionData(), data->databaseName())) { showErrorMessage(QString(), &drv->adminTools()); } if (projectWasOpened) openProject(*data); delete data; } tristate KexiMainWindow::showProjectMigrationWizard(const QString& mimeType, const QString& databaseName) { return d->showProjectMigrationWizard(mimeType, databaseName, 0); } tristate KexiMainWindow::showProjectMigrationWizard( const QString& mimeType, const QString& databaseName, const KDbConnectionData &cdata) { return d->showProjectMigrationWizard(mimeType, databaseName, &cdata); } tristate KexiMainWindow::executeItem(KexiPart::Item* item) { KexiPart::Info *info = item ? Kexi::partManager().infoForPluginId(item->pluginId()) : 0; if ((! info) || (! info->isExecuteSupported())) return false; KexiPart::Part *part = Kexi::partManager().part(info); if (!part) return false; return part->execute(item); } void KexiMainWindow::slotProjectImportDataTable() { //! @todo allow data appending (it is not possible now) if (d->userMode) return; QMap args; args.insert("sourceType", "file"); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart dlg->exec(); delete dlg; } tristate KexiMainWindow::executeCustomActionForObject(KexiPart::Item* item, const QString& actionName) { if (actionName == "exportToCSV") return exportItemAsDataTable(item); else if (actionName == "copyToClipboardAsCSV") return copyItemToClipboardAsDataTable(item); qWarning() << "no such action:" << actionName; return false; } tristate KexiMainWindow::exportItemAsDataTable(KexiPart::Item* item) { if (!item) return false; QMap args; if (!checkForDirtyFlagOnExport(item, &args)) { return false; } //! @todo: accept record changes... args.insert("destinationType", "file"); args.insert("itemId", QString::number(item->identifier())); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args); if (!dlg) return false; //error msg has been shown by KexiInternalPart int result = dlg->exec(); delete dlg; return result == QDialog::Rejected ? tristate(cancelled) : tristate(true); } bool KexiMainWindow::checkForDirtyFlagOnExport(KexiPart::Item *item, QMap *args) { //! @todo: handle tables if (item->pluginId() != "org.kexi-project.query") { return true; } KexiWindow * itemWindow = openedWindowFor(item); if (itemWindow && itemWindow->isDirty()) { tristate result; if (item->neverSaved()) { result = true; } else { int prevWindowId = 0; if (!itemWindow->isVisible()) { prevWindowId = currentWindow()->id(); activateWindow(itemWindow->id()); } result = askOnExportingChangedQuery(item); if (prevWindowId != 0) { activateWindow(prevWindowId); } } if (~result) { return false; } else if (true == result) { args->insert("useTempQuery","1"); } } return true; } tristate KexiMainWindow::askOnExportingChangedQuery(KexiPart::Item *item) const { const KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancel(const_cast(this), xi18nc("@info", "Design of query %1 that you want to export data" " from is changed and has not yet been saved. Do you want to use data" " from the changed query for exporting or from its original (saved)" " version?", item->captionOrName()), QString(), KGuiItem(xi18nc("@action:button Export query data", "Use the Changed Query")), KGuiItem(xi18nc("@action:button Export query data", "Use the Original Query")), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); if (result == KMessageBox::Yes) { return true; } else if (result == KMessageBox::No) { return false; } return cancelled; } bool KexiMainWindow::printItem(KexiPart::Item* item, const QString& titleText) { //! @todo printItem(item, KexiSimplePrintingSettings::load(), titleText); Q_UNUSED(item) Q_UNUSED(titleText) return false; } tristate KexiMainWindow::printItem(KexiPart::Item* item) { return printItem(item, QString()); } bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item, const QString& titleText, bool reload) { //! @todo printPreviewForItem(item, KexiSimplePrintingSettings::load(), titleText, reload); Q_UNUSED(item) Q_UNUSED(titleText) Q_UNUSED(reload) return false; } tristate KexiMainWindow::printPreviewForItem(KexiPart::Item* item) { return printPreviewForItem(item, QString(), //! @todo store cached record data? true/*reload*/); } tristate KexiMainWindow::showPageSetupForItem(KexiPart::Item* item) { Q_UNUSED(item) //! @todo check if changes to this object's design are saved, if not: ask for saving //! @todo accept record changes... //! @todo printActionForItem(item, PageSetupForItem); return false; } //! @todo reenable printItem() when ported #if 0 bool KexiMainWindow::printItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText) { //! @todo: check if changes to this object's design are saved, if not: ask for saving //! @todo: accept record changes... KexiSimplePrintingCommand cmd(this, item->identifier()); //modal return cmd.print(settings, titleText); } bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText, bool reload) { //! @todo: check if changes to this object's design are saved, if not: ask for saving //! @todo: accept record changes... KexiSimplePrintingCommand* cmd = d->openedCustomObjectsForItem( item, "KexiSimplePrintingCommand"); if (!cmd) { d->addOpenedCustomObjectForItem( item, cmd = new KexiSimplePrintingCommand(this, item->identifier()), "KexiSimplePrintingCommand" ); } return cmd->showPrintPreview(settings, titleText, reload); } tristate KexiMainWindow::printActionForItem(KexiPart::Item* item, PrintActionType action) { if (!item) return false; KexiPart::Info *info = Kexi::partManager().infoForPluginId(item->pluginId()); if (!info->isPrintingSupported()) return false; KexiWindow *printingWindow = d->pageSetupWindows[ item->identifier()]; if (printingWindow) { if (!activateWindow(*printingWindow)) return false; if (action == PreviewItem || action == PrintItem) { QTimer::singleShot(0, printingWindow->selectedView(), (action == PreviewItem) ? SLOT(printPreview()) : SLOT(print())); } return true; } #ifndef KEXI_NO_PENDING_DIALOGS Private::PendingJobType pendingType; KexiWindow *window = d->openedWindowFor(item, pendingType); if (pendingType != Private::NoJob) return cancelled; #else KexiWindow *window = openedWindowFor(item); #endif if (window) { // accept record changes QWidget *prevFocusWidget = focusWidget(); window->setFocus(); d->action_data_save_row->activate(QAction::Trigger); if (prevFocusWidget) prevFocusWidget->setFocus(); // opened: check if changes made to this dialog are saved, if not: ask for saving if (window->neverSaved()) //sanity check return false; if (window->isDirty()) { KGuiItem saveChanges(KStandardGuiItem::save()); saveChanges.setToolTip(futureI18n("Save changes")); saveChanges.setWhatsThis( futureI18n("Pressing this button will save all recent changes made in \"%1\" object.", item->name())); KGuiItem doNotSave(KStandardGuiItem::no()); doNotSave.setWhatsThis( futureI18n("Pressing this button will ignore all unsaved changes made in \"%1\" object.", window->partItem()->name())); QString question; if (action == PrintItem) question = futureI18n("Do you want to save changes before printing?"); else if (action == PreviewItem) question = futureI18n("Do you want to save changes before making print preview?"); else if (action == PageSetupForItem) question = futureI18n("Do you want to save changes before showing page setup?"); else return false; const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this, "

    " + window->part()->i18nMessage("Design of object %1 has been modified.", window) .subs(item->name()) + "

    " + question + "

    ", QString(), saveChanges, doNotSave); if (KMessageBox::Cancel == questionRes) return cancelled; if (KMessageBox::Yes == questionRes) { tristate savingRes = saveObject(window, QString(), DoNotAsk); if (true != savingRes) return savingRes; } } } KexiPart::Part * printingPart = Kexi::partManager().partForClass("org.kexi-project.simpleprinting"); if (!printingPart) printingPart = new KexiSimplePrintingPart(); //hardcoded as there're no .desktop file KexiPart::Item* printingPartItem = d->prj->createPartItem( printingPart, item->name() //<-- this will look like "table1 : printing" on the window list ); QMap staticObjectArgs; staticObjectArgs["identifier"] = QString::number(item->identifier()); if (action == PrintItem) staticObjectArgs["action"] = "print"; else if (action == PreviewItem) staticObjectArgs["action"] = "printPreview"; else if (action == PageSetupForItem) staticObjectArgs["action"] = "pageSetup"; else return false; bool openingCancelled; printingWindow = openObject(printingPartItem, Kexi::DesignViewMode, &openingCancelled, &staticObjectArgs); if (openingCancelled) return cancelled; if (!printingWindow) //sanity return false; d->pageSetupWindows.insert(item->identifier(), printingWindow); d->pageSetupWindowItemID2dataItemID_map.insert( printingWindow->partItem()->identifier(), item->identifier()); return true; } #endif void KexiMainWindow::slotEditCopySpecialDataTable() { KexiPart::Item* item = d->navigator->selectedPartItem(); if (item) copyItemToClipboardAsDataTable(item); } tristate KexiMainWindow::copyItemToClipboardAsDataTable(KexiPart::Item* item) { if (!item) return false; QMap args; if (!checkForDirtyFlagOnExport(item, &args)) { return false; } args.insert("destinationType", "clipboard"); args.insert("itemId", QString::number(item->identifier())); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args); if (!dlg) return false; //error msg has been shown by KexiInternalPart const int result = dlg->exec(); delete dlg; return result == QDialog::Rejected ? tristate(cancelled) : tristate(true); } void KexiMainWindow::slotEditPasteSpecialDataTable() { //! @todo allow data appending (it is not possible now) if (d->userMode) return; QMap args; args.insert("sourceType", "clipboard"); QDialog *dlg = KexiInternalPart::createModalDialogInstance( "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args); if (!dlg) return; //error msg has been shown by KexiInternalPart dlg->exec(); delete dlg; } void KexiMainWindow::slotEditFind() { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; d->updateFindDialogContents(true/*create if does not exist*/); d->findDialog()->setReplaceMode(false); d->findDialog()->show(); d->findDialog()->activateWindow(); d->findDialog()->raise(); } void KexiMainWindow::slotEditFind(bool next) { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; tristate res = iface->find( d->findDialog()->valueToFind(), d->findDialog()->options(), next); if (~res) return; d->findDialog()->updateMessage(true == res); //! @todo result } void KexiMainWindow::slotEditFindNext() { slotEditFind(true); } void KexiMainWindow::slotEditFindPrevious() { slotEditFind(false); } void KexiMainWindow::slotEditReplace() { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; d->updateFindDialogContents(true/*create if does not exist*/); d->findDialog()->setReplaceMode(true); //! @todo slotEditReplace() d->findDialog()->show(); d->findDialog()->activateWindow(); } void KexiMainWindow::slotEditReplaceNext() { slotEditReplace(false); } void KexiMainWindow::slotEditReplace(bool all) { KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface(); if (!iface) return; //! @todo add question: "Do you want to replace every occurrence of \"%1\" with \"%2\"? //! You won't be able to undo this." + "Do not ask again". tristate res = iface->findNextAndReplace( d->findDialog()->valueToFind(), d->findDialog()->valueToReplaceWith(), d->findDialog()->options(), all); d->findDialog()->updateMessage(true == res); //! @todo result } void KexiMainWindow::slotEditReplaceAll() { slotEditReplace(true); } void KexiMainWindow::highlightObject(const QString& pluginId, const QString& name) { slotShowNavigator(); if (!d->prj) return; KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name); if (!item) return; if (d->navigator) { d->navigator->selectItem(*item); } } void KexiMainWindow::slotPartItemSelectedInNavigator(KexiPart::Item* item) { Q_UNUSED(item); } KToolBar *KexiMainWindow::toolBar(const QString& name) const { return d->tabbedToolBar ? d->tabbedToolBar->toolBar(name) : 0; } void KexiMainWindow::appendWidgetToToolbar(const QString& name, QWidget* widget) { if (d->tabbedToolBar) d->tabbedToolBar->appendWidgetToToolbar(name, widget); } void KexiMainWindow::setWidgetVisibleInToolbar(QWidget* widget, bool visible) { if (d->tabbedToolBar) d->tabbedToolBar->setWidgetVisibleInToolbar(widget, visible); } void KexiMainWindow::addToolBarAction(const QString& toolBarName, QAction *action) { if (d->tabbedToolBar) d->tabbedToolBar->addAction(toolBarName, action); } void KexiMainWindow::updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet) { d->propEditor->updateInfoLabelForPropertySet(d->propertySet, textToDisplayForNullSet); } void KexiMainWindow::addSearchableModel(KexiSearchableModel *model) { if (d->tabbedToolBar) { d->tabbedToolBar->addSearchableModel(model); } } void KexiMainWindow::setReasonableDialogSize(QDialog *dialog) { dialog->setMinimumSize(600, 400); dialog->resize(size() * 0.8); } void KexiMainWindow::restoreDesignTabAndActivateIfNeeded(const QString &tabName) { if (!d->tabbedToolBar) { return; } d->tabbedToolBar->showTab(tabName); if (currentWindow() && currentWindow()->partItem() && currentWindow()->partItem()->identifier() != 0) // for unstored items id can be < 0 { const QString tabToActivate = d->tabsToActivateOnShow.value( currentWindow()->partItem()->identifier()); //qDebug() << "tabToActivate:" << tabToActivate << "tabName:" << tabName; if (tabToActivate == tabName) { d->tabbedToolBar->setCurrentTab(tabToActivate); } } } void KexiMainWindow::restoreDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode, int previousItemId) { //qDebug() << pluginId << viewMode << previousItemId; if (viewMode == Kexi::DesignViewMode) { switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: { hideDesignTab(previousItemId, "org.kexi-project.report"); restoreDesignTabAndActivateIfNeeded("form"); break; } case KexiPart::ReportObjectType: { hideDesignTab(previousItemId, "org.kexi-project.form"); restoreDesignTabAndActivateIfNeeded("report"); break; } default: hideDesignTab(previousItemId); } } else { hideDesignTab(previousItemId); } } void KexiMainWindow::activateDesignTab(const QString &pluginId) { if (!d->tabbedToolBar) { return; } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->setCurrentTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->setCurrentTab("report"); break; default:; } } void KexiMainWindow::activateDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode) { if (!d->tabbedToolBar) { return; } const QString tabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier()); //qDebug() << pluginId << viewMode << tabToActivate; if (viewMode == Kexi::DesignViewMode && tabToActivate.isEmpty()) { activateDesignTab(pluginId); } else { d->tabbedToolBar->setCurrentTab(tabToActivate); } } void KexiMainWindow::hideDesignTab(int itemId, const QString &pluginId) { if (!d->tabbedToolBar) { return; } //qDebug() << itemId << pluginId; if ( itemId > 0 && d->tabbedToolBar->currentWidget()) { const QString currentTab = d->tabbedToolBar->currentWidget()->objectName(); //qDebug() << "d->tabsToActivateOnShow.insert" << itemId << currentTab; d->tabsToActivateOnShow.insert(itemId, currentTab); } switch (d->prj->typeIdForPluginId(pluginId)) { case KexiPart::FormObjectType: d->tabbedToolBar->hideTab("form"); break; case KexiPart::ReportObjectType: d->tabbedToolBar->hideTab("report"); break; default: d->tabbedToolBar->hideTab("form"); d->tabbedToolBar->hideTab("report"); } } void KexiMainWindow::showDesignTabIfNeeded(int previousItemId) { if (d->insideCloseWindow && d->tabbedToolBar) return; if (currentWindow()) { restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), currentWindow()->currentViewMode(), previousItemId); } else { hideDesignTab(previousItemId); } } KexiUserFeedbackAgent* KexiMainWindow::userFeedbackAgent() const { return &d->userFeedback; } KexiMigrateManagerInterface* KexiMainWindow::migrateManager() { if (!d->migrateManager) { d->migrateManager = dynamic_cast( KexiInternalPart::createObjectInstance( "org.kexi-project.migration", "manager", this, this, nullptr)); } return d->migrateManager; } void KexiMainWindow::toggleFullScreen(bool isFullScreen) { static bool isTabbarRolledDown; if (d->tabbedToolBar) { if (isFullScreen) { isTabbarRolledDown = !d->tabbedToolBar->isRolledUp(); if (isTabbarRolledDown) { d->tabbedToolBar->toggleRollDown(); } } else { if (isTabbarRolledDown && d->tabbedToolBar->isRolledUp()) { d->tabbedToolBar->toggleRollDown(); } } } const Qt::WindowStates s = windowState() & Qt::WindowMaximized; if (isFullScreen) { setWindowState(windowState() | Qt::WindowFullScreen | s); } else { setWindowState((windowState() & ~Qt::WindowFullScreen)); showMaximized(); } } diff --git a/src/main/KexiMainWindow.h b/src/main/KexiMainWindow.h index b3eb82445..fc650a1a1 100644 --- a/src/main/KexiMainWindow.h +++ b/src/main/KexiMainWindow.h @@ -1,630 +1,633 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIMAINWINDOW_H #define KEXIMAINWINDOW_H //#define KEXI_IMPL_WARNINGS #include "keximain_export.h" #include #include #include +#include #include class QPaintEvent; class KDbObject; class KDbConnectionData; class KexiProjectData; class KexiMainWidget; namespace KexiPart { class Info; class Part; } #define KexiMainWindowSuper QWidget //KMainWindow /** * @short Kexi's main window implementation */ class KEXIMAIN_EXPORT KexiMainWindow : public QWidget /*KMainWindow*/, public KexiMainWindowIface, public KexiGUIMessageHandler { Q_OBJECT public: /*! Creates an empty mainwindow. */ explicit KexiMainWindow(QWidget *parent = 0); virtual ~KexiMainWindow(); virtual KActionCollection* actionCollection() const; //! @todo virtual QWidget* focusWidget() const; virtual QWidget* focusWidget() const { return KexiMainWindowSuper::focusWidget(); } - /*! Used by the main Kexi's routine. Creates a new Kexi main window and a new - QApplication object. + /*! Used by the main Kexi's routine. Creates a new Kexi main window. + For Kexi applications @a arguments are equal to CoreApplication::arguments() but test + applications alter the list. If @a componentName is provided, it is assigned to application's KAboutData::componentName. - It's not used by Kexi itself but this is useful for test application that are based + It's not used by Kexi itself but is useful for test application that are based on KexiMainWindow. - @return result 1 on error and 0 on success (the result can be used as a result of main()) - @note Yes, the data referred to by argc and argv must stay valid for the entire lifetime - of the QApplication object so int& is used. */ - static int create(int &argc, char *argv[], const QString &componentName = QString()); + @a extraOptions can be supplied to extend the list of supported options. + @note Extra options must not override Kexi's built-in options. + @return 0 on success (the result can be used as a result of main()) and other value on error */ + static int create(const QStringList &arguments, const QString &componentName = QString(), + const QList &extraOptions = QList()); //! Project data of currently opened project or NULL if no project here yet. virtual KexiProject *project(); /*! Registers window \a window for watching and adds it to the main window's stack. */ virtual void registerChild(KexiWindow *window); /*! Activates a window by it's document identifier. \return false if doc couldn't be raised or isn't opened. */ bool activateWindow(int id); /*! Like above, using \a window passed explicitly. Above method just calls this one. */ bool activateWindow(KexiWindow& window); /*! Performs startup actions. \return false if application should exit immediately with an error status. */ tristate startup(); /*! \return true if the application window is in the User Mode. */ virtual bool userMode() const; /*! \return true if opening of item \a item in \a viewMode mode is allowed. userMode() is taken into account as well as KexiPart::PartInfo::supportedUserViewModes() for \a item. */ bool openingAllowed(KexiPart::Item* item, Kexi::ViewMode viewMode, QString* errorMessage = 0); /*! Implemented for KexiMainWindow. */ virtual QList allActions() const; /*! \return currently active window or 0 if there is no active window. Implemented for KexiWindow. */ virtual KexiWindow* currentWindow() const; /*! @return window for tab @a tabIndex or 0 if there is no such tab. */ KexiWindow* windowForTab(int tabIndex) const; //! @todo temporary solution before the tabbed toolbar framework emerges // see KexiMainWindowIface virtual void appendWidgetToToolbar(const QString& name, QWidget* widget); //! @todo temporary solution before the tabbed toolbar framework emerges // see KexiMainWindowIface virtual void setWidgetVisibleInToolbar(QWidget* widget, bool visible); //! @todo replace with the final Actions API // see KexiMainWindowIface virtual void addToolBarAction(const QString& toolBarName, QAction *action); // see KexiMainWindowIface virtual KToolBar *toolBar(const QString& name) const; //! Shows design tab @a tabName again and activates it as current if it was hidden //! before for the same object. void restoreDesignTabAndActivateIfNeeded(const QString &tabName); //! Shows design tab again when switching between objects or views. void restoreDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode, int previousItemId); //! Sets currently visible design tab when switching to design view, according to object type opened. virtual void activateDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode); //! Hides design tabs when they are closed (depending on ID @a pluginId). //! If @a pluginId is empty, all tabs get hidden. virtual void hideDesignTab(int itemId, const QString &pluginId = QString()); /*! Implemented for KexiMainWindow */ virtual KexiUserFeedbackAgent* userFeedbackAgent() const; /*! Implemented for KexiMainWindow */ virtual KexiMigrateManagerInterface* migrateManager(); public Q_SLOTS: /*! Implemented for KexiMainWindow */ virtual tristate closeWindow(KexiWindow *window); /*! Closes the current window. */ tristate closeCurrentWindow(); /*! Closes window inside tab @a tabIndex. */ tristate closeWindowForTab(int tabIndex); /*! Internal implementation. If \a doNotSaveChanges is true, messages asking for saving the will be skipped and the changes will be dropped. This should not be usually used, maybe except for test suites (see kexi/tests/altertable/ directory). */ tristate closeWindow(KexiWindow *window, bool layoutTaskBar, bool doNotSaveChanges = false); /*! Activates next window. */ void activateNextWindow(); /*! Activates previous window. */ void activatePreviousWindow(); //! @todo move part of this to KexiProject, because currently KexiProject::openObject() allows multiple opens! /*! 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). \a openingCancelled is set to true if opening has been cancelled. \a errorMessage, if not 0, points to a string that can be set to error message if one encountered. @c nullptr can be returned if the KexiWindow object for @a item is not yet fully constructed but openObject() has been quickly called again for the same @a item. This can happen if user clicked multiple times on the same Project navigator's item. In this case @a openingCancelled is not set; the caller should not display error message but the opening should be silently abandoned. */ virtual KexiWindow* openObject(KexiPart::Item *item, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs = 0, QString* errorMessage = 0); //! For convenience virtual KexiWindow* openObject(const QString& pluginId, const QString& name, Kexi::ViewMode viewMode, bool *openingCancelled, QMap* staticObjectArgs = 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); /*! Implemented for KexiMainWindow */ virtual tristate saveObject(KexiWindow *window, const QString& messageWhenAskingForName = QString(), SaveObjectOptions options = 0); /*! Implemented for KexiMainWindowIface. */ virtual KexiWindow *openedWindowFor(int identifier); virtual KexiWindow *openedWindowFor(const KexiPart::Item *item); /*! Implemented for KexiMainWindowIface */ virtual QList currentParametersForQuery(int queryId) const; /*! Implemented for KexiMainWindowIface. */ virtual KDbQuerySchema *unsavedQuery(int queryId); /*! Implemented for KexiMainWindow */ virtual tristate getNewObjectInfo(KexiPart::Item *partItem, const QString &originalName, KexiPart::Part *part, bool allowOverwriting, bool *overwriteNeeded, const QString& messageWhenAskingForName = QString()); /*! Implemented for KexiMainWindow */ virtual void highlightObject(const QString& pluginId, const QString& name); /*! Opens project pointed by \a projectData. Application state (e.g. actions) is updated. \a projectData is copied into a project structures. \return true on success */ tristate openProject(const KexiProjectData& projectData); /*! Helper. Opens project pointed by \a aFileName. If \a aFileName is empty, a connection shortcut (.kexic file name) is obtained from global connection set using \a cdata (if present). In this case: * If connection shortcut has been found and \a dbName (a server database name) is provided 'kexi --skip-dialog --connection file.kexic dbName' is executed (or the project is opened directly if there's no project opened in the current Kexi main window. * If connection shortcut has been found and \a dbName is not provided, 'kexi --skip-dialog file.kexic' is executed (or the connection is opened directly if there's no porject opened in the current Kexi main window. */ tristate openProject(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName = QString(), const KexiProjectData::AutoOpenObjects& autoopenObjects = KexiProjectData::AutoOpenObjects()); /*! Helper. Opens project pointed by \a aFileName. Like above but \a fileNameForConnectionData can be passed instead of a pointer to connection data itself. \return false if \a fileNameForConnectionData is not empty but there is no such connection in Kexi::connset() for this filename. \a fileNameForConnectionData can be empty. */ tristate openProject(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName = QString()); /*! Helper. Opens project pointed by \a aFileName. */ tristate openProject(const QString& aFileName); /*! Opens project referenced by @a data. If @a shortcutPath is a empty .kexis filename and there is another project opened, a new instance of Kexi is started with the .kexis file as argument. Value pointed by @a opened is set to true if the database has been opened successfully. @return true on successful opening, cancelled if the operation was cancelled and false on failure.*/ tristate openProject(const KexiProjectData& data, const QString& shortcutPath, bool *opened); /*! Creates a new project usign template pointed by \a projectData. Application state (e.g. actions) is updated. New project data is copied into a project structures. \return true on success */ tristate createProjectFromTemplate(const KexiProjectData& projectData); /*! Closes current project, \return true on success. Application state (e.g. actions) is updated. \return true on success. If closing was cancelled by user, cancelled is returned. */ tristate closeProject(); //! Shows "print" dialog for \a item. //! \return true on success. virtual tristate printItem(KexiPart::Item* item); //! Shows "print preview" window. //! \return true on success. virtual tristate printPreviewForItem(KexiPart::Item* item); //! Shows "page setup" window for \a item. //! \return true on success and cancelled when the action was cancelled. virtual tristate showPageSetupForItem(KexiPart::Item* item); /*! 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); /*! Add searchable model to the main window. This extends search to a new area. One example is Project Navigator. @see KexiMainWindowIface */ virtual void addSearchableModel(KexiSearchableModel *model); //! Shows design tab when switching between objects or views. Depends on current window and view mode. void showDesignTabIfNeeded(int previousItemId); void toggleFullScreen(bool isFullScreen); /*! Implemented for KexiMainWindowIface. Sets reasonable dialog size based on main window size, that is 80% of its size. */ virtual void setReasonableDialogSize(QDialog *dialog); Q_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(); //! Emitted after opening a project, even after slotAutoOpenObjectsLater(). void projectOpened(); protected: /*! Setups main widget */ void setupMainWidget(); /*! Creates the Project Navigator (if it's not yet created), lookups items for current project and fills the nav. with not-opened items */ void setupProjectNavigator(); void setupContextHelp(); void setupPropertyEditor(); void setupMainMenuActionShortcut(QAction * action); /*! Creates standard actions like new, open, save ... */ void setupActions(); /*! Creates user project-wide actions */ void setupUserActions(); /*! Sets up the window from user settings. */ void restoreSettings(); /*! Writes user settings back. */ void storeSettings(); /*! Invalidates availability of all actions for current application state. */ void invalidateActions(); /*! Invalidates action availability for current application state. These actions are dependent on active window. */ virtual void invalidateSharedActions(QObject *o); /*! Invalidates action availability for current application state. These actions only depend on project availability, not on curently active window. */ void invalidateProjectWideActions(); /*! Shows dialog for creating new project, and creates one. The dialog is not shown if option for automatic creation - is checked or Kexi::startupHandler().projectData() was provided from command line. + is checked or KexiStartupHandler::global()->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 */ void createNewProject(); /*! Shows dialog for creating new blank project, and return a data describing it. If the dialog was cancelled, \a cancelled will be set to true (false otherwise). \a shortcutFileName, if not 0, will be set to a shortcut filename (in case when server database project was selected). */ KexiProjectData* createBlankProjectData(bool *cancelled, bool confirmOverwrites = true, QString *shortcutFileName = 0); /*! Reimplemented from KexiSharedActionHost: accepts only KexiDockBase and KexiWindow subclasses. */ virtual bool acceptsSharedActions(QObject *w); /*! Performs lookup like in KexiSharedActionHost::focusWindow() but starting from \a w instead of a widget returned by QWidget::focusWidget(). \return NULL if no widget matches acceptsSharedActions() or if \a w is NULL. */ virtual QWidget* findWindow(QWidget *w); /*! Updates application's caption - also shows project's name. */ void updateAppCaption(); virtual void closeEvent(QCloseEvent *ev); //! Called by KexiMainWidget::queryClose() bool queryClose(); /*! Implemented for KexiMainWindowIface. Switches \a window to view \a mode. Activates the window if it is not the current window. */ virtual tristate switchToViewMode(KexiWindow& window, Kexi::ViewMode viewMode); /*! Helper. Updates setup of property panel's tabs. Used when switching from \a prevWindow window to a current window. */ void updateCustomPropertyPanelTabs(KexiWindow *prevWindow, Kexi::ViewMode prevViewMode); /*! @overload void updateCustomPropertyPanelTabs(KexiWindow *prevWindow, Kexi::ViewMode prevViewMode) */ void updateCustomPropertyPanelTabs(KexiPart::Part *prevWindowPart, Kexi::ViewMode prevViewMode, KexiPart::Part *curWindowPart, Kexi::ViewMode curViewMode); /*! Used in openProject when running another Kexi process is required. */ tristate openProjectInExternalKexiInstance(const QString& aFileName, KDbConnectionData *cdata, const QString& dbName); /*! Used in openProject when running another Kexi process is required. */ tristate openProjectInExternalKexiInstance(const QString& aFileName, const QString& fileNameForConnectionData, const QString& dbName); /*! Updates info label of the property editor by reusing properties provided by the current property set. Read documentation of KexiPropertyEditorView class for information about accepted properties. If the current property is 0 and @a textToDisplayForNullSet string is not empty, this string is displayed (without icon or any other additional part). If the current property is 0 and @a textToDisplayForNullSet string is empty, the info label widget becomes hidden. Implemented for KexiMainWindow. @see KexiPropertyPaneViewBase::updateInfoLabelForPropertySet() */ virtual void updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet); //! Activates design tab when switching to design view, according to \a pluginId. void activateDesignTab(const QString &pluginId); protected Q_SLOTS: tristate createNewProject(const KexiProjectData &projectData); /*! Called once after timeout (after ctors are executed). */ void slotAutoOpenObjectsLater(); /*! Called if a window (tab) changes from \a prevWindow to \a window. Both parameters can be 0. */ void activeWindowChanged(KexiWindow *window, KexiWindow *prevWindow); void slotPartLoaded(KexiPart::Part* p); //! Internal - creates and initializes Kexi project object based on @a data. KexiProject* createKexiProjectObject(const KexiProjectData &data); /*! Handles event when user double clicked (or single -depending on settings) or pressed Return key on the part item in the navigator. This differs from openObject() signal in that if the object is already opened in view mode other than \a viewMode, the mode is not changed. \sa KexiProjectNavigator::openOrActivateItem() */ KexiWindow* openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled); //! For convenience KexiWindow* openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode); /*! Creates new object of type defined by \a info part info. \a openingCancelled is set to true if opening has been cancelled. \return true on success. */ virtual bool newObject(KexiPart::Info *info, bool *openingCancelled); //! For convenience bool newObject(KexiPart::Info *info) { bool openingCancelled; return newObject(info, &openingCancelled); } //! For convenience KexiWindow* openObject(KexiPart::Item *item, Kexi::ViewMode viewMode, QMap* staticObjectArgs = 0) { bool openingCancelled; return openObject(item, viewMode, &openingCancelled, staticObjectArgs); } /*! Removes object pointed by \a item from current project. Asks for confirmation. \return true on success or cancelled if removing was cancelled (only possible if \a dontAsk is false). */ tristate removeObject(KexiPart::Item *item, bool dontAsk = false); /*! Renames object pointed by \a item to a new name \a _newName. Sets \a success to false on failure. Used as a slot connected to KexiProjectNavigator::renameItem() signal. */ void renameObject(KexiPart::Item *item, const QString& _newName, bool *succes); /*! Changes caption of object pointed by \a item to \a _newCaption. Sets \a success to false on failure. Used as a slot connected to KexiProjectNavigator::changeItemCaption() signal. */ void setObjectCaption(KexiPart::Item *item, const QString& _newCaption, bool *succes); /*! Reaction for object rename (signalled by KexiProject). If this item has opened window, it's caption is updated, and also optionally application's caption. */ virtual void slotObjectRenamed(const KexiPart::Item &item, const QString& oldName); //! @todo virtual void fillWindowMenu(); void invalidateSharedActions(); void invalidateSharedActionsLater(); //! Updates the statusbar, navigator and "Insert->....." actions, dependent on read-only state. //! Only called on project opening and closing. void updateReadOnlyState(); void slotProjectWelcome(); void slotProjectNew(); void slotProjectOpen(); void slotProjectSave(); void slotProjectSaveAs(); void slotProjectPrint(); void slotProjectPrintPreview(); void slotProjectPageSetup(); void slotProjectProperties(); void slotProjectImportExportOrSend(); void slotProjectClose(); void slotProjectRelations(); void slotProjectImportDataTable(); void slotProjectExportDataTable(); void slotProjectQuit(); void slotEditPasteSpecialDataTable(); void slotEditCopySpecialDataTable(); void slotEditFind(); void slotEditFind(bool next); //!< helper void slotEditFindNext(); void slotEditFindPrevious(); void slotEditReplace(bool all); //!< helper void slotEditReplace(); void slotEditReplaceNext(); void slotEditReplaceAll(); void slotActivateNavigator(); void slotActivateMainArea(); void slotActivatePropertyEditor(); void slotShowNavigator(); void slotShowPropertyEditor(); void slotViewDataMode(); void slotViewDesignMode(); void slotViewTextMode(); //!< sometimes called "SQL View" void slotSettings(); void slotConfigureKeys(); void slotConfigureToolbars(); void slotToolsImportProject(); void slotToolsImportTables(); void slotToolsCompactDatabase(); void slotReportBug(); void slotTipOfTheDay(); void slotImportFile(); void slotImportServer(); //! There are performed all actions that need to be done immediately after ctro (using timer) void slotLastActions(); virtual void acceptPropertySetEditing(); virtual void propertySetSwitched(KexiWindow *window, bool force = false, bool preservePrevSelection = true, bool sortedProperties = false, const QByteArray& propertyToSelect = QByteArray()); /*! Handles changes in 'dirty' flag for windows. */ void slotDirtyFlagChanged(KexiWindow* window); /*! Shows Project Migration Wizard. \return true on successful migration, cancelled on cancellation, and false on failure. If \a mimeType and \a databaseName are not empty, the wizard will only ask about parameters of destination project and skip pages related to source project. */ tristate showProjectMigrationWizard(const QString& mimeType, const QString& databaseName); /*! @overload tristate showProjectMigrationWizard(const QString& mimeType, const QString& databaseName) @a cdata is used preselect a server-based connection. */ tristate showProjectMigrationWizard(const QString& mimeType, const QString& databaseName, const KDbConnectionData &cdata); //! Receives "selectionChanged()" signal from navigator to update some actions. void slotPartItemSelectedInNavigator(KexiPart::Item* item); /*! Receives the "executeItem" signal from navigator to perform "execute" action on \a item. \return true on success */ tristate executeItem(KexiPart::Item* item); //! Shows "export as data table" dialog for \a item. tristate exportItemAsDataTable(KexiPart::Item* item); //! Shows "copy special as data table" dialog for \a item. tristate copyItemToClipboardAsDataTable(KexiPart::Item* item); bool checkForDirtyFlagOnExport(KexiPart::Item *item, QMap *args); /*! Shows a question message * "Design of query %1 that you want to export data from is changed and has not yet been saved. * Do you want to use data from the changed query for exporting or from its original (saved) version?" \return true if the user picked the first option, * false if the user picked the second option and cancelled value if user cancelled the export. */ tristate askOnExportingChangedQuery(KexiPart::Item* item) const; //! Shows "print" dialog for \a item. //! \return true on success. bool printItem(KexiPart::Item* item, const QString& titleText); //! Shows "print" dialog for \a item and \a settings. //! \return true on success. //! @todo reenable when ported bool printItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText = QString()); /*! Shows "print preview" window for \a item. The preview windoe is cached, so \a reload == true is sometimes needed if data or print settings have changed in the meantime. \return true on success. */ bool printPreviewForItem(KexiPart::Item* item, const QString& titleText, bool reload); //! Shows "print preview" window. //! \return true on success. //! @todo reenable when ported bool printPreviewForItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings, const QString& titleText = QString(), bool reload = false); /*! Implemented for KexiMainWindow. Helper for printItem() and printPreviewForItem(). Also used by KexiFormEventAction. \return true on success and cancelled when the action was cancelled. */ //! @todo reenable when ported tristate printActionForItem(KexiPart::Item* item, PrintActionType action); void slotSetProjectNavigatorVisible(bool set); void slotSetPropertyEditorVisible(bool set); void slotProjectNavigatorVisibilityChanged(bool visible); void slotPropertyEditorVisibilityChanged(bool visible); void slotMultiTabBarTabClicked(int id); private: //! Adds action @a name with text @a text and optional shortcut @a shortcut. //! This is helper method containing workaround for Kexi //! until QAction::setShortcut() works again. //! @return created action QAction * addAction(const char *name, const QString &text, const char *shortcut = 0); //! Like @ref addAction(const char *, const QString&, const char *) but also adds //! icon @a icon. //! @return created action QAction * addAction(const char *name, const QIcon &icon, const QString& text, const char *shortcut = 0); class MessageHandler; class Private; Private * const d; friend class KexiWindow; friend class KexiMainWidget; }; #endif diff --git a/src/main/KexiMainWindow_p.cpp b/src/main/KexiMainWindow_p.cpp index 2a76d023b..1f4c88f64 100644 --- a/src/main/KexiMainWindow_p.cpp +++ b/src/main/KexiMainWindow_p.cpp @@ -1,1583 +1,1583 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiMainWindow_p.h" #include #include #include #include #include #include #include #include KexiWindowContainer::KexiWindowContainer(QWidget* parent) : QWidget(parent) , window(0) , lyr(new QVBoxLayout(this)) { lyr->setContentsMargins(0, 0, 0, 0); } KexiWindowContainer::~KexiWindowContainer() { //! @todo warning if saveSettings() failed? if (window) { window->saveSettings(); delete (KexiWindow*)window; } } void KexiWindowContainer::setWindow(KexiWindow* w) { window = w; if (w) lyr->addWidget(w); } // --- EmptyMenuContentWidget::EmptyMenuContentWidget(QWidget* parent) : QWidget(parent) { setAutoFillBackground(true); alterBackground(); } void EmptyMenuContentWidget::alterBackground() { QPalette pal(palette()); QColor bg(pal.color(QPalette::Window)); bg.setAlpha(200); pal.setColor(QPalette::Window, bg); setPalette(pal); } void EmptyMenuContentWidget::changeEvent(QEvent *e) { if (e->type() == QEvent::PaletteChange) { alterBackground(); } QWidget::changeEvent(e); } //! @todo KEXI3 is KexiMenuWidgetStyle needed? #if 0 KexiMenuWidgetStyle::KexiMenuWidgetStyle(QStyle *style, QObject *parent) : KexiUtils::StyleProxy(style, parent) { } KexiMenuWidgetStyle::~KexiMenuWidgetStyle() { } void KexiMenuWidgetStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const { if (element == QStyle::CE_MenuItem && (option->state & QStyle::State_Selected) && (option->state & QStyle::State_Enabled) && parentStyle()->objectName() == QLatin1String("oxygen")) { // Ugly fix for visual glitch of oxygen; no chance for improvement since // we've forked QMenu and oxygen checks for qobject_cast directly. QColor c(option->palette.color(QPalette::Window)); int h, s, v, a; c.getHsv(&h, &s, &v, &a); // Why 0.91208791? I knew you're curious. There are some algorithms in Oxygen // to make color a bit lighter. They are not in the public API nor they are simple. // So the number was computed by me to find the proper value for the color // (the accuracy is quite OK). // It's also related to the fact that Oxygen's menus have gradient background. // A lot of computation happens under the mask... c.setHsv(h, s, v * 0.91208791, a); painter->fillRect(option->rect.x() + 6, option->rect.y() + 6, option->rect.width() - 12, option->rect.height() - 12, c); } KexiUtils::StyleProxy::drawControl(element, option, painter, widget); } #endif KexiMainMenu::KexiMainMenu(KexiTabbedToolBar *toolBar, QWidget* parent) : QWidget(parent), m_toolBar(toolBar), m_initialized(false) { m_content = 0; m_selectFirstItem = false; } KexiMainMenu::~KexiMainMenu() { delete (QWidget*)m_contentWidget; } bool KexiMainMenu::eventFilter(QObject * watched, QEvent* event) { if (event->type() == QEvent::MouseButtonPress && watched == m_content && !m_contentWidget) { emit contentAreaPressed(); } else if (event->type() == QEvent::KeyPress) { QKeyEvent* ke = static_cast(event); if ((ke->key() == Qt::Key_Escape) && ke->modifiers() == Qt::NoModifier) { emit hideContentsRequested(); return true; } } return QWidget::eventFilter(watched, event); } void KexiMainMenu::setContent(QWidget *contentWidget) { if (m_menuWidget && m_persistentlySelectedAction) { m_menuWidget->setPersistentlySelectedAction( m_persistentlySelectedAction, m_persistentlySelectedAction->persistentlySelected()); } /*if (m_menuWidget->persistentlySelectedAction()) qDebug() << "****" << m_menuWidget->persistentlySelectedAction()->objectName();*/ KexiFadeWidgetEffect *fadeEffect = 0; if (m_contentWidget && contentWidget) { fadeEffect = new KexiFadeWidgetEffect(m_content); } if (m_contentWidget) m_contentWidget->deleteLater(); m_contentWidget = contentWidget; if (m_contentWidget) { QPalette contentWidgetPalette(m_contentWidget->palette()); contentWidgetPalette.setBrush(QPalette::Active, QPalette::Window, contentWidgetPalette.brush(QPalette::Active, QPalette::Base)); contentWidgetPalette.setBrush(QPalette::Inactive, QPalette::Window, contentWidgetPalette.brush(QPalette::Inactive, QPalette::Base)); contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::Window, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Base)); contentWidgetPalette.setBrush(QPalette::Active, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Active, QPalette::Text)); contentWidgetPalette.setBrush(QPalette::Inactive, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Inactive, QPalette::Text)); contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::WindowText, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Text)); const QColor highlightDisabled(KexiUtils::blendedColors( contentWidgetPalette.color(QPalette::Active, QPalette::Highlight), contentWidgetPalette.color(QPalette::Disabled, QPalette::Window), 1, 2)); contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::Highlight, highlightDisabled); const QColor highlightedTextDisabled(KexiUtils::blendedColors( contentWidgetPalette.color(QPalette::Active, QPalette::HighlightedText), contentWidgetPalette.color(QPalette::Disabled, QPalette::WindowText), 1, 2)); contentWidgetPalette.setBrush(QPalette::Disabled, QPalette::HighlightedText, highlightedTextDisabled); m_contentWidget->setPalette(contentWidgetPalette); for(QAbstractScrollArea *area : m_contentWidget->findChildren()) { QPalette pal(area->viewport()->palette()); pal.setBrush(QPalette::Disabled, QPalette::Base, contentWidgetPalette.brush(QPalette::Disabled, QPalette::Base)); area->viewport()->setPalette(pal); } m_contentWidget->setAutoFillBackground(true); m_contentWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); m_contentWidget->setContentsMargins(0, 0, 0, 0); m_contentLayout->addWidget(m_contentWidget); m_contentLayout->setCurrentWidget(m_contentWidget); m_contentWidget->setFocus(); m_contentWidget->installEventFilter(this); //connect(m_contentWidget, SIGNAL(destroyed()), this, SLOT(contentWidgetDestroyed())); } if (fadeEffect) { if (m_contentWidget) m_contentLayout->update(); QTimer::singleShot(10, fadeEffect, SLOT(start())); } } const QWidget *KexiMainMenu::contentWidget() const { return m_contentWidget; } void KexiMainMenu::setPersistentlySelectedAction(KexiMenuWidgetAction* action, bool set) { m_persistentlySelectedAction = action; m_persistentlySelectedAction->setPersistentlySelected(set); } /* void KexiMainMenu::setActiveAction(QAction* action = 0) { if (!action && !m_menuWidget->actions().isEmpty()) { action = actions().first(); } if (action) { m_menuWidget->setActiveAction(action); } } */ void KexiMainMenu::selectFirstItem() { m_selectFirstItem = true; } void KexiMainMenu::showEvent(QShowEvent * event) { if (!m_initialized) { m_initialized = true; KActionCollection *ac = KexiMainWindowIface::global()->actionCollection(); QHBoxLayout *hlyr = new QHBoxLayout(this); hlyr->setSpacing(0); hlyr->setMargin(0); m_menuWidget = new KexiMenuWidget; //! @todo KEXI3 is KexiMenuWidgetStyle needed? #if 0 QString styleName(m_menuWidget->style()->objectName()); if (KDE::version() < KDE_MAKE_VERSION(4, 8, 0) // a fix is apparently needed for glitch in KDE < 4.8 && styleName == "oxygen") { KexiMenuWidgetStyle *customStyle = new KexiMenuWidgetStyle(m_menuWidget->style()->objectName(), this); m_menuWidget->setStyle(customStyle); } #endif m_menuWidget->installEventFilter(this); m_menuWidget->setFocusPolicy(Qt::StrongFocus); setFocusProxy(m_menuWidget); m_menuWidget->setFrame(false); m_menuWidget->setAutoFillBackground(true); m_menuWidget->addAction(ac->action("project_welcome")); m_menuWidget->addAction(ac->action("project_open")); m_menuWidget->addAction(ac->action("project_close")); m_menuWidget->addSeparator(); m_menuWidget->addAction(ac->action("project_new")); m_menuWidget->addAction(ac->action("project_import_export_send")); #ifdef KEXI_SHOW_UNIMPLEMENTED m_menuWidget->addAction(ac->action("project_properties")); //! @todo project information m_menuWidget->addAction(ac->action("settings")); #endif m_menuWidget->addSeparator(); m_menuWidget->addAction(ac->action("quit")); hlyr->addWidget(m_menuWidget); m_content = new EmptyMenuContentWidget; m_content->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); m_content->installEventFilter(this); m_mainContentLayout = new QVBoxLayout; hlyr->addLayout(m_mainContentLayout); m_contentLayout = new QStackedLayout(m_content); m_contentLayout->setStackingMode(QStackedLayout::StackAll); m_contentLayout->setContentsMargins(0, 0, 0, 0); m_mainContentLayout->addWidget(m_content); hlyr->setStretchFactor(m_mainContentLayout, 1); } QWidget::showEvent(event); if (m_selectFirstItem && !m_menuWidget->actions().isEmpty()) { QAction* action = m_menuWidget->actions().first(); m_menuWidget->setActiveAction(action); m_selectFirstItem = false; } } // --- KexiTabbedToolBar::Private::Private(KexiTabbedToolBar *t) : q(t), createWidgetToolBar(0) #ifdef KEXI_AUTORISE_TABBED_TOOLBAR , tabToRaise(-1) #endif , rolledUp(false) { #ifdef KEXI_AUTORISE_TABBED_TOOLBAR tabRaiseTimer.setSingleShot(true); tabRaiseTimer.setInterval(300); #endif tabBarAnimation.setPropertyName("opacity"); tabBarAnimation.setDuration(500); connect(&tabBarAnimation, SIGNAL(finished()), q, SLOT(tabBarAnimationFinished())); tabIndex = 0; lowestIndex = 2; } //! @return true if @a style name is specific regarding tab styling static bool isSpecificTabStyle(const QString &styleName) { return styleName == "oxygen" || styleName == "qtcurve" || styleName == "gtk+"; } KexiTabbedToolBarStyle::KexiTabbedToolBarStyle(const QString &baseStyleName) : QProxyStyle(baseStyleName) { } KexiTabbedToolBarStyle::~KexiTabbedToolBarStyle() { } void KexiTabbedToolBarStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { const QString styleName(baseStyle()->objectName()); qreal origOpacity = -1.0; if (element == CE_TabBarTab) { const QStyleOptionTabV3* opt = qstyleoption_cast(option); const QTabBar* tabBar = qobject_cast(widget); KexiTabbedToolBar* tbar = tabBar ? qobject_cast(tabBar->parentWidget()) : 0; if (opt && tbar) { const int index = tabBar->tabAt(opt->rect.center()); if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX) return; bool mouseOver = opt->state & QStyle::State_MouseOver; bool unselectedOrMenuVisible = !(opt->state & State_Selected) || tbar->mainMenuVisible(); if (unselectedOrMenuVisible) { if (styleName == "bespin") { unselectedOrMenuVisible = false; } } QStyleOptionTabV3 newOpt(*opt); const bool specificStyle = isSpecificTabStyle(styleName); newOpt.text = (specificStyle ? " " : "") + tabBar->tabText(index) + (specificStyle ? " " : ""); if (!mouseOver && unselectedOrMenuVisible && index > 0) { if (tbar->mainMenuVisible()) newOpt.state &= ~QStyle::State_HasFocus; QProxyStyle::drawControl(CE_TabBarTabLabel, &newOpt, painter, widget); return; } else if (index == 0) { QBrush bg; newOpt.state |= State_Selected; if (tbar->mainMenuVisible()) { bg = newOpt.palette.brush(QPalette::Active, QPalette::Highlight); if (!specificStyle) { newOpt.palette.setBrush(QPalette::WindowText, newOpt.palette.brush(QPalette::Active, QPalette::HighlightedText)); newOpt.palette.setBrush(QPalette::ButtonText, newOpt.palette.brush(QPalette::Active, QPalette::HighlightedText)); } } else { bg = Qt::transparent; } QFont origFont(painter->font()); QFont f(origFont); f.setBold(true); painter->setFont(f); newOpt.palette.setBrush(QPalette::Window, bg); newOpt.palette.setBrush(QPalette::Button, // needed e.g. for Plastique style bg); QProxyStyle::drawControl(element, &newOpt, painter, widget); painter->setFont(origFont); if (!mouseOver || tbar->mainMenuVisible() || styleName == "gtk+") { return; } } if (index > 0 || mouseOver) { const QPalette::ColorGroup hbGroup = (styleName == "oxygen") ? QPalette::Active : QPalette::Inactive; const QBrush hb(newOpt.palette.brush(hbGroup, QPalette::Highlight)); newOpt.palette.setBrush(QPalette::Window, hb); newOpt.palette.setBrush(QPalette::Button, hb); // needed e.g. for Plastique style if (mouseOver && (index != tbar->currentIndex() || tbar->mainMenuVisible())) { // use lower opacity for diplaying hovered tabs origOpacity = painter->opacity(); painter->setOpacity(styleName == "qtcurve" ? 0.2 : 0.3); newOpt.state |= State_Selected; } else { if (!specificStyle) { newOpt.palette.setBrush(QPalette::WindowText, newOpt.palette.brush(QPalette::Inactive, QPalette::HighlightedText)); newOpt.palette.setBrush(QPalette::ButtonText, newOpt.palette.brush(QPalette::Inactive, QPalette::HighlightedText)); } } if (index == tbar->currentIndex() && styleName == "qtcurve") { origOpacity = painter->opacity(); painter->setOpacity(0.5); } (newOpt.state |= State_Active) ^= State_Active; QProxyStyle::drawControl(element, &newOpt, painter, widget); if (origOpacity != -1.0) { // restore opacity and draw labels using full this opacity painter->setOpacity(origOpacity); if (index > 0) { QProxyStyle::drawControl(CE_TabBarTabLabel, &newOpt, painter, widget); } } return; } } } else if (element == CE_ToolBar) { return; } QProxyStyle::drawControl(element, option, painter, widget); } void KexiTabbedToolBarStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { const QString styleName(baseStyle()->objectName()); if (element == PE_FrameTabWidget) { return; } if (element == PE_FrameTabBarBase) { const QTabBar* tabBar = qobject_cast(widget); KexiTabbedToolBar* tbar = tabBar ? qobject_cast(tabBar->parentWidget()) : 0; if (tbar && tbar->mainMenuVisible() && styleName != "bespin") { return; } } if (element == QStyle::PE_PanelToolBar || element == QStyle::PE_FrameMenu) { return; } QProxyStyle::drawPrimitive(element, option, painter, widget); } int KexiTabbedToolBarStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const { if (metric == QStyle::PM_SmallIconSize) return KIconLoader::SizeMedium; return QProxyStyle::pixelMetric(metric, option, widget); } // --- KexiTabbedToolBarTabBar::KexiTabbedToolBarTabBar(QWidget *parent) : QTabBar(parent) { setObjectName("tabbar"); customStyle = new KexiTabbedToolBarStyle(style()->objectName()); customStyle->setParent(this); setStyle(customStyle); installEventFilter(parent); QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget(); mainWindow->installEventFilter(parent); setAttribute(Qt::WA_Hover, true); } QSize KexiTabbedToolBarTabBar::originalTabSizeHint(int index) const { return QTabBar::tabSizeHint(index); } QSize KexiTabbedToolBarTabBar::tabSizeHint(int index) const { QSize s = QTabBar::tabSizeHint(index); QStyleOptionTab ot; ot.initFrom(this); QFont f(font()); f.setBold(true); ot.text = (isSpecificTabStyle(style()->objectName()) ? " " : "") + tabText(index); ot.fontMetrics = QFontMetrics(f); int w = customStyle->pixelMetric(QStyle::PM_TabBarTabHSpace, &ot, this); if (w <= 0) { // needed e.g. for oxygen w = fontMetrics().width(" "); } if (index == 0) { s.setWidth(QFontMetrics(f).width(ot.text) + w * 2); return s; } else if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX) { // fix width of the spacer tab s.setWidth(w); } return s; } void KexiTabbedToolBar::Private::toggleMainMenu() { if (q->mainMenuVisible()) hideMainMenu(); else showMainMenu(); } void KexiTabbedToolBar::Private::showMainMenu(const char* actionName) { QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget(); if (!mainMenu) { mainMenu = new KexiMainMenu(q, mainWindow); connect(mainMenu, SIGNAL(contentAreaPressed()), this, SLOT(hideMainMenu())); connect(mainMenu, SIGNAL(hideContentsRequested()), this, SLOT(hideContentsOrMainMenu())); } updateMainMenuGeometry(); if (actionName) { q->selectMainMenuItem(actionName); } else { mainMenu->selectFirstItem(); } mainMenu->show(); mainMenu->setFocus(); q->update(); // tab bar could have a line that should be repainted } void KexiTabbedToolBar::Private::updateMainMenuGeometry() { if (!mainMenu) return; QWidget *mainWindow = KexiMainWindowIface::global()->thisWidget(); KexiTabbedToolBarTabBar *tabBar = static_cast(q->tabBar()); QPoint pos = q->mapToGlobal(QPoint(0, tabBar->originalTabSizeHint(0).height() - 1)); // qDebug() << "1." << pos; pos = mainWindow->mapFromGlobal(pos); // qDebug() << "2." << pos; // qDebug() << "3." << q->pos(); QStyleOptionTab ot; ot.initFrom(tabBar); int overlap = tabBar->style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &ot, tabBar) - tabBar->style()->pixelMetric(QStyle::PM_TabBarBaseHeight, &ot, tabBar); // qDebug() << "4. overlap=" << overlap; mainMenu->setGeometry(0, pos.y() - overlap /*- q->y()*/, mainWindow->width(), mainWindow->height() - pos.y() + overlap /*+ q->y()*/); } void KexiTabbedToolBar::activateSearchLineEdit() { d->searchLineEdit->selectAll(); d->searchLineEdit->setFocus(); } void KexiTabbedToolBar::Private::hideMainMenu() { if (!mainMenu || !mainMenu->isVisible()) return; mainMenu->hide(); mainMenu->setContent(0); q->update(); // tab bar could have a line that should be repainted } void KexiTabbedToolBar::Private::hideContentsOrMainMenu() { if (!mainMenu || !mainMenu->isVisible()) return; if (mainMenu->contentWidget()) { mainMenu->setContent(0); } else { hideMainMenu(); } } KToolBar *KexiTabbedToolBar::Private::createToolBar(const char *name, const QString& caption) { KToolBar *tbar = new KToolBar(q, true /*main toolbar*/, false /*read config*/); tbar->setIconDimensions(22); //!< @todo make configurable or system-dependent? // needed e.g. for Windows style to remove the toolbar's frame tbar->setStyle(customTabBar->customStyle); toolbarsForName.insert(name, tbar); tbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); tbar->setObjectName(name); toolbarsCaptionForName.insert(name, caption); tabIndex = q->addTab(tbar, caption); toolbarsVisibleForIndex.append(true); toolbarsIndexForName.insert(name, tabIndex); return tbar; } KexiTabbedToolBar::KexiTabbedToolBar(QWidget *parent) : QTabWidget(parent) , d(new Private(this)) { d->customTabBar = new KexiTabbedToolBarTabBar(this); setTabBar(d->customTabBar); setStyle(d->customTabBar->customStyle); // from ktabwidget.cpp //! @todo QTabWidget::setTabBar() should be added with this code //! @todo KEXI3 Are these tabBar() connections useful to port? #if 0 connect(tabBar(), SIGNAL(contextMenu(int,QPoint)), SLOT(contextMenu(int,QPoint&))); connect(tabBar(), SIGNAL(tabDoubleClicked(int)), SLOT(mouseDoubleClick(int))); connect(tabBar(), SIGNAL(newTabRequest()), this, SIGNAL(mouseDoubleClick())); // #185487 connect(tabBar(), SIGNAL(mouseMiddleClick(int)), SLOT(mouseMiddleClick(int))); connect(tabBar(), SIGNAL(initiateDrag( int )), SLOT(initiateDrag( int ))); connect(tabBar(), SIGNAL(testCanDecode(QDragMoveEvent*,bool*)), SIGNAL(testCanDecode(QDragMoveEvent*,bool*))); connect(tabBar(), SIGNAL(receivedDropEvent(int,QDropEvent*)), SLOT(receivedDropEvent(int,QDropEvent*))); connect(tabBar(), SIGNAL(moveTab(int,int)), SLOT(moveTab(int,int))); connect(tabBar(), SIGNAL(tabCloseRequested(int)), SLOT(closeRequest(int))); #endif setMouseTracking(true); // for mouseMoveEvent() setWhatsThis(xi18n("Task-oriented toolbar. Groups commands using tabs.")); #ifdef KEXI_AUTORISE_TABBED_TOOLBAR connect(&d->tabRaiseTimer, SIGNAL(timeout()), this, SLOT(slotDelayedTabRaise())); #endif connect(tabBar(), SIGNAL(tabBarDoubleClicked(int)), this, SLOT(slotTabDoubleClicked(int))); d->ac = KexiMainWindowIface::global()->actionCollection(); QWidget *mainWin = KexiMainWindowIface::global()->thisWidget(); const bool userMode = KexiMainWindowIface::global()->userMode(); KToolBar *tbar; slotSettingsChanged(0);//KGlobalSettings::FontChanged //! @todo KEXI3 port from KGlobalSettings::Private::_k_slotNotifyChange: //! connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(slotSettingsChanged(int))); // help area QWidget *helpWidget = new QWidget(this); helpWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); QHBoxLayout *helpLyr = new QHBoxLayout(helpWidget); helpLyr->setContentsMargins(0, 0, 0, 0); // * HELP MENU // add help menu actions... (KexiTabbedToolBar depends on them) d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true/*showWhatsThis*/); QAction* help_report_bug_action = d->helpMenu->action(KHelpMenu::menuReportBug); d->ac->addAction(help_report_bug_action->objectName(), help_report_bug_action); QObject::disconnect(help_report_bug_action, 0, 0, 0); QObject::connect(help_report_bug_action, SIGNAL(triggered()), mainWin, SLOT(slotReportBug())); help_report_bug_action->setText(xi18nc("Report a bug or wish for Kexi application", "Report a &Bug or Wish...")); help_report_bug_action->setIcon(koIcon("tools-report-bug")); // good icon for toolbar help_report_bug_action->setWhatsThis(xi18n("Files a bug or wish for Kexi application.")); QAction* help_whats_this_action = d->helpMenu->action(KHelpMenu::menuWhatsThis); d->ac->addAction(help_whats_this_action->objectName(), help_whats_this_action); help_whats_this_action->setWhatsThis(xi18n("Activates a \"What's This?\" tool.")); QAction* help_contents_action = d->helpMenu->action(KHelpMenu::menuHelpContents); d->ac->addAction(help_contents_action->objectName(), help_contents_action); help_contents_action->setText(xi18n("Help")); help_contents_action->setWhatsThis(xi18n("Shows Kexi Handbook.")); QAction* help_about_app_action = d->helpMenu->action(KHelpMenu::menuAboutApp); d->ac->addAction(help_about_app_action->objectName(), help_about_app_action); help_about_app_action->setWhatsThis(xi18n("Shows information about Kexi application.")); QAction* help_about_kde_action = d->helpMenu->action(KHelpMenu::menuAboutKDE); d->ac->addAction(help_about_kde_action->objectName(), help_about_kde_action); help_about_kde_action->setWhatsThis(xi18n("Shows information about KDE.")); QAction* help_switch_language_action = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); if (help_switch_language_action) { d->ac->addAction(help_switch_language_action->objectName(), help_switch_language_action); } // extra action such as help_donate may be confusing for the user or conflicting with existing so hide it QAction *extraAction = d->helpMenu->action(static_cast(KHelpMenu::menuSwitchLanguage + 1)); if (extraAction) { extraAction->setVisible(false); } QAction *action_show_help_menu = d->ac->action("help_show_menu"); KexiSmallToolButton *btn = new KexiSmallToolButton(koIcon("help-about"), QString(), helpWidget); btn->setToolButtonStyle(Qt::ToolButtonIconOnly); btn->setPopupMode(QToolButton::InstantPopup); btn->setToolTip(action_show_help_menu->toolTip()); btn->setWhatsThis(action_show_help_menu->whatsThis()); btn->setFocusPolicy(Qt::NoFocus); QStyleOptionToolButton opt; opt.initFrom(btn); int w = btn->sizeHint().width(); int wAdd = btn->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, btn); if (w <= (2 * (wAdd + 1))) { w += wAdd + 2; } btn->setMinimumWidth(w); connect(action_show_help_menu, SIGNAL(triggered()), btn, SLOT(showMenu())); helpLyr->addWidget(btn); btn->setMenu(d->helpMenu->menu()); setCornerWidget(helpWidget, Qt::TopRightCorner); d->searchLineEdit = new KexiSearchLineEdit; kexiTester() << KexiTestObject(d->searchLineEdit, "globalSearch.lineEdit"); d->searchLineEdit->installEventFilter(this); helpLyr->addWidget(d->searchLineEdit); // needed e.g. for Windows style to remove the toolbar's frame QWidget *dummyWidgetForMainMenu = new QWidget(this); dummyWidgetForMainMenu->setObjectName("kexi"); addTab(dummyWidgetForMainMenu, KAboutData::applicationData().displayName()); d->toolbarsVisibleForIndex.append(true); addTab(new QWidget(this), QString()); // dummy for spacer d->toolbarsVisibleForIndex.append(true); if (!userMode) { d->createWidgetToolBar = d->createToolBar("create", xi18n("Create")); } tbar = d->createToolBar("data", xi18n("Data")); addAction(tbar, "edit_cut"); addAction(tbar, "edit_copy"); addAction(tbar, "edit_paste"); if (!userMode) addAction(tbar, "edit_paste_special_data_table"); //! @todo move undo/redo to quickbar: tbar = d->createToolBar("external", xi18n("External Data")); if (!userMode) { addAction(tbar, "project_import_data_table"); addAction(tbar, "tools_import_tables"); } tbar = d->createToolBar("tools", xi18n("Tools")); addAction(tbar, "tools_compact_database"); //! @todo move to form plugin tbar = d->createToolBar("form", xi18n("Form Design")); //! @todo move to report plugin tbar = d->createToolBar("report", xi18n("Report Design")); connect(this, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int))); setCurrentWidget(widget(KEXITABBEDTOOLBAR_SPACER_TAB_INDEX + 1)); // the default setFocusPolicy(Qt::NoFocus); } void KexiTabbedToolBar::Private::setCurrentTab(const QString& name) { q->setCurrentWidget(q->toolBar(name)); } void KexiTabbedToolBar::Private::hideTab(const QString& name) { q->removeTab(q->indexOf(toolbarsForName.value(name))); toolbarsVisibleForIndex[toolbarsIndexForName.value(name)] = false; } bool KexiTabbedToolBar::Private::isTabVisible(const QString& name) const { return q->indexOf(toolbarsForName.value(name)) != -1 && toolbarsVisibleForIndex[toolbarsIndexForName.value(name)]; } #ifndef NDEBUG void KexiTabbedToolBar::Private::debugToolbars() const { qDebug() << "QHash toolbarsForName:"; for (QHash::ConstIterator it(toolbarsForName.constBegin()); it!=toolbarsForName.constEnd(); ++it) { qDebug() << it.key() << "->" << it.value(); } qDebug() << "QHash toolbarsIndexForName:"; for (QHash::ConstIterator it(toolbarsIndexForName.constBegin()); it!=toolbarsIndexForName.constEnd(); ++it) { qDebug() << it.key() << "->" << it.value(); } qDebug() << "QHash toolbarsCaptionForName:"; for (QHash::ConstIterator it(toolbarsCaptionForName.constBegin()); it!=toolbarsCaptionForName.constEnd(); ++it) { qDebug() << it.key() << "->" << it.value(); } qDebug() << "QVector toolbarsVisibleForIndex:"; for (int i = 0; i < toolbarsVisibleForIndex.size(); i++) { qDebug() << i << "->" << toolbarsVisibleForIndex[i]; } } #endif void KexiTabbedToolBar::Private::showTab(const QString& name) { // qDebug() << "name:" << name; // qDebug() << "toolbarsForName.value(name):" << toolbarsForName.value(name); // qDebug() << "toolbarsIndexForName.value(name):" << toolbarsIndexForName.value(name); // qDebug() << "q->indexOf(toolbarsForName.value(name))" << q->indexOf(toolbarsForName.value(name)); #ifndef NDEBUG //debugToolbars(); #endif if (q->indexOf(toolbarsForName.value(name)) == -1) { int h = 0; // count h = invisible tabs before this for (int i = lowestIndex; i < toolbarsIndexForName.value(name); i++) { if (!toolbarsVisibleForIndex.at(i)) h++; } q->insertTab(toolbarsIndexForName.value(name) - h, toolbarsForName.value(name), toolbarsCaptionForName.value(name)); toolbarsVisibleForIndex[toolbarsIndexForName.value(name)] = true; } } // --- KexiTabbedToolBar::~KexiTabbedToolBar() { delete d; } bool KexiTabbedToolBar::mainMenuVisible() const { return d->mainMenu && d->mainMenu->isVisible(); } QRect KexiTabbedToolBar::tabRect(int index) const { return tabBar()->tabRect(index); } KHelpMenu* KexiTabbedToolBar::helpMenu() const { return d->helpMenu; } void KexiTabbedToolBar::slotSettingsChanged(int category) { Q_UNUSED(category); //! @todo if (category == KGlobalSettings::FontChanged) { //! @todo KEXI3 KGlobalSettings::menuFont() not available (using QFontDatabase::systemFont(QFontDatabase::GeneralFont) for now) //! https://community.kde.org/Frameworks/Porting_Notes#Global_Settings setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); // the toolbar acts like a menu //} } KToolBar* KexiTabbedToolBar::createWidgetToolBar() const { return d->createWidgetToolBar; } void KexiTabbedToolBar::mouseMoveEvent(QMouseEvent* event) { #ifdef KEXI_AUTORISE_TABBED_TOOLBAR QPoint p = event->pos(); int tab = tabBar()->tabAt(p); if (d->tabToRaise != -1 && (tab == -1 || tab == currentIndex())) { d->tabRaiseTimer.stop(); d->tabToRaise = -1; } else if (d->tabToRaise != tab) { d->tabRaiseTimer.start(); d->tabToRaise = tab; } #endif QTabWidget::mouseMoveEvent(event); } void KexiTabbedToolBar::leaveEvent(QEvent* event) { #ifdef KEXI_AUTORISE_TABBED_TOOLBAR d->tabRaiseTimer.stop(); d->tabToRaise = -1; #endif QTabWidget::leaveEvent(event); } bool KexiTabbedToolBar::eventFilter(QObject* watched, QEvent* event) { switch (event->type()) { case QEvent::MouseButtonPress: { QWidget *mainWin = KexiMainWindowIface::global()->thisWidget(); // qDebug() << "MouseButtonPress: watched:" << watched << "window()->focusWidget():" << window()->focusWidget(); if (watched == d->searchLineEdit) { activateSearchLineEdit(); // custom setFocus() for search box, so it's possible to focus // back on Escape key press return false; } else if (watched == tabBar()) { QMouseEvent* me = static_cast(event); QPoint p = me->pos(); KexiTabbedToolBarTabBar *tb = static_cast(tabBar()); int index = tb->tabAt(p); if (index == 0) { d->toggleMainMenu(); return true; } d->hideMainMenu(); if (index == KEXITABBEDTOOLBAR_SPACER_TAB_INDEX) { return true; } } else if (watched == mainWin && d->mainMenu) { QMouseEvent* me = static_cast(event); if (!QRect(d->mainMenu->mapToGlobal(QPoint(0,0)), d->mainMenu->size()) .contains(mainWin->mapToGlobal(me->pos()))) { // hide if clicked outside of the menu d->hideMainMenu(); } } } break; case QEvent::KeyPress: { QKeyEvent* ke = static_cast(event); // qDebug() << "**********" << QString::number(ke->key(), 16) // << QKeySequence::mnemonic(tabText(0))[0]; if (QKeySequence::mnemonic(tabText(0)) == QKeySequence(ke->key())) { // qDebug() << "eat the &File accel"; if (!d->mainMenu || !d->mainMenu->isVisible()) { d->showMainMenu(); } /*this could be unexpected: else if (d->mainMenu && d->mainMenu->isVisible()) { d->hideMainMenu(); }*/ return true; } if (d->mainMenu && d->mainMenu->isVisible() && (ke->key() == Qt::Key_Escape) && ke->modifiers() == Qt::NoModifier) { d->hideContentsOrMainMenu(); return true; } break; } case QEvent::Resize: if (watched == KexiMainWindowIface::global()->thisWidget()) { d->updateMainMenuGeometry(); } break; case QEvent::Shortcut: { QShortcutEvent *se = static_cast(event); if (watched == tabBar() && QKeySequence::mnemonic(tabText(0)) == se->key()) { // qDebug() << "eat the &File accel"; if (!d->mainMenu || !d->mainMenu->isVisible()) { d->showMainMenu(); return true; } } break; } default:; } return QTabWidget::eventFilter(watched, event); } void KexiTabbedToolBar::slotCurrentChanged(int index) { if (index == indexOf(d->createWidgetToolBar) && index != -1) { if (d->createWidgetToolBar->actions().isEmpty()) { QTimer::singleShot(10, this, SLOT(setupCreateWidgetToolbar())); } } if (d->rolledUp) { // switching the tab rolls down slotTabDoubleClicked(index); } if (index == 0) { // main menu d->showMainMenu(); } else { d->hideMainMenu(); } } void KexiTabbedToolBar::slotTabDoubleClicked(int index) { if (index <= 0) { return; // main item does not count here } d->rolledUp = !d->rolledUp; d->tabBarAnimation.stop(); QWidget *w = widget(index); if (!w) { return; } w->setGraphicsEffect(&d->tabBarOpacityEffect); if (d->rolledUp) { d->tabBarOpacityEffect.setOpacity(1.0); d->tabBarAnimation.setTargetObject(&d->tabBarOpacityEffect); d->tabBarAnimation.setStartValue(1.0); d->tabBarAnimation.setEndValue(0.0); d->tabBarAnimation.start(); } else { // roll down d->tabBarOpacityEffect.setOpacity(0.0); setMaximumHeight(QWIDGETSIZE_MAX); widget(d->rolledUpIndex)->show(); widget(d->rolledUpIndex)->setMaximumHeight(QWIDGETSIZE_MAX); w->setMaximumHeight(QWIDGETSIZE_MAX); w->show(); d->tabBarAnimation.setTargetObject(&d->tabBarOpacityEffect); d->tabBarAnimation.setStartValue(0.0); d->tabBarAnimation.setEndValue(1.0); d->tabBarAnimation.start(); } } void KexiTabbedToolBar::tabBarAnimationFinished() { if (d->rolledUp) { // hide and collapse the area widget(currentIndex())->hide(); KexiTabbedToolBarTabBar *tb = static_cast(tabBar()); setFixedHeight(tb->tabSizeHint(currentIndex()).height()); widget(currentIndex())->setFixedHeight(0); d->rolledUpIndex = currentIndex(); } } void KexiTabbedToolBar::setupCreateWidgetToolbar() { if (!d->createWidgetToolBar->actions().isEmpty()) return; //! @todo separate core object types from custom.... KexiPart::PartInfoList *plist = Kexi::partManager().infoList(); //this list is properly sorted if (plist) { foreach(KexiPart::Info *info, *plist) { QAction* a = info->newObjectAction(); if (a) { d->createWidgetToolBar->addAction(a); } else { //! @todo err } } } } void KexiTabbedToolBar::slotDelayedTabRaise() { #ifdef KEXI_AUTORISE_TABBED_TOOLBAR QPoint p = mapFromGlobal(QCursor::pos()); // make sure cursor is still over the tab int tab = tabBar()->tabAt(p); if (tab != d->tabToRaise) { d->tabToRaise = -1; } else if (d->tabToRaise != -1) { setCurrentIndex(d->tabToRaise); d->tabToRaise = -1; } #endif } KToolBar *KexiTabbedToolBar::toolBar(const QString& name) const { return d->toolbarsForName[name]; } void KexiTabbedToolBar::addAction(KToolBar *tbar, const char* actionName) { QAction *a = d->ac->action(actionName); if (a) tbar->addAction(a); } void KexiTabbedToolBar::addAction(const QString& toolBarName, QAction *action) { if (!action) return; KToolBar *tbar = d->toolbarsForName[toolBarName]; if (!tbar) return; tbar->addAction(action); } void KexiTabbedToolBar::addSeparatorAndAction(KToolBar *tbar, const char* actionName) { QAction *a = d->ac->action(actionName); if (a) { tbar->addSeparator(); tbar->addAction(a); } } void KexiTabbedToolBar::appendWidgetToToolbar(const QString& name, QWidget* widget) { KToolBar *tbar = d->toolbarsForName[name]; if (!tbar) { return; } QAction *action = tbar->addWidget(widget); d->extraActions.insert(widget, action); } void KexiTabbedToolBar::setWidgetVisibleInToolbar(QWidget* widget, bool visible) { QAction *action = d->extraActions[widget]; if (!action) { return; } action->setVisible(visible); } void KexiTabbedToolBar::showMainMenu(const char* actionName) { d->showMainMenu(actionName); } void KexiTabbedToolBar::hideMainMenu() { d->hideMainMenu(); } void KexiTabbedToolBar::toggleMainMenu() { d->toggleMainMenu(); } void KexiTabbedToolBar::setMainMenuContent(QWidget *w) { d->mainMenu->setContent(w); } void KexiTabbedToolBar::selectMainMenuItem(const char *actionName) { if (actionName) { KActionCollection *ac = KexiMainWindowIface::global()->actionCollection(); KexiMenuWidgetAction *a = qobject_cast(ac->action(actionName)); if (a) { d->mainMenu->setPersistentlySelectedAction(a, true); } } } void KexiTabbedToolBar::addSearchableModel(KexiSearchableModel *model) { d->searchLineEdit->addSearchableModel(model); } KToolBar* KexiTabbedToolBar::createToolBar(const char* name, const QString& caption) { return d->createToolBar(name, caption); } void KexiTabbedToolBar::setCurrentTab(const QString& name) { //qDebug() << name; d->setCurrentTab(name); } void KexiTabbedToolBar::setCurrentTab(int index) { setCurrentIndex(d->lowestIndex + index); } void KexiTabbedToolBar::hideTab(const QString& name) { //qDebug() << name; d->hideTab(name); } void KexiTabbedToolBar::showTab(const QString& name) { //qDebug() << name; d->showTab(name); } bool KexiTabbedToolBar::isTabVisible(const QString& name) const { return d->isTabVisible(name); } bool KexiTabbedToolBar::isRolledUp() { return d->rolledUp; } void KexiTabbedToolBar::toggleRollDown() { slotTabDoubleClicked(-1);//use -1 just to rolldown/up the tabbar } // --- KexiMainWidget::KexiMainWidget() : KMainWindow(0, Qt::Widget) , m_mainWindow(0) { setupCentralWidget(); } KexiMainWidget::~KexiMainWidget() { } void KexiMainWidget::setParent(KexiMainWindow* mainWindow) { KMainWindow::setParent(mainWindow); m_mainWindow = mainWindow; } KexiMainWindowTabWidget* KexiMainWidget::tabWidget() const { return m_tabWidget; } void KexiMainWidget::setupCentralWidget() { QWidget *centralWidget = new QWidget(this); QVBoxLayout *centralWidgetLyr = new QVBoxLayout(centralWidget); m_tabWidget = new KexiMainWindowTabWidget(centralWidget, this); connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabIndexChanged(int))); centralWidgetLyr->setContentsMargins(0, 0, 0, 0); centralWidgetLyr->setSpacing(0); centralWidgetLyr->addWidget(m_tabWidget); setCentralWidget(centralWidget); layout()->setContentsMargins(0, 0, 0, 0); layout()->setSpacing(0); } bool KexiMainWidget::queryClose() { return m_mainWindow ? m_mainWindow->queryClose() : true; } void KexiMainWidget::slotCurrentTabIndexChanged(int index) { KexiWindowContainer* cont = dynamic_cast(m_tabWidget->widget(index)); if (! cont || (KexiWindow*)m_previouslyActiveWindow == cont->window) return; if (m_mainWindow) m_mainWindow->activeWindowChanged(cont->window, (KexiWindow*)m_previouslyActiveWindow); m_previouslyActiveWindow = cont->window; emit currentTabIndexChanged(index); } //------------------------------------------ KexiMainWindow::Private::Private(KexiMainWindow* w) : wnd(w) { actionCollection = new KActionCollection(w); propEditor = 0; propEditorDockWidget = 0; navDockWidget = 0; propEditorTabWidget = 0; - KexiProjectData *pdata = Kexi::startupHandler().projectData(); - userMode = Kexi::startupHandler().forcedUserMode() /* <-- simply forced the user mode */ + KexiProjectData *pdata = KexiStartupHandler::global()->projectData(); + userMode = KexiStartupHandler::global()->forcedUserMode() /* <-- simply forced the user mode */ /* project has 'user mode' set as default and not 'design mode' override is found: */ - || (pdata && pdata->userMode() && !Kexi::startupHandler().forcedDesignMode()); - isProjectNavigatorVisible = Kexi::startupHandler().isProjectNavigatorVisible(); - isMainMenuVisible = Kexi::startupHandler().isMainMenuVisible(); + || (pdata && pdata->userMode() && !KexiStartupHandler::global()->forcedDesignMode()); + isProjectNavigatorVisible = KexiStartupHandler::global()->isProjectNavigatorVisible(); + isMainMenuVisible = KexiStartupHandler::global()->isMainMenuVisible(); navigator = 0; prj = 0; config = KSharedConfig::openConfig(); nameDialog = 0; m_findDialog = 0; focus_before_popup = 0; action_show_nav = 0; action_show_propeditor = 0; action_activate_nav = 0; action_activate_propeditor = 0; action_welcome_projects_title_id = -1; action_welcome_connections_title_id = -1; forceWindowClosing = false; insideCloseWindow = false; #ifndef KEXI_NO_PENDING_DIALOGS actionToExecuteWhenPendingJobsAreFinished = NoAction; #endif propEditorDockSeparatorPos = -1; navDockSeparatorPos = -1; wasAutoOpen = false; windowExistedBeforeCloseProject = false; #ifndef KEXI_SHOW_UNIMPLEMENTED dummy_action = new KActionMenu(QString(), wnd); #endif forceShowProjectNavigatorOnCreation = false; forceHideProjectNavigatorOnCreation = false; navWasVisibleBeforeProjectClosing = false; saveSettingsForShowProjectNavigator = true; propertyEditorCollapsed = false; enable_slotPropertyEditorVisibilityChanged = true; migrateManager = 0; } KexiMainWindow::Private::~Private() { qDeleteAll(m_openedCustomObjectsForItem); } void KexiMainWindow::Private::insertWindow(KexiWindow *window) { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); windows.insert(window->id(), window); #ifndef KEXI_NO_PENDING_DIALOGS pendingWindows.remove(window->id()); #endif } bool KexiMainWindow::Private::windowContainerExistsFor(int identifier) const { return windowContainers.contains(identifier); } void KexiMainWindow::Private::setWindowContainerExistsFor(int identifier, bool set) { if (set) { windowContainers.insert(identifier); } else { windowContainers.remove(identifier); } } void KexiMainWindow::Private::updateWindowId(KexiWindow *window, int oldItemID) { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); windows.remove(oldItemID); #ifndef KEXI_NO_PENDING_DIALOGS pendingWindows.remove(oldItemID); #endif windows.insert(window->id(), window); } void KexiMainWindow::Private::removeWindow(int identifier) { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); windows.remove(identifier); } int KexiMainWindow::Private::openedWindowsCount() { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); return windows.count(); } //! Used in KexiMainWindowe::closeProject() void KexiMainWindow::Private::clearWindows() { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); windows.clear(); #ifndef KEXI_NO_PENDING_DIALOGS pendingWindows.clear(); #endif } void KexiMainWindow::Private::showStartProcessMsg(const QStringList& args) { wnd->showErrorMessage(xi18nc("@info", "Could not start %1 application.", QString::fromLatin1(KEXI_APP_NAME)), xi18nc("@info", "Command %1 failed.", args.join(" "))); } void KexiMainWindow::Private::updatePropEditorVisibility(Kexi::ViewMode viewMode, KexiPart::Info *info) { if (!propEditorDockWidget) return; KexiWindow *currentWindow = wnd->currentWindow(); if (!info && currentWindow) { info = currentWindow->part()->info(); } const bool visible = (viewMode == Kexi::DesignViewMode) && ((currentWindow && currentWindow->propertySet()) || (info && info->isPropertyEditorAlwaysVisibleInDesignMode())); //qDebug() << "visible == " << visible; enable_slotPropertyEditorVisibilityChanged = false; if (visible && propertyEditorCollapsed) { // used when we're switching back to a window with propeditor available but collapsed propEditorDockWidget->setVisible(!visible); setPropertyEditorTabBarVisible(true); } else { propEditorDockWidget->setVisible(visible); setPropertyEditorTabBarVisible(false); } enable_slotPropertyEditorVisibilityChanged = true; } void KexiMainWindow::Private::setTabBarVisible(KMultiTabBar::KMultiTabBarPosition position, int id, KexiDockWidget *dockWidget, bool visible) { KMultiTabBar *mtbar = multiTabBars.value(position); if (!mtbar) { return; } if (!visible) { mtbar->removeTab(id); } else if (!mtbar->tab(id)) { mtbar->appendTab(koIcon("document-properties"), id, dockWidget->tabText); KMultiTabBarTab *tab = mtbar->tab(id); QObject::connect(tab, SIGNAL(clicked(int)), wnd, SLOT(slotMultiTabBarTabClicked(int)), Qt::UniqueConnection); } } void KexiMainWindow::Private::setPropertyEditorTabBarVisible(bool visible) { setTabBarVisible(KMultiTabBar::Right, PROPERTY_EDITOR_TABBAR_ID, propEditorDockWidget, visible); } QObject *KexiMainWindow::Private::openedCustomObjectsForItem(KexiPart::Item* item, const char* name) { if (!item || !name) { qWarning() << "!item || !name"; return 0; } QByteArray key(QByteArray::number(item->identifier()) + name); return m_openedCustomObjectsForItem.value(key); } void KexiMainWindow::Private::addOpenedCustomObjectForItem(KexiPart::Item* item, QObject* object, const char* name) { QByteArray key(QByteArray::number(item->identifier()) + name); m_openedCustomObjectsForItem.insert(key, object); } KexiFindDialog *KexiMainWindow::Private::findDialog() { if (!m_findDialog) { m_findDialog = new KexiFindDialog(wnd); m_findDialog->setActions(action_edit_findnext, action_edit_findprev, action_edit_replace, action_edit_replace_all); } return m_findDialog; } void KexiMainWindow::Private::updateFindDialogContents(bool createIfDoesNotExist) { if (!wnd->currentWindow()) return; if (!createIfDoesNotExist && (!m_findDialog || !m_findDialog->isVisible())) return; KexiSearchAndReplaceViewInterface* iface = currentViewSupportingSearchAndReplaceInterface(); if (!iface) { if (m_findDialog) { m_findDialog->setButtonsEnabled(false); m_findDialog->setLookInColumnList(QStringList(), QStringList()); } return; } //! @todo use ->caption() here, depending on global settings related to displaying captions findDialog()->setObjectNameForCaption(wnd->currentWindow()->partItem()->name()); QStringList columnNames; QStringList columnCaptions; QString currentColumnName; // for 'look in' if (!iface->setupFindAndReplace(columnNames, columnCaptions, currentColumnName)) { m_findDialog->setButtonsEnabled(false); m_findDialog->setLookInColumnList(QStringList(), QStringList()); return; } m_findDialog->setButtonsEnabled(true); const QString prevColumnName(m_findDialog->currentLookInColumnName()); m_findDialog->setLookInColumnList(columnNames, columnCaptions); m_findDialog->setCurrentLookInColumnName(prevColumnName); } KexiView *KexiMainWindow::Private::currentViewSupportingAction(const char* actionName) const { if (!wnd->currentWindow()) return 0; KexiView *view = wnd->currentWindow()->selectedView(); if (!view) return 0; QAction *action = view->sharedAction(actionName); if (!action || !action->isEnabled()) return 0; return view; } KexiSearchAndReplaceViewInterface* KexiMainWindow::Private::currentViewSupportingSearchAndReplaceInterface() const { if (!wnd->currentWindow()) return 0; KexiView *view = wnd->currentWindow()->selectedView(); if (!view) return 0; return dynamic_cast(view); } tristate KexiMainWindow::Private::showProjectMigrationWizard( const QString& mimeType, const QString& databaseName, const KDbConnectionData *cdata) { //pass arguments QMap args; args.insert("mimeType", mimeType); args.insert("databaseName", databaseName); if (cdata) { //pass KDbConnectionData serialized as a string... QString str; KDbUtils::serializeMap(cdata->toMap(), &str); args.insert("connectionData", str); } QDialog *dlg = KexiInternalPart::createModalDialogInstance("org.kexi-project.migration", "migration", wnd, 0, &args); if (!dlg) return false; //error msg has been shown by KexiInternalPart const int result = dlg->exec(); delete dlg; if (result != QDialog::Accepted) return cancelled; //open imported project in a new Kexi instance QString destinationDatabaseName(args["destinationDatabaseName"]); QString fileName, destinationConnectionShortcut; if (!destinationDatabaseName.isEmpty()) { if (args.contains("destinationConnectionShortcut")) { // server-based destinationConnectionShortcut = args["destinationConnectionShortcut"]; } else { // file-based fileName = destinationDatabaseName; destinationDatabaseName.clear(); } tristate res = wnd->openProject(fileName, destinationConnectionShortcut, destinationDatabaseName); wnd->raise(); return res; } return true; } #ifndef KEXI_NO_PENDING_DIALOGS void KexiMainWindow::Private::executeActionWhenPendingJobsAreFinished() { ActionToExecuteWhenPendingJobsAreFinished a = actionToExecuteWhenPendingJobsAreFinished; actionToExecuteWhenPendingJobsAreFinished = NoAction; switch (a) { case QuitAction: qApp->quit(); break; case CloseProjectAction: wnd->closeProject(); break; default:; } } KexiWindow *KexiMainWindow::Private::openedWindowFor(const KexiPart::Item* item, PendingJobType &pendingType) { return openedWindowFor(item->identifier(), pendingType); } KexiWindow *KexiMainWindow::Private::openedWindowFor(int identifier, PendingJobType &pendingType) { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); QHash::ConstIterator it = pendingWindows.find(identifier); if (it == pendingWindows.end()) pendingType = NoJob; else pendingType = it.value(); if (pendingType == WindowOpeningJob) { return 0; } return windows.contains(identifier) ? (KexiWindow*)windows.value(identifier) : 0; } void KexiMainWindow::Private::addItemToPendingWindows(const KexiPart::Item* item, PendingJobType jobType) { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); pendingWindows.insert(item->identifier(), jobType); } bool KexiMainWindow::Private::pendingWindowsExist() { if (pendingWindows.begin() != pendingWindows.end()) qDebug() << pendingWindows.constBegin().key() << " " << (int)pendingWindows.constBegin().value(); //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); return !pendingWindows.isEmpty(); } void KexiMainWindow::Private::removePendingWindow(int identifier) { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); pendingWindows.remove(identifier); } #else // KEXI_NO_PENDING_DIALOGS KexiWindow *KexiMainWindow::Private::openedWindowFor(int identifier) { //! @todo (threads) QMutexLocker dialogsLocker( &dialogsMutex ); return windows.contains(identifier) ? (KexiWindow*)windows.value(identifier) : 0; } #endif diff --git a/src/main/autotests/CMakeLists.txt b/src/main/autotests/CMakeLists.txt index 8680d98b4..30896582a 100644 --- a/src/main/autotests/CMakeLists.txt +++ b/src/main/autotests/CMakeLists.txt @@ -1,2 +1,11 @@ -set(LIBS keximain kexicore kexiextendedwidgets) -kexi_add_test(kexi GlobalSearchTest ${LIBS}) +#set(LIBS keximain kexicore kexiextendedwidgets) +#kexi_add_test(kexi GlobalSearchTest ${LIBS}) +ecm_add_tests( + GlobalSearchTest.cpp + LINK_LIBRARIES + Qt5::Test + keximain + kexiextendedwidgets +) +set_property(TARGET GlobalSearchTest PROPERTY COMPILE_FLAGS + ${_COMPILE_FLAGS}\ -DCURRENT_SOURCE_DIR="\\"${CMAKE_CURRENT_SOURCE_DIR}/\\""\ -DFILES_OUTPUT_DIR="\\"${CMAKE_CURRENT_BINARY_DIR}/\\"") diff --git a/src/main/autotests/GlobalSearchTest.cpp b/src/main/autotests/GlobalSearchTest.cpp index 2c002e19e..1e5ac0160 100644 --- a/src/main/autotests/GlobalSearchTest.cpp +++ b/src/main/autotests/GlobalSearchTest.cpp @@ -1,216 +1,129 @@ /* This file is part of the KDE project - Copyright (C) 2012-2016 Jarosław Staniek + Copyright (C) 2012-2017 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ -#include "GlobalSearchTest.h" - -#include #include #include #include
    #include #include +#include +#include #include #include -#include -#include -#include #include #include #include #include const int GUI_DELAY = 10; #define FILES_DATA_DIR CURRENT_SOURCE_DIR "data" -void GlobalSearchTest::initTestCase() -{ -} - -//! Copies 0-th arg and adds second empty -class NewArgs +class GlobalSearchTest : public KexiTestHandler { + Q_OBJECT public: - NewArgs(char *argv[]) { - count = 2; - vals = new char*[count]; - vals[0] = qstrdup(argv[0]); - vals[count - 1] = 0; - } - ~NewArgs() { - for (int i = 0; i < count; i++) { - delete [] vals[i]; - } - delete [] vals; - } - - int count; - char **vals; -}; - -//! A helper that creates Kexi main window, and manages its lifetime. -//! It is needed because lifetime of QApplication, main window and command line arguments -//! should be synchronized. This class is designed for using on a stack of test blocks. -class KexiMainWindowCreator -{ -public: - //! A helper that creates Kexi main window, passing @a filename to it. - //! @a testObject (required) is used only to obtain test object name for debugging. - KexiMainWindowCreator(char *argv[], const QString& filename, QObject *testObject) - : args(argv) - { - Q_ASSERT(testObject); - args.vals[args.count - 1] = qstrdup(QFile::encodeName(filename).constData()); - result = KexiMainWindow::create(args.count, args.vals, - testObject->metaObject()->className()); - } - - //! Executes action @a name. @return true if action was found. - bool executeAction(const QString &name) { - KActionCollection *actionCollection = KexiMainWindowIface::global()->actionCollection(); - QAction *a = actionCollection->action(name); - if (a) { - a->trigger(); - } - return a; - } - - ~KexiMainWindowCreator() { - delete KexiMainWindowIface::global(); - delete qApp; - } - int result; + GlobalSearchTest(); +private Q_SLOTS: + void testGlobalSearch(); private: - NewArgs args; + QCommandLineOption m_loopOption; }; -GlobalSearchTest::GlobalSearchTest(int &argc, char **argv, bool goToEventLoop) - : m_argc(argc), m_argv(argv), m_goToEventLoop(goToEventLoop) +GlobalSearchTest::GlobalSearchTest() + : m_loopOption("loop", "Do not exit after successful test (stay in event loop)") { + addExtraOption(m_loopOption); } void GlobalSearchTest::testGlobalSearch() { QString filename(QFile::decodeName(FILES_DATA_DIR "/GlobalSearchTest.kexi")); - qDebug() << filename; - KexiMainWindowCreator windowCreator(m_argv, filename, this); - QVERIFY(qApp); - QCOMPARE(windowCreator.result, 0); + QStringList args(QApplication::arguments()); + args.append(filename); + const int result = KexiMainWindow::create(args, metaObject()->className(), extraOptions()); + const KexiStartupData *h = KexiStartupData::global(); + QCOMPARE(result, 0); QLineEdit *lineEdit = kexiTester().widget("globalSearch.lineEdit"); QVERIFY(lineEdit); QTreeView *treeView = kexiTester().widget("globalSearch.treeView"); QVERIFY(treeView); lineEdit->setFocus(); // enter "cars", expect 4 completion items QTest::keyClicks(lineEdit, "cars"); QVERIFY(treeView->isVisible()); int globalSearchCompletionListRows = treeView->model()->rowCount(); QCOMPARE(globalSearchCompletionListRows, 4); // add "x", expect no completion items and hidden list QTest::keyClicks(lineEdit, "x"); QVERIFY(!treeView->isVisible()); globalSearchCompletionListRows = treeView->model()->rowCount(); QCOMPARE(globalSearchCompletionListRows, 0); // Escape should clear QTest::keyClick(lineEdit, Qt::Key_Escape, Qt::NoModifier, GUI_DELAY); QVERIFY(lineEdit->text().isEmpty()); - QTest::keyClicks(lineEdit, "cars"); + QTest::keyClicks(lineEdit, "cars", Qt::NoModifier, GUI_DELAY); QVERIFY(treeView->isVisible()); treeView->setFocus(); // no highlight initially KexiProjectNavigator *projectNavigator = kexiTester().widget("KexiProjectNavigator"); QVERIFY(projectNavigator); QVERIFY(!projectNavigator->partItemWithSearchHighlight()); QTest::keyPress(treeView, Qt::Key_Down, Qt::NoModifier, GUI_DELAY); // selecting 1st row should highlight "cars" table KexiPart::Item* highlightedPartItem = projectNavigator->partItemWithSearchHighlight(); QVERIFY(highlightedPartItem); QCOMPARE(highlightedPartItem->name(), QLatin1String("cars")); QCOMPARE(highlightedPartItem->pluginId(), QLatin1String("org.kexi-project.table")); QTest::keyPress(treeView, Qt::Key_Down, Qt::NoModifier, GUI_DELAY); QTest::keyPress(treeView, Qt::Key_Down, Qt::NoModifier, GUI_DELAY); // 3rd row should be "cars" form QModelIndexList selectedIndices = treeView->selectionModel()->selectedRows(); QCOMPARE(selectedIndices.count(), 1); QCOMPARE(treeView->model()->data(selectedIndices.first(), Qt::DisplayRole).toString(), QLatin1String("cars")); // check if proper entry of Project Navigator is selected QTest::keyPress(treeView, Qt::Key_Enter, Qt::NoModifier, GUI_DELAY); KexiPart::Item* selectedPartItem = projectNavigator->selectedPartItem(); QVERIFY(selectedPartItem); QCOMPARE(selectedPartItem->name(), QLatin1String("cars")); QCOMPARE(selectedPartItem->pluginId(), QLatin1String("org.kexi-project.form")); - if (m_goToEventLoop) { + if (h->isSet(m_loopOption)) { const int result = qApp->exec(); QCOMPARE(result, 0); } } -void GlobalSearchTest::cleanupTestCase() -{ -} +QTEST_MAIN(GlobalSearchTest) -int main(int argc, char *argv[]) -{ - // Pull off custom options - bool goToEventLoop = false; - int realCount = 0; - char **realVals = new char*[argc]; - for (int i = 0; i < argc; ++i) { - realVals[i] = 0; - } - for (int i = 0; i < argc; ++i) { - if (0 == qstrcmp(argv[i], "-loop")) { - goToEventLoop = true; - continue; - } - else { - if (0 == qstrcmp(argv[i], "-help") || 0 == qstrcmp(argv[i], "--help")) { - printf(" Options coming from the Kexi test suite:\n -loop : Go to event loop after successful test\n\n"); - } - realVals[realCount] = qstrdup(argv[i]); - ++realCount; - } - } - - // Actual test - GlobalSearchTest tc(realCount, realVals, goToEventLoop); - int result = QTest::qExec(&tc, realCount, realVals); - - // Clean up - for (int i = 0; i < argc; i++) { - delete [] realVals[i]; - } - delete [] realVals; - return result; -} +#include "GlobalSearchTest.moc" diff --git a/src/main/startup/KexiStartup.cpp b/src/main/startup/KexiStartup.cpp index 60231692b..b150035ce 100644 --- a/src/main/startup/KexiStartup.cpp +++ b/src/main/startup/KexiStartup.cpp @@ -1,1047 +1,1039 @@ /* This file is part of the KDE project - Copyright (C) 2003-2015 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiStartup.h" #include "kexi.h" #include "kexiproject.h" #include "kexiprojectdata.h" #include "kexiprojectset.h" #include "kexiguimsghandler.h" #include "KexiStartupDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -namespace Kexi +static void destroyStartupHandler() { - -// Don't use Q_GLOBAL_STATIC as destroys the object *after* QApplication is gone but we have to cleanup before -> use qAddPostRoutine -static KexiStartupHandler* _startupHandler = 0; - -static void _destroyStartupHandler() -{ - delete _startupHandler; - _startupHandler = 0; -} - -KexiStartupHandler& startupHandler() -{ - if (!_startupHandler) { - _startupHandler = new KexiStartupHandler; - qAddPostRoutine(_destroyStartupHandler); + if (!KexiStartupData::global()) { + return; } - return *_startupHandler; -} + KexiStartupHandler* startupHandler = static_cast(KexiStartupHandler::global()); + delete startupHandler; // resets KexiStartup::global() } class KexiStartupData; //--------------------------------- //! @internal class Q_DECL_HIDDEN KexiStartupHandler::Private { public: Private() : passwordDialog(0)//, showConnectionDetailsExecuted(false) , connShortcutFile(0), connDialog(0), startupDialog(0) { } ~Private() { destroyGui(); } void destroyGui() { delete passwordDialog; passwordDialog = 0; delete connDialog; connDialog = 0; delete startupDialog; startupDialog = 0; } KexiDBPasswordDialog* passwordDialog; QString shortcutFileName; KexiDBConnShortcutFile *connShortcutFile; KexiDBConnectionDialog *connDialog; QString shortcutFileGroupKey; KexiStartupDialog *startupDialog; }; //--------------------------------- void updateProgressBar(QProgressDialog *pd, char *buffer, int buflen) { char *p = buffer; QByteArray line; line.reserve(80); for (int i = 0; i < buflen; i++, p++) { if ((i == 0 || buffer[i-1] == '\n') && buffer[i] == '%') { bool ok; int j = 0; ++i; line.clear(); for (;i= '0' && *p <= '9'; j++, i++, p++) line += *p; --i; --p; const int percent = line.toInt(&ok); if (ok && percent >= 0 && percent <= 100 && pd->value() < percent) { // qDebug() << percent; pd->setValue(percent); qApp->processEvents(QEventLoop::AllEvents, 100); } } } } //--------------------------------- KexiStartupHandler::KexiStartupHandler() : QObject(0) , KexiStartupData() , d(new Private()) { connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(slotAboutToAppQuit())); } KexiStartupHandler::~KexiStartupHandler() { - qAddPostRoutine(Kexi::_destroyStartupHandler); // post routine is installed! + qRemovePostRoutine(destroyStartupHandler); // post routine is installed! delete d; } +KexiStartupHandler* KexiStartupHandler::global() +{ + if (!KexiStartupData::global()) { + (void)new KexiStartupHandler; // sets KexiStartup::global() + qAddPostRoutine(destroyStartupHandler); + } + return static_cast(KexiStartupData::global()); +} + void KexiStartupHandler::slotAboutToAppQuit() { d->destroyGui(); } //! @todo KEXI3 port getAutoopenObjects() #if 0 static bool stripQuotes(const QString &item, QString &name) { if (item.left(1) == "\"" && item.right(1) == "\"") { name = item.mid(1, item.length() - 2); return true; } name = item; return false; } bool KexiStartupHandler::getAutoopenObjects(KCmdLineArgs *args, const QByteArray &action_name) { QStringList list = args->getOptionList(action_name); bool atLeastOneFound = false; foreach(const QString &option, list) { QString type_name, obj_name, item = option; int idx; bool name_required = true; if (action_name == "new") { obj_name.clear(); stripQuotes(item, type_name); name_required = false; } else {//open, design, text... QString defaultType; if (action_name == "execute") defaultType = "macro"; else defaultType = "table"; //option with " " (set default type) if (stripQuotes(item, obj_name)) { type_name = defaultType; } else if ((idx = item.indexOf(':')) != -1) { //option with type name specified: type_name = item.left(idx).toLower(); obj_name = item.mid(idx + 1); //optional: remove "" if (obj_name.startsWith(QLatin1Char('\"')) && obj_name.endsWith(QLatin1Char('\"'))) { obj_name.chop(1); obj_name.remove(0, 1); } } else { //just obj. name: set default type name obj_name = item; type_name = defaultType; } } if (type_name.isEmpty()) continue; if (name_required && obj_name.isEmpty()) continue; atLeastOneFound = true; if (projectData()) { KexiProjectData::ObjectInfo* info = new KexiProjectData::ObjectInfo(); info->insert("name", obj_name); info->insert("type", type_name); info->insert("action", action_name); //ok, now add info for this object projectData()->autoopenObjects.append(info); } else return true; //no need to find more because we do not have projectData() anyway } //for return atLeastOneFound; } #endif void prettyPrintPluginMetaData(int maxWidth, const QStringList &labels, QTextStream *out, const KPluginMetaData& metaData) { #define LABEL(n) labels[n] << QString(maxWidth - labels[n].length() + 1, ' ') *out << " * " << metaData.pluginId() << endl << " " << LABEL(0) << metaData.name() << endl << " " << LABEL(1) << metaData.description() << endl << " " << LABEL(2) << metaData.version() << endl << " " << LABEL(3) << metaData.fileName() << endl; #undef LABEL } //! Nicely output a list of plugins static void prettyPrintListOfPlugins() { QTextStream out(stdout); QStringList labels; labels << i18nc("Plugin name", "Name:") << i18nc("Plugin description", "Description:") << i18nc("Plugin version", "Version:") << i18nc("Plugin fileName", "File:"); int maxWidth = -1; foreach(const QString &label, labels) { maxWidth = qMax(maxWidth, label.length()); } // 1. Kexi plugins if (Kexi::partManager().infoList()->isEmpty()) { out << i18n("No Kexi plugins found.") << endl; } else { out << i18n("Kexi plugins (%1):", Kexi::partManager().infoList()->count()) << endl; foreach(const KexiPart::Info *info, *Kexi::partManager().infoList()) { prettyPrintPluginMetaData(maxWidth, labels, &out, *info); } } // 2. KDb drivers KDbDriverManager driverManager; if (driverManager.driverIds().isEmpty()) { out << i18n("No KDb database driver plugins found.") << endl; } else { out << i18n("KDb database driver plugins (%1):", driverManager.driverIds().count()) << endl; foreach(const QString &pluginId, driverManager.driverIds()) { const KDbDriverMetaData *metaData = driverManager.driverMetaData(pluginId); if (metaData) { prettyPrintPluginMetaData(maxWidth, labels, &out, *metaData); } } } } -// Handle higher-prioroty options. -// When such options are present, handle them and immediately exit without showing -// the GUI even if other options or arguments are present. -// These options are currently: -// - options that display configuration or state of Kexi installation tristate KexiStartupHandler::handleHighPriorityOptions() { if (isSet(options().listPlugins)) { prettyPrintListOfPlugins(); setAction(Exit); return true; } // option not found: return cancelled; } -tristate KexiStartupHandler::init() +tristate KexiStartupHandler::init(const QStringList &arguments, + const QList &extraOptions) { setAction(DoNothing); - tristate res = parseOptions(); + tristate res = parseOptions(arguments, extraOptions); if (res != true) { setAction(Exit); return res; } // if (isSet(options().help)) { // helpText // } res = handleHighPriorityOptions(); if (res == true || res == false) { setAction(Exit); return res; } // Other options KDbConnectionData cdata; if (isSet(options().connectionShortcut)) { KexiDBConnShortcutFile connectionShortcut(value(options().connectionShortcut)); if (!connectionShortcut.loadConnectionData(&cdata)) { //! @todo Show error message from KexiDBConnShortcutFile when there's one implemented. //! For we're displaying generic error msg. KMessageBox::sorry(0, xi18nc("@info", "Could not read connection information from connection shortcut " "file %1." "Check whether the file has valid contents.", QDir::toNativeSeparators(connectionShortcut.fileName()))); return false; } } // Set to true if user explicitly sets conn data options from command line. // In this case display login dialog and skip the standard Welcome Wizard. bool connDataOptionsSpecified = false; if (isSet(options().dbDriver)) { cdata.setDriverId(value(options().dbDriver)); connDataOptionsSpecified = true; } QString fileType; if (isSet(options().fileType)) { fileType = value(options().fileType); } if (!positionalArguments().isEmpty() && !fileType.isEmpty()) { if (fileType != "project" && fileType != "shortcut" && fileType != "connection") { KMessageBox::sorry(0, xi18nc("Please don't translate the \"type\" word, it's constant.", "%1 is not valid value for --type " "command-line option. Possible value can be %2, " "%3 or %4", fileType, "project", "shortcut", "connection")); return false; } } if (isSet(options().host)) { cdata.setHostName(value(options().host)); connDataOptionsSpecified = true; } if (isSet(options().localSocket)) { cdata.setLocalSocketFileName(value(options().localSocket)); connDataOptionsSpecified = true; } if (isSet(options().user)) { cdata.setUserName(value(options().user)); connDataOptionsSpecified = true; } bool fileDriverSelected; if (cdata.driverId().isEmpty()) fileDriverSelected = true; else { KDbDriverManager manager; const KDbDriverMetaData *driverMetaData = manager.driverMetaData(cdata.driverId()); if (!driverMetaData) { //driver id provided explicitly, but not found KMessageBox::sorry(0, manager.result().message()); return false; } fileDriverSelected = driverMetaData->isFileBased(); } bool projectFileExists = false; if (isSet(options().port)) { bool ok; const int p = value(options().port).toInt(&ok); if (ok && p > 0) { cdata.setPort(p); connDataOptionsSpecified = true; } else { KMessageBox::sorry(0, xi18n("Invalid port number %1 specified.", value(options().port))); return false; } } if (connDataOptionsSpecified && cdata.driverId().isEmpty()) { KMessageBox::sorry(0, xi18n("Could not open database. No database driver specified.")); return false; } KexiStartupData::setForcedUserMode(isSet(options().userMode)); KexiStartupData::setForcedDesignMode(isSet(options().designMode)); KexiStartupData::setProjectNavigatorVisible(isSet(options().showNavigator)); KexiStartupData::setMainMenuVisible(!isSet(options().hideMenu)); KexiStartupData::setForcedFullScreen(isSet(options().fullScreen)); bool createDB = isSet(options().createDb) || isSet(options().createAndOpenDb); const bool openExisting = !createDB && !isSet(options().dropDb); bool readOnly = isSet(options().readOnly); const QString couldnotMsg = QString::fromLatin1("\n") + xi18n("Could not start Kexi application this way."); if (createDB && isSet(options().dropDb)) { KMessageBox::sorry(0, xi18nc("Please don't translate the \"createdb\" and \"dropdb\" words, these are constants.", "Both createdb and dropdb used in startup options.") + couldnotMsg); return false; }; if (createDB || isSet(options().dropDb)) { if (positionalArguments().isEmpty()) { KMessageBox::sorry(0, xi18n("No project name specified.")); return false; } KexiStartupData::setAction(Exit); } //! @todo add option for non-gui; integrate with KWallet; move to static KexiProject method if (!fileDriverSelected && !cdata.driverId().isEmpty() && cdata.password().isEmpty()) { if (cdata.password().isEmpty()) { delete d->passwordDialog; d->passwordDialog = new KexiDBPasswordDialog(0, cdata, KexiDBPasswordDialog::ShowDetailsButton); if (connDataOptionsSpecified) { if (cdata.userName().isEmpty()) { d->passwordDialog->setUsername(QString()); d->passwordDialog->setUsernameReadOnly(false); QLineEdit *userEdit = KexiUtils::findFirstChild(d->passwordDialog, "QLineEdit", "userEdit"); if (userEdit) { userEdit->setFocus(); } } } const int ret = d->passwordDialog->exec(); if (d->passwordDialog->showConnectionDetailsRequested() || ret == QDialog::Accepted) { } else { KexiStartupData::setAction(Exit); return true; } } } /* qDebug() << "ARGC==" << args->count(); for (int i=0;icount();i++) { qDebug() << "ARG" <user-mode and design-mode words, these are constants.", "Both user-mode and design-mode used in startup options.") + couldnotMsg); return false; } //database filenames, shortcut filenames or db names on a server if (!positionalArguments().isEmpty()) { QString prjName; QString fileName; if (fileDriverSelected) { fileName = positionalArguments().first(); } else { prjName = positionalArguments().first(); } if (fileDriverSelected) { QFileInfo finfo(fileName); prjName = finfo.fileName(); //filename only, to avoid messy names like when Kexi is started with "../../db" arg cdata.setDatabaseName(finfo.absoluteFilePath()); projectFileExists = finfo.exists(); if (isSet(options().dropDb) && !projectFileExists) { KMessageBox::sorry(0, xi18nc("@info", "Could not remove project. The file " "%1 does not exist.", QDir::toNativeSeparators(cdata.databaseName()))); return 0; } } if (createDB) { if (cdata.driverId().isEmpty()) cdata.setDriverId(KDb::defaultFileBasedDriverId()); KexiStartupData::setProjectData(new KexiProjectData(cdata, prjName)); //dummy } else { if (fileDriverSelected) { int detectOptions = 0; if (fileType == "project") detectOptions |= ThisIsAProjectFile; else if (fileType == "shortcut") detectOptions |= ThisIsAShortcutToAProjectFile; else if (fileType == "connection") detectOptions |= ThisIsAShortcutToAConnectionData; if (isSet(options().dropDb)) detectOptions |= DontConvert; if (readOnly) detectOptions |= OpenReadOnly; QString detectedDriverId; KexiStartupData::Import importData = KexiStartupData::importActionData(); bool forceReadOnly; const tristate res = detectActionForFile(&importData, &detectedDriverId, cdata.driverId(), cdata.databaseName(), 0, detectOptions, &forceReadOnly); if (true != res) return res; if (forceReadOnly) { readOnly = true; } KexiStartupData::setImportActionData(importData); if (KexiStartupData::importActionData()) { //importing action KexiStartupData::setAction(ImportProject); return true; } //opening action cdata.setDriverId(detectedDriverId); if (cdata.driverId() == "shortcut") { //get information for a shortcut file KexiStartupData::setProjectData(new KexiProjectData()); d->shortcutFileName = cdata.databaseName(); if (!KexiStartupData::projectData()->load(d->shortcutFileName, &d->shortcutFileGroupKey)) { KMessageBox::sorry(0, xi18nc("@info", "Could not open shortcut file %1.", QDir::toNativeSeparators(cdata.databaseName()))); delete KexiStartupData::projectData(); KexiStartupData::setProjectData(0); return false; } if (KexiStartupData::projectData()->databaseName().isEmpty()) { d->connDialog = new KexiDBConnectionDialog(0, *KexiStartupData::projectData(), d->shortcutFileName); connect(d->connDialog, SIGNAL(saveChanges()), this, SLOT(slotSaveShortcutFileChanges())); int dialogRes = d->connDialog->exec(); if (dialogRes == QDialog::Accepted) { //get (possibly changed) prj data KexiStartupData::setProjectData( new KexiProjectData(d->connDialog->currentProjectData())); } delete d->connDialog; d->connDialog = 0; if (dialogRes == QDialog::Rejected) { delete KexiStartupData::projectData(); KexiStartupData::setProjectData(0); return cancelled; } } } else if (cdata.driverId() == "connection") { //get information for a connection file d->connShortcutFile = new KexiDBConnShortcutFile(cdata.databaseName()); if (!d->connShortcutFile->loadConnectionData(&cdata, &d->shortcutFileGroupKey)) { KMessageBox::sorry(0, xi18nc("@info", "Could not open connection data file %1.", QDir::toNativeSeparators(cdata.databaseName()))); delete d->connShortcutFile; d->connShortcutFile = 0; return false; } bool cancel = false; while (true) { if (isSet(options().skipConnDialog)) { //show connection dialog, so user can change parameters if (!d->connDialog) { d->connDialog = new KexiDBConnectionDialog(0, cdata, d->connShortcutFile->fileName()); connect(d->connDialog, SIGNAL(saveChanges()), this, SLOT(slotSaveShortcutFileChanges())); } const int dialogRes = d->connDialog->exec(); if (dialogRes == QDialog::Accepted) { //get (possibly changed) prj data cdata = *d->connDialog->currentProjectData().connectionData(); } else { cancel = true; break; } } KexiStartupData::setProjectData(selectProject(&cdata, &cancel)); if (KexiStartupData::projectData() || cancel || !isSet(options().skipConnDialog)) break; } delete d->connShortcutFile; d->connShortcutFile = 0; delete d->connDialog; d->connDialog = 0; if (cancel) return cancelled; } else { // !shortcut && !connection KexiStartupData::setProjectData(new KexiProjectData(cdata, prjName)); } } else { // !fileDriverSelected KexiStartupData::setProjectData(new KexiProjectData(cdata, prjName)); } } } if (positionalArguments().count() > 1) { //! @todo KRun another Kexi instance } //let's show connection details, user asked for that in the "password dialog" if (d->passwordDialog && d->passwordDialog->showConnectionDetailsRequested()) { if (KexiStartupData::projectData()) { d->connDialog = new KexiDBConnectionDialog(0, *KexiStartupData::projectData()); } else { d->connDialog = new KexiDBConnectionDialog(0, cdata); } int dialogRes = d->connDialog->exec(); if (dialogRes == QDialog::Accepted) { //get (possibly changed) prj data KexiStartupData::setProjectData(new KexiProjectData(d->connDialog->currentProjectData())); } delete d->connDialog; d->connDialog = 0; if (dialogRes == QDialog::Rejected) { delete KexiStartupData::projectData(); KexiStartupData::setProjectData(0); return cancelled; } } if (positionalArguments().isEmpty() && connDataOptionsSpecified) { bool cancel = false; KexiStartupData::setProjectData(selectProject(&cdata, &cancel)); if (!KexiStartupData::projectData() || cancel) { KexiStartupData::setProjectData(0); return false; } } //! @todo KEXI3 port getAutoopenObjects() #if 0 //---autoopen objects: const bool atLeastOneAOOFound = getAutoopenObjects(args, "open") || getAutoopenObjects(args, "design") || getAutoopenObjects(args, "edittext") || getAutoopenObjects(args, "execute") || getAutoopenObjects(args, "new") || getAutoopenObjects(args, "print") || getAutoopenObjects(args, "print-preview"); if (atLeastOneAOOFound && !openExisting) { KMessageBox::information(0, xi18n("You have specified a few database objects to be opened automatically, " "using startup options.\n" "These options will be ignored because they are not available while creating " "or dropping projects.")); } #endif if (createDB) { bool creationCancelled; KexiGUIMessageHandler gui; KexiProject *prj = KexiProject::createBlankProject(&creationCancelled, *projectData(), &gui); bool ok = prj != 0; delete prj; if (creationCancelled) return cancelled; if (!isSet(options().createAndOpenDb)) { if (ok) { KMessageBox::information(0, xi18nc("@info", "Project %1 created successfully.", QDir::toNativeSeparators(projectData()->databaseName()))); } return ok; } } else if (isSet(options().dropDb)) { KexiGUIMessageHandler gui; res = KexiProject::dropProject(*projectData(), &gui, false/*ask*/); if (res == true) KMessageBox::information(0, xi18nc("@info", "Project %1 dropped successfully.", QDir::toNativeSeparators(projectData()->databaseName()))); return res != false; } //------ KexiPart::PartInfoList *partInfoList = Kexi::partManager().infoList(); if (!partInfoList || partInfoList->isEmpty()) { KexiGUIMessageHandler msgh; msgh.showErrorMessage(Kexi::partManager().result()); KexiStartupData::setProjectData(0); return false; } if (!KexiStartupData::projectData()) { cdata = KDbConnectionData(); //clear KexiStartupData::setAction(ShowWelcomeScreen); return true; //! @todo remove startup dialog code #if 0 if (!d->startupDialog) { //create startup dialog for reuse because it can be used again after conn err. d->startupDialog = new KexiStartupDialog( KexiStartupDialog::Everything, KexiStartupDialog::CheckBoxDoNotShowAgain, Kexi::connset(), 0); } if (d->startupDialog->exec() != QDialog::Accepted) return true; const int r = d->startupDialog->result(); if (r == KexiStartupDialog::CreateBlankResult) { KexiStartupData::setAction(CreateBlankProject); return true; } else if (r == KexiStartupDialog::ImportResult) { KexiStartupData::setAction(ImportProject); return true; } #ifdef KEXI_PROJECT_TEMPLATES else if (r == KexiStartupDialog::CreateFromTemplateResult) { const QString selFile(d->startupDialog->selectedFileName()); cdata.setDatabaseName(selFile); QString detectedDriverId; KexiStartupData::Import importData = KexiStartupData::importActionData(); res = detectActionForFile(&importData, &detectedDriverId, cdata.driverId(), selFile); if (true != res) return res; KexiStartupData::setImportActionData(importData); if (KexiStartupData::importActionData() || detectedDriverId.isEmpty()) return false; cdata.setDriverId(detectedDriverId); KexiStartupData::setProjectData(new KexiProjectData(cdata, selFile)); KexiStartupData::projectData()->autoopenObjects = d->startupDialog->autoopenObjects(); KexiStartupData::setAction(CreateFromTemplate); return true; } #endif else if (r == KexiStartupDialog::OpenExistingResult) { const QString selectedFile(d->startupDialog->selectedFileName()); if (!selectedFile.isEmpty()) { //file-based project cdata.setDatabaseName(selectedFile); QString detectedDriverId; KexiStartupData::Import importData = KexiStartupData::importActionData(); res = detectActionForFile(&importData, &detectedDriverId, cdata.driverId(), selectedFile); if (true != res) return res; KexiStartupData::setImportActionData(importData); if (KexiStartupData::importActionData()) { //importing action KexiStartupData::setAction(ImportProject); return true; } if (detectedDriverId.isEmpty()) return false; cdata.setDriverId(detectedDriverId); KexiStartupData::setProjectData(new KexiProjectData(cdata, selectedFile)); } else if (d->startupDialog->selectedExistingConnection()) { KDbConnectionData *cdata = d->startupDialog->selectedExistingConnection(); //ok, now we will try to show projects for this connection to the user bool cancelled; KexiStartupData::setProjectData(selectProject(cdata, &cancelled)); if ((!KexiStartupData::projectData() && !cancelled) || cancelled) { //try again return init(); } //not needed anymore delete d->startupDialog; d->startupDialog = 0; } } if (!KexiStartupData::projectData()) return true; #endif } if (KexiStartupData::projectData() && (openExisting || (createDB && isSet(options().createAndOpenDb)))) { KexiStartupData::projectData()->setReadOnly(readOnly); KexiStartupData::setAction(OpenProject); } return true; } tristate KexiStartupHandler::detectActionForFile( KexiStartupData::Import* detectedImportAction, QString *detectedDriverId, const QString& _suggestedDriverId, const QString &databaseName, QWidget *parent, int options, bool *forceReadOnly) { Q_ASSERT(detectedDriverId); *detectedImportAction = KexiStartupData::Import(); //clear if (forceReadOnly) { *forceReadOnly = false; } QString suggestedDriverId(_suggestedDriverId); //safe detectedDriverId->clear(); QFileInfo finfo(databaseName); if (databaseName.isEmpty()) { if (!(options & SkipMessages)) { KMessageBox::sorry(parent, xi18nc("@info", "Could not open file. Missing filename."), xi18nc("@title:window", "Could Not Open File")); } return false; } if (!finfo.exists()) { if (!(options & SkipMessages)) { KMessageBox::sorry(parent, xi18nc("@info", "Could not open file. " "The file %1 does not exist.", QDir::toNativeSeparators(databaseName)), xi18nc("@title:window", "Could Not Open File" )); } return false; } if (!finfo.isReadable()) { if (!(options & SkipMessages)) { KMessageBox::sorry(parent, xi18nc("@info", "Could not open file %1 for reading." "Check the file's permissions and whether it is " "already opened and locked by another application.", QDir::toNativeSeparators(databaseName)), xi18nc("@title:window", "Could Not Open File" )); } return false; } if (!(options & OpenReadOnly) && !finfo.isWritable()) { if (!KexiProject::askForOpeningNonWritableFileAsReadOnly(parent, finfo)) { return false; } if (forceReadOnly) { *forceReadOnly = true; } } QMimeType mime; QString mimename; const bool thisIsShortcut = (options & ThisIsAShortcutToAProjectFile) || (options & ThisIsAShortcutToAConnectionData); if ((options & ThisIsAProjectFile) || !thisIsShortcut) { //try this detection if "project file" mode is forced or no type is forced: QMimeDatabase db; mime = db.mimeTypeForFile(databaseName, QMimeDatabase::MatchContent); if (mime.isValid()) { mimename = mime.name(); } qDebug() << "found mime is:" << mimename; if (mimename.isEmpty() || mimename == "application/octet-stream" || mimename == "text/plain") { //try by URL: mime = db.mimeTypeForUrl(QUrl::fromLocalFile(databaseName)); mimename = mime.name(); } } if (mimename.isEmpty() || mimename == "application/octet-stream") { // perhaps the file is locked QFile f(databaseName); if (!f.open(QIODevice::ReadOnly)) { // BTW: similar error msg is provided in SQLiteConnection::drv_useDatabase() if (!(options & SkipMessages)) { KMessageBox::sorry(parent, xi18nc("@info", "Could not open project." "The file %1 is not readable. " "Check the file's permissions and whether it is already opened " "and locked by another application.", QDir::toNativeSeparators(databaseName))); } return false; } } if ((options & ThisIsAShortcutToAProjectFile) || mimename == "application/x-kexiproject-shortcut") { *detectedDriverId = "shortcut"; return true; } if ((options & ThisIsAShortcutToAConnectionData) || mimename == "application/x-kexi-connectiondata") { *detectedDriverId = "connection"; return true; } //! @todo rather check this use migration drivers' X-KexiSupportedMimeTypes [strlist] property if (mime.isValid()) { if (mimename == "application/vnd.ms-access") { if ((options & SkipMessages) || KMessageBox::Yes != KMessageBox::questionYesNo( parent, xi18nc("@info", "%1 is an external file of type %2." "Do you want to import the file as a Kexi project?", QDir::toNativeSeparators(databaseName), mime.comment()), xi18n("Open External File"), KGuiItem(xi18nc("@action:button Import File", "Import..."), KexiIconName("database-import")), KStandardGuiItem::cancel())) { return cancelled; } detectedImportAction->mimeType = mimename; detectedImportAction->fileName = databaseName; return true; } } if (!finfo.isWritable()) { //! @todo if file is ro: change project mode (but do not care if we're jsut importing) } // "application/x-kexiproject-sqlite", etc.: const QStringList driverIdsForMimeType = Kexi::driverManager().driverIdsForMimeType(mimename); QString compatibleDatabaseDriverId; if (!driverIdsForMimeType.isEmpty()) { //! @todo if there are more drivers to pick, ask which to use compatibleDatabaseDriverId = driverIdsForMimeType.first(); } bool useDetectedDriver = suggestedDriverId.isEmpty() || suggestedDriverId.compare(*detectedDriverId, Qt::CaseInsensitive) == 0; if (!useDetectedDriver) { if (compatibleDatabaseDriverId.isEmpty()) { //! @todo error message "Could not detect database driver to use" return false; } KMessageBox::ButtonCode res = KMessageBox::Yes; if (!(options & SkipMessages)) res = KMessageBox::warningYesNoCancel(parent, xi18nc("@info", "The project file %1 is recognized as compatible with " "%2 database driver, " "while you have asked for %3 database driver to be used.\n" "Do you want to use %4 database driver?", QDir::toNativeSeparators(databaseName), compatibleDatabaseDriverId, suggestedDriverId, compatibleDatabaseDriverId)); if (KMessageBox::Yes == res) useDetectedDriver = true; else if (KMessageBox::Cancel == res) return cancelled; } if (useDetectedDriver) { *detectedDriverId = compatibleDatabaseDriverId; } else { //use the suggested driver *detectedDriverId = suggestedDriverId; } // qDebug() << "driver id:" << detectedDriverName; if (detectedDriverId->isEmpty()) { QString possibleProblemsMessage(Kexi::driverManager().possibleProblemsMessage()); if (!possibleProblemsMessage.isEmpty()) { possibleProblemsMessage = xi18n("Possible problems: %1", possibleProblemsMessage); } if (!(options & SkipMessages)) { const QString comment(mime.comment().isEmpty() ? QString() : QString::fromLatin1(" (%1)").arg(mime.comment())); KMessageBox::detailedSorry(parent, xi18nc("@info", "The file %1 is not recognized as being supported by Kexi.", QDir::toNativeSeparators(databaseName)), possibleProblemsMessage.isEmpty() ? xi18nc("@info", "Could not find plugin supporting for this file type." "Detected MIME type is %1%2.", mimename, comment) : xi18nc("@info", "Could not find plugin supporting for this file type." "Detected MIME type is %1%2.%3", mimename, comment, possibleProblemsMessage)); } return false; } return true; } KexiProjectData* KexiStartupHandler::selectProject(KDbConnectionData *cdata, bool *cancelled, QWidget *parent) { clearStatus(); *cancelled = false; if (!cdata) return 0; if (!cdata->savePassword() && cdata->password().isEmpty()) { if (!d->passwordDialog) d->passwordDialog = new KexiDBPasswordDialog(0, *cdata); const int ret = d->passwordDialog->exec(); if (d->passwordDialog->showConnectionDetailsRequested() || ret == QDialog::Accepted) { } else { *cancelled = true; return 0; } } KexiProjectData* projectData = 0; //dialog for selecting a project KexiProjectSelectorDialog prjdlg(parent, *cdata, true, false); if (!prjdlg.projectSet() || prjdlg.projectSet()->result().isError()) { KexiGUIMessageHandler msgh; QString msg(xi18n("Could not load list of available projects for %1 database server.", cdata->toUserVisibleString())); if (prjdlg.projectSet()) { msgh.showErrorMessage(msg, prjdlg.projectSet()); } else { msgh.showErrorMessage(msg, QString()); } return 0; } if (prjdlg.exec() != QDialog::Accepted) { *cancelled = true; return 0; } if (prjdlg.selectedProjectData()) { //deep copy projectData = new KexiProjectData(*prjdlg.selectedProjectData()); } return projectData; } void KexiStartupHandler::slotSaveShortcutFileChanges() { bool ok = true; QString fileName; if (!d->shortcutFileName.isEmpty()) { fileName = d->shortcutFileName; ok = d->connDialog->currentProjectData().save( d->shortcutFileName, d->connDialog->savePasswordOptionSelected(), &d->shortcutFileGroupKey); } else if (d->connShortcutFile) { fileName = d->connShortcutFile->fileName(); ok = d->connShortcutFile->saveConnectionData( *d->connDialog->currentProjectData().connectionData(), d->connDialog->savePasswordOptionSelected(), &d->shortcutFileGroupKey); } if (!ok) { KMessageBox::sorry(0, xi18n("Failed saving connection data to %1 file.", QDir::toNativeSeparators(fileName))); } } diff --git a/src/main/startup/KexiStartup.h b/src/main/startup/KexiStartup.h index 5fa054591..3422ca6f2 100644 --- a/src/main/startup/KexiStartup.h +++ b/src/main/startup/KexiStartup.h @@ -1,114 +1,115 @@ /* This file is part of the KDE project - Copyright (C) 2003-2015 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXI_STARTUPHANDLER_H #define KEXI_STARTUPHANDLER_H #include #include #include class KCmdLineArgs; class KDbConnectionData; class KexiProjectData; /*! Handles startup actions for Kexi application. */ class KexiStartupHandler : public QObject, public KexiStartupData, public Kexi::ObjectStatus { Q_OBJECT public: - KexiStartupHandler(); virtual ~KexiStartupHandler(); - virtual tristate init(); + //! \return singleton Startup Handler singleton. + static KexiStartupHandler* global(); /*! Options for detectActionForFile() */ enum DetectActionForFileOptions { DontConvert = 1, //!< Skip asking for conversion (used e.g. when dropdb is called). ThisIsAProjectFile = 2, //!< A hint, forces detection of the file as a project file. ThisIsAShortcutToAProjectFile = 4, //!< A hint, forces detection of the file //!< as a shortcut to a project file. ThisIsAShortcutToAConnectionData = 8, //!< A hint, forces detection of the file //!< as a shortcut to a connection data. SkipMessages = 0x10, //!< Do not display error or warning messages. OpenReadOnly = 0x20 //!< Open in readonly mode. }; /*! Used for opening existing file-based projects. Detects actions that should be performed for by looking at the file's mime type. \return true if actions should be performed or cancelled if action should be cancelled In this case there are two possibilities: - \a detectedImportAction == true means "import action" should be performed - nonempty \a detectedDriverId means "open action" should be performed. \a detectedDriverId can contain following special strings: - "shortcut" if the file looks like a shortcut to a project/connection file - "connection" if the file looks like a connection data file. \a parent is passed as a parent for potential error message boxes. \a _suggestedDriverId is a preferred driver ID. \a options should be a combination of DetectActionForFileOptions enum values. \a forceReadOnly points to a flag that will be set to true if it was detected that the file cannot be written. */ static tristate detectActionForFile( KexiStartupData::Import* detectedImportAction, QString* detectedDriverId, const QString& _suggestedDriverId, const QString &databaseName, QWidget *parent = 0, int options = 0, bool *forceReadOnly = 0); /*! Allows user to select a project with KexiProjectSelectorDialog. \return selected project's data Returns NULL and sets cancelled to true if the dialog was cancelled. Returns NULL and sets cancelled to false if there was an error. */ KexiProjectData* selectProject(KDbConnectionData *cdata, bool *cancelled, QWidget *parent = 0); protected Q_SLOTS: void slotSaveShortcutFileChanges(); //! Reaction to application's quit, needed because it is safer to destroy filewidget-related GUIs before void slotAboutToAppQuit(); protected: + virtual tristate init(const QStringList &arguments, + const QList &extraOptions = QList()); + //! @todo KEXI3 port getAutoopenObjects() // bool getAutoopenObjects(KCmdLineArgs *args, const QByteArray &action_name); - //! Handle higher-prioroty options. +private: + KexiStartupHandler(); + + //! Handle higher-priority options. /*! When such options are present, handle them and immediately exit without showing the GUI even if other options or arguments are present. These options are currently: - options that display configuration or state of Kexi installation @return true if such an option has been found, false if there's failure, cancelled if no such option has been found (in this case processing of other options should continue) */ tristate handleHighPriorityOptions(); class Private; Private * const d; + friend class KexiMainWindow; }; -namespace Kexi -{ -//! \return singleton Startup Handler singleton. - KexiStartupHandler& startupHandler(); -} - #endif