diff --git a/src/core/KexiCommandLineOptions.cpp b/src/core/KexiCommandLineOptions.cpp index 74b0699ac..0969043f5 100644 --- a/src/core/KexiCommandLineOptions.cpp +++ b/src/core/KexiCommandLineOptions.cpp @@ -1,190 +1,196 @@ /* This file is part of the KDE project Copyright (C) 2002, 2003 Lucijan Busch Copyright (C) 2002, 2003 Joseph Wenninger Copyright (C) 2003-2016 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 "KexiCommandLineOptions.h" #include #include #include #include #include KexiCommandLineOptions::KexiCommandLineOptions( QCommandLineParser *parser) // NOTE: REMEMBER TO ADD NEW OPTIONS IN KexiStartupData::parseOptions() // Options related to entire projects: : createDb("createdb", xi18nc("'createdb' command line option", "Create a new, blank project using specified database driver and database " "name and exit immediately. You will be asked for confirmation if " "overwriting is needed.")), createAndOpenDb("create-opendb", xi18nc("'create-opendb' command line option", "Like --createdb, but also open newly created database.")), dropDb("dropdb", xi18nc("'dropdb' command line option", "Drop (remove) a project using specified database driver and database name. " "You will be asked for confirmation.")), dbDriver(QStringList() << "drv" << "dbdriver", xi18nc("'dbdriver' command line option", "Name of a database driver to be used when connecting to a database project " "(\"sqlite\" by default). Ignored if a shortcut filename is provided. " "Complete KDb-specific globally unique identifier can be used, " "e.g. \"org.kde.kdb.sqlite\" to specify exact vendor of the driver."), "name_or_id"), fileType(QStringList() << "t" << "type", xi18nc("'type' command line option", "Specify the type of file provided as an argument. This option is only " "useful if the filename does not have a valid extension set and its type " "cannot be determined unambiguously by examining its contents. This option " "is ignored if no file is specified as an argument.\n" "Available file types are:\n" "- \"project\" for a project file (the default)\n" "- \"shortcut\" for a shortcut file pointing to a\n" " project.\n" "- \"connection\" for database connection data."), "name"), connectionShortcut(QStringList() << "conn" << "connection", xi18nc("'connection' command line option", "Specify a database connection shortcut .kexic file containing connection data. " "Can be used with --createdb or --create-opendb for convenience instead " "of using options such as --user, --host or --port.\n" "Note: Options like --user, --host have precedence over settings defined " "in the shortcut file."), "shortcut_filename"), readOnly("readonly", xi18nc("'readonly' command line option", "Specify that any database connections will be performed without write support. " "This option is ignored when \"createdb\" option is present, otherwise the " "database could not be created.")), userMode("user-mode", xi18nc("'user-mode' command line option", "Start project in User Mode, regardless of the project settings.")), designMode("design-mode", xi18nc("'design-mode' command line option", "Start project in Design Mode, regardless of the project settings.")), showNavigator("show-navigator", xi18nc("'show-navigator' command line option", "Show the Project Navigator side pane even if Kexi runs in User Mode.")), hideMenu("hide-menu", xi18nc("'hide-menu' command line option", "Hide the main menu (the tabbed toolbar) completely. A number of commands " "from the main menu is still visible. This option is useful in User Mode.")), // Options related to opening objects within a project: open("open", xi18nc("'open' command line option", "Open object of type 'object_type' and name 'object_name' from specified " "project on application start. 'object_type' is optional, if omitted - %1 " "type is assumed. Other object types can be %2, %3, %4, %5. " "There may by more or less types available depending on Kexi plugins " "installed.\n" "Use \"\" characters to specify names containing spaces.\n" "Examples: --open MyTable, --open %2:\"My very big query\"", "table", "query", "form", "report", "script"), "[object_type:]object_name"), design("design", xi18nc("'design' command line option", "Like --open, but the object will be opened in Design Mode, if one is available."), "[object_type:]object_name"), editText("edittext", xi18nc("'edittext' command line option", "Like --open, but the object will be opened in Text Mode, if one is available."), "[object_type:]object_name"), - execute(QStringList() << "exec" << "execute", + execute(QStringList() << "execute" << "exec", xi18nc("'execute' command line option", "Start execution of object of type 'object_type' and name 'object_name' on " "application start. 'object_type' is optional, if omitted - %1 type is " "assumed. Object type can be also %2. There may by more or less types " "available depending on Kexi plugins installed.\n" "Use \"\" characters to specify names containing spaces.", "macro", "script"), "[object_type:]object_name"), newObject("new", xi18nc("'new' command line option", - "Start design of a new object of type 'object_type'.")), + "Start design of a new object of type 'object_type'."), + "object_type"), print("print", xi18nc("'print' command line option", "Open the Print dialog window for an object of type 'object_type' and " "name 'object_name' in the specified project when the application starts " "for quick printing of the object's data. 'object_type' is optional; " "if omitted, %1 type is assumed. Object type can also be %2.", "table", "query"), "[object_type:]object_name"), printPreview("print-preview", xi18nc("'print-preview' command line option", "Open the Print Preview window for an object of type 'object_type' and " "name 'object_name' in the specified project when the application starts " "to see preview of the object's data printout. 'object_type' is optional; " "if omitted, %1 type is assumed. Object type can also be %2.", "table", "query"), "[object_type:]object_name"), // Options related to database servers: user(QStringList() << "u" << "user", xi18nc("'user' command line option", "Database server's user name when connecting to a project. Ignored if the " "project is opened using a shortcut file. Default user name is the same " "as the current login (%1).", KUser().loginName())), //! @todo re-add '-h' as soon as KAboutData::setupCommandLine() stops forcibly call parser->addHelpOption() host(QStringList() /*<< "h"*/ << "host", xi18nc("'host' command line option", "Network server's (host) name to be used when connecting to a database " "project. Ignored if the project is opened using a shortcut file. Default " "host is the local computer."), "name"), port("port", xi18nc("'port' command line option", "Network server's port number to be used when connecting to a database " "project. Ignored if the project is opened using a shortcut file. " "Defaults depend on the used server type (e.g. %1, %2).", "MySQL", "PostgreSQL"), "number"), localSocket(QStringList() << "socket" << "local-socket", xi18nc("'local-socket' command line option", "Local computer's socket filename to be used when connecting to " "a database project. Ignored if the project is opened using a shortcut file. " "Defaults depend on the used server type (e.g. %1, %2).", "MySQL", "PostgreSQL"), "filename"), // Options related to the GUI: skipConnDialog("skip-conn-dialog", xi18nc("'skip-conn-dialog' command line option", "Skip displaying connection dialog window and connect directly. Available " "when opening .kexic or .kexis shortcut files.")), fullScreen(QStringList() << "f" << "fullscreen", xi18nc("'fullscreen' command line option", "Start Kexi in full screen mode to occupy the whole screen area by hiding " "window decorations such as title bars.")), // Options that display configuration or state of Kexi installation. // When used, Kexi immediately exits without showing the GUI even if other options // or arguments are present. listPlugins("list-plugins", xi18nc("'list-plugins' command line option", "Displays list of plugins available for Kexi with their name, description, " "version and filenames.")) { Q_UNUSED(parser); } + +QList KexiCommandLineOptions::autoopeningObjectsOptions() const +{ + return {open, design, editText, execute, newObject, print, printPreview}; +} diff --git a/src/core/KexiCommandLineOptions.h b/src/core/KexiCommandLineOptions.h index c8cba67d7..60530fdf5 100644 --- a/src/core/KexiCommandLineOptions.h +++ b/src/core/KexiCommandLineOptions.h @@ -1,64 +1,67 @@ /* This file is part of the KDE project Copyright (C) 2002, 2003 Lucijan Busch Copyright (C) 2002, 2003 Joseph Wenninger Copyright (C) 2003-2016 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 KEXICOMMANDLINEOPTIONS_H #define KEXICOMMANDLINEOPTIONS_H +#include "kexicore_export.h" #include - #include class QCommandLineParser; //! Command line options for Kexi -class KexiCommandLineOptions +class KEXICORE_EXPORT KexiCommandLineOptions { public: explicit KexiCommandLineOptions(QCommandLineParser *parser); + //! @return list of options related to "auto-opening objects" + QList autoopeningObjectsOptions() const; + QCommandLineOption createDb; QCommandLineOption createAndOpenDb; QCommandLineOption dropDb; QCommandLineOption dbDriver; QCommandLineOption fileType; QCommandLineOption connectionShortcut; QCommandLineOption readOnly; QCommandLineOption userMode; QCommandLineOption designMode; QCommandLineOption showNavigator; QCommandLineOption hideMenu; QCommandLineOption open; QCommandLineOption design; QCommandLineOption editText; QCommandLineOption execute; QCommandLineOption newObject; QCommandLineOption print; QCommandLineOption printPreview; QCommandLineOption user; QCommandLineOption host; QCommandLineOption port; QCommandLineOption localSocket; QCommandLineOption skipConnDialog; QCommandLineOption fullScreen; QCommandLineOption listPlugins; }; #endif diff --git a/src/core/kexipartmanager.cpp b/src/core/kexipartmanager.cpp index 3cdbd9eab..714a36ab3 100644 --- a/src/core/kexipartmanager.cpp +++ b/src/core/kexipartmanager.cpp @@ -1,308 +1,310 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2016 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 "kexipartmanager.h" #include "kexipart.h" #include "kexiinternalpart.h" #include "kexipartinfo.h" //! @todo KEXI3 #include "kexistaticpart.h" #include "KexiVersion.h" #include "KexiJsonTrader.h" #include #include #include #include #include #include #include #include #include using namespace KexiPart; typedef QHash KexiInternalPartDict; Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiPartTrader_instance, ("kexi")) class Manager::Private { public: explicit Private(Manager *manager_); ~Private(); Manager *manager; PartDict parts; KexiInternalPartDict internalParts; PartInfoList partlist; PartInfoDict partsByPluginId; bool lookupDone; bool lookupResult; }; Manager::Private::Private(Manager *manager_) : manager(manager_) , lookupDone(false) , lookupResult(false) { } Manager::Private::~Private() { qDeleteAll(partlist); partlist.clear(); } //--- Manager::Manager(QObject *parent) : QObject(parent), KDbResultable(), d(new Private(this)) { } Manager::~Manager() { delete d; } template PartClass* Manager::part(Info *info, QHash *partDict) { if (!info) { return 0; } clearResult(); KDbMessageGuard mg(this); if (!lookup()) { return 0; } if (!info->isValid()) { m_result = KDbResult(info->errorMessage()); return 0; } PartClass *p = partDict->value(info->pluginId()); if (p) { return p; } // actual loading KPluginFactory *factory = qobject_cast(info->instantiate()); if (!factory) { m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, xi18nc("@info", "Could not load Kexi plugin file %1.", info->fileName())); QPluginLoader loader(info->fileName()); // use this to get the message (void)loader.load(); m_result.setServerMessage(loader.errorString()); info->setErrorMessage(m_result.message()); qWarning() << m_result.message() << m_result.serverMessage(); return 0; } p = factory->create(this); if (!p) { m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, xi18nc("@info", "Could not open Kexi plugin %1.").arg(info->fileName())); qWarning() << m_result.message(); return 0; } p->setInfo(info); p->setObjectName(QString("%1 plugin").arg(info->id())); partDict->insert(info->pluginId(), p); return p; } //! @return a string list @a list with removed whitespace from the beginning and end of each string. //! Empty strings are also removed. static QStringList cleanupStringList(const QStringList &list) { QStringList result; foreach(const QString &item, list) { QString cleanedItem = item.trimmed(); if (!cleanedItem.isEmpty()) { result.append(cleanedItem); } } return result; } bool Manager::lookup() { if (d->lookupDone) { return d->lookupResult; } d->lookupDone = true; d->lookupResult = false; d->partlist.clear(); d->partsByPluginId.clear(); d->parts.clear(); // load visual order of plugins KConfigGroup cg(KSharedConfig::openConfig()->group("Parts")); const QStringList orderedPluginIds = cleanupStringList( cg.readEntry("Order", "org.kexi-project.table," "org.kexi-project.query," "org.kexi-project.form," "org.kexi-project.report," "org.kexi-project.macro," "org.kexi-project.script").split(',')); QVector orderedInfos(orderedPluginIds.count()); QStringList serviceTypes; serviceTypes << "Kexi/Viewer" << "Kexi/Designer" << "Kexi/Editor" << "Kexi/Internal"; QList offers = KexiPartTrader_instance->query(serviceTypes); foreach(const QPluginLoader *loader, offers) { QScopedPointer info(new Info(*loader)); if (info->id().isEmpty()) { qWarning() << "No plugin ID specified for Kexi Part" << info->fileName() << "-- skipping!"; continue; } // check version if (info->majorVersion() != KEXI_PART_VERSION) { qWarning() << "Kexi plugin" << info->id() << "has version" << info->majorVersion() << "but version required by Kexi is" << KEXI_PART_VERSION << "-- skipping this plugin!"; continue; } // skip experimental types if ( (!Kexi::tempShowMacros() && info->id() == "org.kexi-project.macro") || (!Kexi::tempShowScripts() && info->id() == "org.kexi-project.script") ) { continue; } // skip duplicates if (d->partsByPluginId.contains(info->id())) { qWarning() << "More than one Kexi plugin with ID" << info->id() << info->fileName() << "-- skipping this one"; continue; } // find correct place for plugins visible in Navigator if (info->isVisibleInNavigator()) { const int index = orderedPluginIds.indexOf(info->id()); if (index != -1) { orderedInfos[index] = info.data(); } else { orderedInfos.append(info.data()); } // append later when we know order } else { // append now d->partlist.append(info.data()); } d->partsByPluginId.insert(info->pluginId(), info.data()); info.take(); } qDeleteAll(offers); offers.clear(); // fill the final list using computed order for (int i = 0; i < orderedInfos.size(); i++) { Info *info = orderedInfos[i]; if (!info) { continue; } //qDebug() << "adding Kexi part info" << info->pluginId(); d->partlist.insert(i, info); } // now the d->partlist is: [ordered plugins visible in Navigator] [other plugins in unspecified order] d->lookupResult = true; return true; } Part* Manager::part(Info *info) { KDbMessageGuard mg(this); Part *p = part(info, &d->parts); if (p) { emit partLoaded(p); } return p; } static QString realPluginId(const QString &pluginId) { if (pluginId.contains('.')) { return pluginId; } else { // not like "org.kexi-project.table" - construct return QString::fromLatin1("org.kexi-project.") + QString(pluginId).remove("kexi/"); } } Part* Manager::partForPluginId(const QString &pluginId) { Info* info = infoForPluginId(pluginId); return part(info); } Info* Manager::infoForPluginId(const QString &pluginId) { KDbMessageGuard mg(this); if (!lookup()) return 0; const QString realId = realPluginId(pluginId); Info *i = realId.isEmpty() ? 0 : d->partsByPluginId.value(realId); if (i) return i; - m_result = KDbResult(xi18nc("@info", "No plugin for ID %1", realId)); + m_result = KDbResult(kxi18nc("@info", "No plugin for ID %1") + .subs(realId) + .toString(Kuit::VisualFormat::PlainText)); return 0; } /*! @todo KEXI3 void Manager::insertStaticPart(StaticPart* part) { if (!part) return; KDbMessageGuard mg(this); if (!lookup()) return; d->partlist.append(part->info()); if (!part->info()->pluginId().isEmpty()) d->partsByPluginId.insert(part->info()->pluginId(), part->info()); d->parts.insert(part->info()->pluginId(), part); } */ KexiInternalPart* Manager::internalPartForPluginId(const QString& pluginId) { Info* info = infoForPluginId(pluginId); if (!info || !info->serviceTypes().contains("Kexi/Internal")) { return nullptr; } return part(info, &d->internalParts); } PartInfoList* Manager::infoList() { KDbMessageGuard mg(this); if (!lookup()) { return 0; } return &d->partlist; } diff --git a/src/core/kexistartupdata.cpp b/src/core/kexistartupdata.cpp index 10400f124..a6cce9268 100644 --- a/src/core/kexistartupdata.cpp +++ b/src/core/kexistartupdata.cpp @@ -1,242 +1,247 @@ /* This file is part of the KDE project Copyright (C) 2004-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 "kexistartupdata.h" #include "kexi.h" #include "KexiCommandLineOptions.h" #include "config-kexi.h" #include #include #include #include class 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) { } KexiStartupData::~KexiStartupData() { delete d; } 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() { 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(); \ return false; \ } 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) #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); 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::values(const QCommandLineOption &option) const +{ + return d->parser.values(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..2e2ec9e42 100644 --- a/src/core/kexistartupdata.h +++ b/src/core/kexistartupdata.h @@ -1,133 +1,135 @@ /* This file is part of the KDE project Copyright (C) 2004-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. */ #ifndef KEXI_STARTUPDATA_H #define KEXI_STARTUPDATA_H #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(); 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 //! @return true on success tristate parseOptions(); //! @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 value for option @a option + QString value(const QCommandLineOption &option) const; + + //! @return list of values for option @a option + QStringList values(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/main/startup/KexiStartup.cpp b/src/main/startup/KexiStartup.cpp index 525731192..c8808a516 100644 --- a/src/main/startup/KexiStartup.cpp +++ b/src/main/startup/KexiStartup.cpp @@ -1,1046 +1,1053 @@ /* 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 { // 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); } return *_startupHandler; } } class KexiStartupData; //--------------------------------- +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; +} + //! @internal class KexiStartupHandler::Private { public: - Private() - : passwordDialog(0)//, showConnectionDetailsExecuted(false) - , connShortcutFile(0), connDialog(0), startupDialog(0) { + Private(KexiStartupHandler *handler) + : q(handler) + { } ~Private() { destroyGui(); } void destroyGui() { delete passwordDialog; passwordDialog = 0; delete connDialog; connDialog = 0; delete startupDialog; startupDialog = 0; } - KexiDBPasswordDialog* passwordDialog; + bool findAutoopenObjects() + { + bool atLeastOneFound = false; + KexiProjectData::AutoOpenObjects *objects + = q->projectData() ? &q->projectData()->autoopenObjects : nullptr; + for (const QCommandLineOption &option : q->options().autoopeningObjectsOptions()) { + if (findAutoopenObjects(option, objects)) { + atLeastOneFound = true; + } + } + return atLeastOneFound; + } + + bool findAutoopenObjects(const QCommandLineOption &option, + KexiProjectData::AutoOpenObjects *objects) + { + bool atLeastOneFound = false; + for (const QString &value : q->values(option)) { + QString typeName; + QString objectName; + int idx; + bool nameRequired = true; + if (option.names() == q->options().newObject.names()) { + objectName.clear(); + stripQuotes(value, &typeName); + nameRequired = false; + } else {//open, design, text... + QString defaultType; + if (option.names() == q->options().execute.names()) { +#ifdef KEXI_MACROS_SUPPORT + defaultType = "macro"; +#else + defaultType = "script"; +#endif + } else { + defaultType = "table"; + } + + //option with " " (set default type) + if (stripQuotes(value, &objectName)) { + typeName = defaultType; + } else if ((idx = value.indexOf(':')) != -1) { + //option with type name specified: + typeName = value.left(idx).toLower(); + objectName = value.mid(idx + 1); + (void)stripQuotes(objectName, &objectName); // remove optional quotes + } else { + //just obj. name: set default type name + objectName = value; + typeName = defaultType; + } + } + if (typeName.isEmpty()) { + continue; + } + if (nameRequired && objectName.isEmpty()) { + continue; + } + atLeastOneFound = true; + if (objects) { + KexiProjectData::ObjectInfo* info = new KexiProjectData::ObjectInfo; + info->insert("name", objectName); + info->insert("type", typeName); + info->insert("action", option.names().first()); + //ok, now add info for this object + objects->append(info); + } else { + return true; //no need to find more because we do not have project anyway + } + } //for + return atLeastOneFound; + } + + KexiDBPasswordDialog* passwordDialog = nullptr; QString shortcutFileName; - KexiDBConnShortcutFile *connShortcutFile; - KexiDBConnectionDialog *connDialog; + KexiDBConnShortcutFile *connShortcutFile = nullptr; + KexiDBConnectionDialog *connDialog = nullptr; QString shortcutFileGroupKey; - KexiStartupDialog *startupDialog; + KexiStartupDialog *startupDialog = nullptr; +private: + KexiStartupHandler* const q; }; //--------------------------------- 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()) + , d(new Private(this)) { connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(slotAboutToAppQuit())); } KexiStartupHandler::~KexiStartupHandler() { qAddPostRoutine(Kexi::_destroyStartupHandler); // post routine is installed! delete d; } 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() { setAction(DoNothing); tristate res = parseOptions(); 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"); + const bool atLeastOneAOOFound = d->findAutoopenObjects(); 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.")); + "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(); } //! @todo What about trying to reuse CALLIGRA FILTER CHAINS here? 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").arg(possibleProblemsMessage); } if (!(options & SkipMessages)) { KMessageBox::detailedSorry(parent, xi18nc("@info", "The file %1 is not recognized as being supported by Kexi.", QDir::toNativeSeparators(databaseName)), xi18nc("@info", "Could not find plugin supporting for this file type." "Detected MIME type is %1%2.%3", mimename) .arg(mime.comment().isEmpty() ? QString() : QString::fromLatin1(" (%1)").arg(mime.comment())) .arg(possibleProblemsMessage.isEmpty() ? QString() : QString("%1").arg(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..4b04a7e4e 100644 --- a/src/main/startup/KexiStartup.h +++ b/src/main/startup/KexiStartup.h @@ -1,114 +1,111 @@ /* 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(); /*! 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: -//! @todo KEXI3 port getAutoopenObjects() -// bool getAutoopenObjects(KCmdLineArgs *args, const QByteArray &action_name); - //! 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 @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; }; namespace Kexi { //! \return singleton Startup Handler singleton. KexiStartupHandler& startupHandler(); } #endif