File indexing completed on 2024-05-12 16:40:03

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
0003    Copyright (C) 2003-2018 Jarosław Staniek <staniek@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "KexiMainWindow.h"
0022 #include "KexiMainWindow_p.h"
0023 #include "kexiactionproxy.h"
0024 #include "kexipartmanager.h"
0025 #include "kexipart.h"
0026 #include "kexipartinfo.h"
0027 #include "kexipartguiclient.h"
0028 #include "kexiproject.h"
0029 #include "kexiprojectdata.h"
0030 #include "kexi.h"
0031 #include "kexiinternalpart.h"
0032 #include "kexiactioncategories.h"
0033 #include "kexifinddialog.h"
0034 #include "kexisearchandreplaceiface.h"
0035 #include "KexiBugReportDialog.h"
0036 #define KEXI_SKIP_REGISTERICONSRESOURCE
0037 #define KEXI_SKIP_SETUPPRIVATEICONSRESOURCE
0038 #include "KexiRegisterResource_p.h"
0039 #include <kexiutils/utils.h>
0040 #include <kexiutils/KexiCloseButton.h>
0041 #include <kexiutils/KexiTester.h>
0042 #include <KexiVersion.h>
0043 #include <core/KexiWindow.h>
0044 #include <core/KexiRecentProjects.h>
0045 #include <core/kexiaboutdata.h>
0046 #include <core/KexiCommandLineOptions.h>
0047 #include <KexiIcon.h>
0048 #include <kexi_global.h>
0049 #include <widget/properties/KexiPropertyEditorView.h>
0050 #include <widget/utils/kexirecordnavigator.h>
0051 #include <widget/utils/KexiDockableWidget.h>
0052 #include <widget/navigator/KexiProjectNavigator.h>
0053 #include <widget/navigator/KexiProjectModel.h>
0054 #include <widget/KexiNameDialog.h>
0055 #include <widget/KexiNameWidget.h>
0056 #include <widget/KexiDBPasswordDialog.h>
0057 #include "startup/KexiStartup.h"
0058 #include "startup/KexiNewProjectAssistant.h"
0059 #include "startup/KexiOpenProjectAssistant.h"
0060 #include "startup/KexiWelcomeAssistant.h"
0061 #include "startup/KexiImportExportAssistant.h"
0062 #include <KexiAssistantPage.h>
0063 
0064 #include <KDbConnection>
0065 #include <KDbConnectionOptions>
0066 #include <KDbCursor>
0067 #include <KDbAdmin>
0068 #include <KDbDriverManager>
0069 #include <KDbObjectNameValidator>
0070 
0071 #include <KPropertyEditorView>
0072 #include <KPropertySet>
0073 
0074 #include <KActionCollection>
0075 #include <KActionMenu>
0076 #include <KToggleAction>
0077 #include <KStandardShortcut>
0078 #include <KStandardGuiItem>
0079 #include <KConfig>
0080 #include <KShortcutsDialog>
0081 #include <KEditToolBar>
0082 #include <KToggleFullScreenAction>
0083 #include <KIconLoader>
0084 #include <KHelpMenu>
0085 #include <KMultiTabBar>
0086 #include <KLocalizedString>
0087 #include <KMessageBox>
0088 #include <KConfigGroup>
0089 #include <KAcceleratorManager>
0090 
0091 #include <QApplication>
0092 #include <QFile>
0093 #include <QProcess>
0094 #include <QToolButton>
0095 #include <QDebug>
0096 #include <QHash>
0097 #include <QStylePainter>
0098 #include <QStyleFactory>
0099 #include <QDesktopWidget>
0100 #include <QResource>
0101 
0102 #if !defined(KexiVDebug)
0103 # define KexiVDebug if (0) qDebug()
0104 #endif
0105 
0106 #ifdef HAVE_KCRASH
0107 #include <kcrash.h>
0108 //! @todo else, add Breakpad? https://phabricator.kde.org/T1642
0109 #endif
0110 
0111 KexiDockWidgetStyle::KexiDockWidgetStyle(const QString &baseStyleName)
0112  : QProxyStyle(baseStyleName)
0113 {
0114 }
0115 
0116 KexiDockWidgetStyle::~KexiDockWidgetStyle()
0117 {
0118 }
0119 
0120 void KexiDockWidgetStyle::polish(QWidget* widget)
0121 {
0122     baseStyle()->polish(widget);
0123     widget->setContentsMargins(0, 0, 0, 0);
0124 }
0125 
0126 class Q_DECL_HIDDEN KexiDockWidget::Private
0127 {
0128 public:
0129     Private() {}
0130     QSize hint;
0131 };
0132 
0133 KexiDockWidget::KexiDockWidget(const QString &_tabText, QWidget *parent)
0134         : QDockWidget(parent), tabText(_tabText), d(new Private)
0135 {
0136     // No floatable dockers, Dolphin had problems, we don't want the same...
0137     // https://bugs.kde.org/show_bug.cgi?id=288629
0138     // https://bugs.kde.org/show_bug.cgi?id=322299
0139     setFeatures(QDockWidget::NoDockWidgetFeatures);//DockWidgetClosable);
0140     setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea);
0141     setFocusPolicy(Qt::NoFocus);
0142     if (style()->objectName().compare("windowsvista", Qt::CaseInsensitive) == 0) {
0143         // windowsvista style has broken accelerator visualization support
0144         KAcceleratorManager::setNoAccel(this);
0145     }
0146     KexiDockWidgetStyle *customStyle = new KexiDockWidgetStyle(style()->objectName());
0147     customStyle->setParent(this);
0148     setStyle(customStyle);
0149     setTitleBarWidget(new QWidget(this)); // hide the title
0150     layout()->setContentsMargins(0, 0, 0, 0);
0151     layout()->setSpacing(0);
0152 }
0153 
0154 KexiDockWidget::~KexiDockWidget()
0155 {
0156     delete d;
0157 }
0158 
0159 void KexiDockWidget::paintEvent(QPaintEvent *pe)
0160 {
0161     Q_UNUSED(pe);
0162     QStylePainter p(this);
0163     if (isFloating()) {
0164         QStyleOptionFrame framOpt;
0165         framOpt.initFrom(this);
0166         p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
0167     }
0168 
0169     // Title must be painted after the frame, since the areas overlap, and
0170     // the title may wish to extend out to all sides (eg. XP style)
0171     QStyleOptionDockWidget titleOpt;
0172     initStyleOption(&titleOpt);
0173     p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt);
0174 }
0175 
0176 void KexiDockWidget::setSizeHint(const QSize& hint)
0177 {
0178     d->hint = hint;
0179 }
0180 
0181 QSize KexiDockWidget::sizeHint() const
0182 {
0183     return d->hint.isValid() ? d->hint : QDockWidget::sizeHint();
0184 }
0185 
0186 //-------------------------------------------------
0187 
0188 KexiMainWindowTabWidget::KexiMainWindowTabWidget(QWidget *parent, KexiMainWidget* mainWidget)
0189         : QTabWidget(parent)
0190         , m_mainWidget(mainWidget)
0191         , m_tabIndex(-1)
0192 {
0193     m_closeAction = new QAction(koIcon("tab-close"), xi18n("&Close Tab"), this);
0194     m_closeAction->setToolTip(xi18n("Close the current tab"));
0195     m_closeAction->setWhatsThis(xi18n("Closes the current tab."));
0196     m_closeAllTabsAction = new QAction(xi18n("Cl&ose All Tabs"), this);
0197     m_closeAllTabsAction->setToolTip(xi18n("Close all tabs"));
0198     m_closeAllTabsAction->setWhatsThis(xi18n("Closes all tabs."));
0199     connect(m_closeAction, SIGNAL(triggered()), this, SLOT(closeTab()));
0200     connect(m_closeAllTabsAction, SIGNAL(triggered()), this, SLOT(closeAllTabs()));
0201 //! @todo  insert window list in the corner widget as in firefox
0202 #if 0
0203     // close-tab button:
0204     QToolButton* rightWidget = new QToolButton(this);
0205     rightWidget->setDefaultAction(m_closeAction);
0206     rightWidget->setText(QString());
0207     rightWidget->setAutoRaise(true);
0208     rightWidget->adjustSize();
0209     setCornerWidget(rightWidget, Qt::TopRightCorner);
0210 #endif
0211     setMovable(true);
0212     setDocumentMode(true);
0213     tabBar()->setExpanding(false);
0214 }
0215 
0216 KexiMainWindowTabWidget::~KexiMainWindowTabWidget()
0217 {
0218 }
0219 
0220 void KexiMainWindowTabWidget::paintEvent(QPaintEvent * event)
0221 {
0222     if (count() > 0)
0223         QTabWidget::paintEvent(event);
0224     else
0225         QWidget::paintEvent(event);
0226 }
0227 
0228 void KexiMainWindowTabWidget::mousePressEvent(QMouseEvent *event)
0229 {
0230     //! @todo KEXI3 test KexiMainWindowTabWidget's contextMenu event port from KTabWidget
0231     if (event->button() == Qt::RightButton) {
0232         int tab = tabBar()->tabAt(event->pos());
0233         const QPoint realPos(tabBar()->mapToGlobal(event->pos()));
0234         if (QRect(tabBar()->mapToGlobal(QPoint(0,0)),
0235               tabBar()->mapToGlobal(QPoint(tabBar()->width()-1, tabBar()->height()-1))).contains(realPos))
0236         {
0237             showContextMenuForTab(tab, tabBar()->mapToGlobal(event->pos()));
0238             return;
0239         }
0240     }
0241     QTabWidget::mousePressEvent(event);
0242 }
0243 
0244 void KexiMainWindowTabWidget::closeTab()
0245 {
0246     KexiMainWindow *main = dynamic_cast<KexiMainWindow*>(KexiMainWindowIface::global());
0247     if (main) {
0248         main->closeWindowForTab(m_tabIndex);
0249     }
0250 }
0251 
0252 tristate KexiMainWindowTabWidget::closeAllTabs()
0253 {
0254     tristate alternateResult = true;
0255     QList<KexiWindow*> windowList;
0256     KexiMainWindow *main = dynamic_cast<KexiMainWindow*>(KexiMainWindowIface::global());
0257     if (!main) {
0258         return alternateResult;
0259     }
0260     for (int i = 0; i < count(); i++) {
0261         KexiWindow *window = main->windowForTab(i);
0262         if (window) {
0263             windowList.append(window);
0264         }
0265     }
0266     foreach (KexiWindow *window, windowList) {
0267         tristate result = main->closeWindow(window);
0268         if (result != true && result != false) {
0269             return result;
0270         }
0271         if (result == false) {
0272             alternateResult = false;
0273         }
0274     }
0275     return alternateResult;
0276 }
0277 
0278 void KexiMainWindowTabWidget::showContextMenuForTab(int index, const QPoint& point)
0279 {
0280     QMenu menu;
0281     if (index >= 0) {
0282         menu.addAction(m_closeAction);
0283     }
0284     if (count() > 0) {
0285         menu.addAction(m_closeAllTabsAction);
0286     }
0287     //! @todo add "&Detach Tab"
0288     if (menu.actions().isEmpty()) {
0289         return;
0290     }
0291     setTabIndexFromContextMenu(index);
0292     menu.exec(point);
0293 }
0294 
0295 void KexiMainWindowTabWidget::setTabIndexFromContextMenu(int clickedIndex)
0296 {
0297     if (currentIndex() == -1) {
0298         m_tabIndex = -1;
0299         return;
0300     }
0301     m_tabIndex = clickedIndex;
0302 }
0303 
0304 //-------------------------------------------------
0305 
0306 static bool setupIconTheme(KLocalizedString *errorMessage, KLocalizedString *detailsErrorMessage)
0307 {
0308     // Register kexi resource first to have priority over the standard breeze theme.
0309     // For example "table" icon exists in both resources.
0310     if (!registerResource("icons/kexi_breeze.rcc", QStandardPaths::AppDataLocation,
0311                           QString(), QString(), errorMessage, detailsErrorMessage)
0312         || !registerGlobalBreezeIconsResource(errorMessage, detailsErrorMessage))
0313     {
0314         return false;
0315     }
0316     setupBreezeIconTheme();
0317 
0318     // tell KIconLoader an co. about the theme
0319     KConfigGroup cg(KSharedConfig::openConfig(), "Icons");
0320     cg.writeEntry("Theme", "breeze");
0321     cg.sync();
0322     return true;
0323 }
0324 
0325 //! @todo 3.1 replace with KexiStyle
0326 bool setupApplication()
0327 {
0328 #if defined Q_OS_WIN || defined Q_OS_MACOS
0329     // Only this style matches current Kexi theme and can be supported/tested
0330     const char *name = "breeze";
0331     QScopedPointer<QStyle> style(QStyleFactory::create(name));
0332     if (!style || style->objectName() != name) {
0333         qWarning() <<
0334              qPrintable(QString("Could not find application style %1. "
0335                                 "Kexi will not start. Please check if Kexi is properly installed. ")
0336                                 .arg(name));
0337          return false;
0338      }
0339      qApp->setStyle(style.take());
0340 #endif
0341      return true;
0342 }
0343 
0344 //static
0345 int KexiMainWindow::create(const QStringList &arguments, const QString &componentName,
0346                            const QList<QCommandLineOption> &extraOptions)
0347 {
0348     qApp->setQuitOnLastWindowClosed(false);
0349 
0350     KLocalizedString::setApplicationDomain("kexi");
0351     //! @todo KEXI3 app->setAttribute(Qt::AA_UseHighDpiPixmaps, true);
0352 
0353     KexiAboutData aboutData;
0354     if (!componentName.isEmpty()) {
0355         aboutData.setComponentName(componentName);
0356     }
0357     KAboutData::setApplicationData(aboutData);
0358 
0359     if (!setupApplication()) {
0360         return 1;
0361     }
0362 
0363 #ifdef HAVE_KCRASH
0364     KCrash::initialize();
0365 #endif
0366 
0367     KLocalizedString errorMessage;
0368     KLocalizedString detailsErrorMessage;
0369     if (!setupIconTheme(&errorMessage, &detailsErrorMessage)) {
0370         if (detailsErrorMessage.isEmpty()) {
0371             KMessageBox::error(nullptr, errorMessage.toString());
0372         } else {
0373             KMessageBox::detailedError(nullptr, errorMessage.toString(), detailsErrorMessage.toString());
0374         }
0375         qWarning() << qPrintable(errorMessage.toString(Kuit::PlainText));
0376         return 1;
0377     }
0378     QApplication::setWindowIcon(koIcon("kexi"));
0379 
0380     const tristate res = KexiStartupHandler::global()->init(arguments, extraOptions);
0381     if (!res || ~res) {
0382         return (~res) ? 0 : 1;
0383     }
0384     //qDebug() << "startupActions OK";
0385 
0386     /* Exit requested, e.g. after database removing. */
0387     if (KexiStartupHandler::global()->action() == KexiStartupData::Exit) {
0388         return 0;
0389     }
0390 
0391     KexiMainWindow *win = new KexiMainWindow();
0392 #ifdef KEXI_DEBUG_GUI
0393     KConfigGroup generalGroup = KSharedConfig::openConfig()->group("General");
0394     if (generalGroup.readEntry("ShowInternalDebugger", false)) {
0395         QWidget* debugWindow = KexiUtils::createDebugWindow(win);
0396         debugWindow->show();
0397     }
0398 #endif
0399 
0400     if (true != win->startup()) {
0401         delete win;
0402         return 1;
0403     }
0404 
0405     win->restoreSettings();
0406     win->show();
0407 #ifdef KEXI_DEBUG_GUI
0408     win->raise();
0409     static_cast<QWidget*>(win)->activateWindow();
0410 #endif
0411     /*foreach (QWidget *widget, QApplication::topLevelWidgets()) {
0412         qDebug() << widget;
0413     }*/
0414     return 0;
0415 }
0416 
0417 //-------------------------------------------------
0418 
0419 KexiMainMenuActionShortcut::KexiMainMenuActionShortcut(const QKeySequence& key,
0420                                                        QAction *action, QWidget *parent)
0421     : QShortcut(key, parent)
0422     , m_action(action)
0423 {
0424     connect(this, SIGNAL(activated()), this, SLOT(slotActivated()));
0425 }
0426 
0427 KexiMainMenuActionShortcut::~KexiMainMenuActionShortcut()
0428 {
0429 }
0430 
0431 void KexiMainMenuActionShortcut::slotActivated()
0432 {
0433     if (!m_action->isEnabled()) {
0434         return;
0435     }
0436     m_action->trigger();
0437 }
0438 
0439 //-------------------------------------------------
0440 
0441 KexiMainWindow::KexiMainWindow(QWidget *parent)
0442         : KexiMainWindowSuper(parent)
0443         , KexiMainWindowIface()
0444         , KexiGUIMessageHandler(this)
0445         , d(new KexiMainWindow::Private(this))
0446 {
0447     setObjectName("KexiMainWindow");
0448     setAttribute(Qt::WA_DeleteOnClose);
0449     kexiTester() << KexiTestObject(this);
0450 
0451     if (d->userMode)
0452         qDebug() << "starting up in the User Mode";
0453 
0454     setAsDefaultHost(); //this is default host now.
0455 
0456     //get informed
0457     connect(&Kexi::partManager(), SIGNAL(partLoaded(KexiPart::Part*)),
0458             this, SLOT(slotPartLoaded(KexiPart::Part*)));
0459     connect(&Kexi::partManager(), SIGNAL(newObjectRequested(KexiPart::Info*)),
0460             this, SLOT(newObject(KexiPart::Info*)));
0461 
0462     setAcceptDrops(true);
0463     setupActions();
0464     setupMainWidget();
0465     updateAppCaption();
0466 
0467     if (!d->userMode) {
0468         setupContextHelp();
0469         setupPropertyEditor();
0470     }
0471 
0472     invalidateActions();
0473     d->timer.singleShot(0, this, SLOT(slotLastActions()));
0474     if (KexiStartupHandler::global()->forcedFullScreen()) {
0475         toggleFullScreen(true);
0476     }
0477 
0478     // --- global config
0479     //! @todo move to specialized KexiConfig class
0480     KConfigGroup tablesGroup(d->config->group("Tables"));
0481     const int defaultMaxLengthForTextFields = tablesGroup.readEntry("DefaultMaxLengthForTextFields", int(-1));
0482     if (defaultMaxLengthForTextFields >= 0) {
0483         KDbField::setDefaultMaxLength(defaultMaxLengthForTextFields);
0484     }
0485     // --- /global config
0486 }
0487 
0488 KexiMainWindow::~KexiMainWindow()
0489 {
0490     d->forceWindowClosing = true;
0491     closeProject();
0492     delete d;
0493     Kexi::deleteGlobalObjects();
0494 }
0495 
0496 KexiProject *KexiMainWindow::project()
0497 {
0498     return d->prj;
0499 }
0500 
0501 QList<QAction*> KexiMainWindow::allActions() const
0502 {
0503     return actionCollection()->actions();
0504 }
0505 
0506 KActionCollection *KexiMainWindow::actionCollection() const
0507 {
0508     return d->actionCollection;
0509 }
0510 
0511 KexiWindow* KexiMainWindow::currentWindow() const
0512 {
0513     return windowForTab(d->mainWidget->tabWidget()->currentIndex());
0514 }
0515 
0516 KexiWindow* KexiMainWindow::windowForTab(int tabIndex) const
0517 {
0518     if (!d->mainWidget->tabWidget())
0519         return 0;
0520     KexiWindowContainer *windowContainer
0521         = dynamic_cast<KexiWindowContainer*>(d->mainWidget->tabWidget()->widget(tabIndex));
0522     if (!windowContainer)
0523         return 0;
0524     return windowContainer->window;
0525 }
0526 
0527 void KexiMainWindow::setupMainMenuActionShortcut(QAction * action)
0528 {
0529     if (!action->shortcut().isEmpty()) {
0530         foreach(const QKeySequence &shortcut, action->shortcuts()) {
0531             (void)new KexiMainMenuActionShortcut(shortcut, action, this);
0532         }
0533     }
0534 }
0535 
0536 static void addThreeDotsToActionText(QAction* action)
0537 {
0538     action->setText(xi18nc("Action name with three dots...", "%1...", action->text()));
0539 }
0540 
0541 QAction * KexiMainWindow::addAction(const char *name, const QIcon &icon, const QString& text,
0542                                    const char *shortcut)
0543 {
0544     QAction *action = icon.isNull() ? new QAction(text, this) : new QAction(icon, text, this);
0545     actionCollection()->addAction(name, action);
0546     if (shortcut) {
0547         action->setShortcut(QKeySequence(shortcut));
0548         QShortcut *s = new QShortcut(action->shortcut(), this);
0549         connect(s, SIGNAL(activated()), action, SLOT(trigger()));
0550     }
0551     return action;
0552 }
0553 
0554 QAction * KexiMainWindow::addAction(const char *name, const QString& text, const char *shortcut)
0555 {
0556     return addAction(name, QIcon(), text, shortcut);
0557 }
0558 
0559 void KexiMainWindow::setupActions()
0560 {
0561     KActionCollection *ac = actionCollection();
0562 
0563     // PROJECT MENU
0564     QAction *action;
0565 
0566     ac->addAction("project_new",
0567         action = new KexiMenuWidgetAction(KStandardAction::New, this));
0568     addThreeDotsToActionText(action);
0569     action->setShortcuts(KStandardShortcut::openNew());
0570     action->setToolTip(xi18n("Create a new project"));
0571     action->setWhatsThis(
0572         xi18n("Creates a new project. Currently opened project is not affected."));
0573     connect(action, SIGNAL(triggered()), this, SLOT(slotProjectNew()));
0574     setupMainMenuActionShortcut(action);
0575 
0576     ac->addAction("project_open",
0577             action = new KexiMenuWidgetAction(KStandardAction::Open, this));
0578     action->setToolTip(xi18n("Open an existing project"));
0579     action->setWhatsThis(
0580         xi18n("Opens an existing project. Currently opened project is not affected."));
0581     connect(action, SIGNAL(triggered()), this, SLOT(slotProjectOpen()));
0582     setupMainMenuActionShortcut(action);
0583 
0584     {
0585         ac->addAction("project_welcome",
0586             action = d->action_project_welcome = new KexiMenuWidgetAction(
0587                 QIcon(), xi18n("Welcome"), this));
0588             addThreeDotsToActionText(action);
0589         connect(action, SIGNAL(triggered()), this, SLOT(slotProjectWelcome()));
0590         setupMainMenuActionShortcut(action);
0591         action->setToolTip(xi18n("Show Welcome page"));
0592         action->setWhatsThis(
0593             xi18n("Shows Welcome page with list of recently opened projects and other information. "));
0594     }
0595 
0596     ac->addAction("project_save",
0597                   d->action_save = KStandardAction::save(this, SLOT(slotProjectSave()), this));
0598     d->action_save->setToolTip(xi18n("Save object changes"));
0599     d->action_save->setWhatsThis(xi18n("Saves object changes from currently selected window."));
0600     setupMainMenuActionShortcut(d->action_save);
0601 
0602     d->action_save_as = addAction("project_saveas", koIcon("document-save-as"),
0603                                   xi18n("Save &As..."));
0604     d->action_save_as->setToolTip(xi18n("Save object as"));
0605     d->action_save_as->setWhatsThis(
0606         xi18n("Saves object from currently selected window under a new name (within the same project)."));
0607     connect(d->action_save_as, SIGNAL(triggered()), this, SLOT(slotProjectSaveAs()));
0608 
0609 #ifdef KEXI_SHOW_UNIMPLEMENTED
0610     ac->addAction("project_properties",
0611         action = d->action_project_properties = new KexiMenuWidgetAction(
0612             koIcon("document-properties"), futureI18n("Project Properties"), this));
0613     connect(action, SIGNAL(triggered()), this, SLOT(slotProjectProperties()));
0614     setupMainMenuActionShortcut(action);
0615 #else
0616     d->action_project_properties = d->dummy_action;
0617 #endif
0618 
0619     //! @todo replace document-import icon with something other
0620     ac->addAction("project_import_export_send",
0621         action = d->action_project_import_export_send = new KexiMenuWidgetAction(
0622             koIcon("document-import"), xi18n("&Import, Export or Send..."), this));
0623     action->setToolTip(xi18n("Import, export or send project"));
0624     action->setWhatsThis(
0625         xi18n("Imports, exports or sends project."));
0626     connect(action, SIGNAL(triggered()), this, SLOT(slotProjectImportExportOrSend()));
0627     setupMainMenuActionShortcut(action);
0628 
0629     ac->addAction("project_close",
0630         action = d->action_close = new KexiMenuWidgetAction(
0631             koIcon("window-close"), xi18nc("Close Project", "&Close"), this));
0632     action->setToolTip(xi18n("Close the current project"));
0633     action->setWhatsThis(xi18n("Closes the current project."));
0634     connect(action, SIGNAL(triggered()), this, SLOT(slotProjectClose()));
0635     setupMainMenuActionShortcut(action);
0636 
0637     ac->addAction("quit",
0638                   action = new KexiMenuWidgetAction(KStandardAction::Quit, this));
0639     connect(action, SIGNAL(triggered()), this, SLOT(slotProjectQuit()));
0640     action->setWhatsThis(xi18n("Quits Kexi application."));
0641     setupMainMenuActionShortcut(action);
0642 
0643 #ifdef KEXI_SHOW_UNIMPLEMENTED
0644     d->action_project_relations = addAction("project_relations", KexiIcon("database-relations"),
0645                                             futureI18n("&Relationships..."), "Ctrl+R");
0646     d->action_project_relations->setToolTip(futureI18n("Project relationships"));
0647     d->action_project_relations->setWhatsThis(futureI18n("Shows project relationships."));
0648     connect(d->action_project_relations, SIGNAL(triggered()),
0649             this, SLOT(slotProjectRelations()));
0650 
0651 #else
0652     d->action_project_relations = d->dummy_action;
0653 #endif
0654     d->action_tools_import_project = addAction("tools_import_project", KexiIcon("database-import"),
0655                                                xi18n("&Import Database..."));
0656     d->action_tools_import_project->setToolTip(xi18n("Import entire database as a Kexi project"));
0657     d->action_tools_import_project->setWhatsThis(
0658         xi18n("Imports entire database as a Kexi project."));
0659     connect(d->action_tools_import_project, SIGNAL(triggered()),
0660             this, SLOT(slotToolsImportProject()));
0661 
0662     d->action_tools_data_import = addAction("tools_import_tables", koIcon("document-import"),
0663                                             xi18n("Import Tables..."));
0664     d->action_tools_data_import->setToolTip(xi18n("Import data from an external source into this project"));
0665     d->action_tools_data_import->setWhatsThis(xi18n("Imports data from an external source into this project."));
0666     connect(d->action_tools_data_import, SIGNAL(triggered()), this, SLOT(slotToolsImportTables()));
0667 
0668     d->action_tools_compact_database = addAction("tools_compact_database",
0669 //! @todo icon
0670                                                  koIcon("application-x-compress"),
0671                                                  xi18n("&Compact Database..."));
0672     d->action_tools_compact_database->setToolTip(xi18n("Compact the current database project"));
0673     d->action_tools_compact_database->setWhatsThis(
0674         xi18n("Compacts the current database project, so it will take less space and work faster."));
0675     connect(d->action_tools_compact_database, SIGNAL(triggered()),
0676             this, SLOT(slotToolsCompactDatabase()));
0677 
0678     if (d->userMode)
0679         d->action_project_import_data_table = 0;
0680     else {
0681         d->action_project_import_data_table = addAction("project_import_data_table",
0682             KexiIcon("document-empty"),
0683             /*! @todo: change to "file_import" with a table or so */
0684             xi18nc("Import->Table Data From File...", "Import Data From &File..."));
0685         d->action_project_import_data_table->setToolTip(xi18n("Import table data from a file"));
0686         d->action_project_import_data_table->setWhatsThis(xi18n("Imports table data from a file."));
0687         connect(d->action_project_import_data_table, SIGNAL(triggered()),
0688                 this, SLOT(slotProjectImportDataTable()));
0689     }
0690 
0691     d->action_project_export_data_table = addAction("project_export_data_table",
0692         KexiIcon("table"),
0693         /*! @todo: change to "file_export" with a table or so */
0694         xi18nc("Export->Table or Query Data to File...", "Export Data to &File..."));
0695     d->action_project_export_data_table->setToolTip(
0696         xi18n("Export data from the active table or query to a file"));
0697     d->action_project_export_data_table->setWhatsThis(
0698         xi18n("Exports data from the active table or query to a file."));
0699     connect(d->action_project_export_data_table, SIGNAL(triggered()),
0700             this, SLOT(slotProjectExportDataTable()));
0701 
0702 //! @todo new QAction(xi18n("From File..."), "document-open", 0,
0703 //!          this, SLOT(slotImportFile()), actionCollection(), "project_import_file");
0704 //! @todo new QAction(xi18n("From Server..."), "network-server-database", 0,
0705 //!          this, SLOT(slotImportServer()), actionCollection(), "project_import_server");
0706 
0707 #ifdef KEXI_QUICK_PRINTING_SUPPORT
0708     ac->addAction("project_print",
0709                   d->action_project_print = KStandardAction::print(this, SLOT(slotProjectPrint()), this));
0710     d->action_project_print->setToolTip(futureI18n("Print data from the active table or query"));
0711     d->action_project_print->setWhatsThis(futureI18n("Prints data from the active table or query."));
0712 
0713     ac->addAction("project_print_preview",
0714                   d->action_project_print_preview = KStandardAction::printPreview(
0715                                                         this, SLOT(slotProjectPrintPreview()), this));
0716     d->action_project_print_preview->setToolTip(
0717         futureI18n("Show print preview for the active table or query"));
0718     d->action_project_print_preview->setWhatsThis(
0719         futureI18n("Shows print preview for the active table or query."));
0720 
0721     d->action_project_print_setup = addAction("project_print_setup",
0722         koIcon("configure"), futureI18n("Print Set&up...")); //!< @todo document-page-setup could be a better icon
0723     d->action_project_print_setup->setToolTip(
0724         futureI18n("Show print setup for the active table or query"));
0725     d->action_project_print_setup->setWhatsThis(
0726         futureI18n("Shows print setup for the active table or query."));
0727     connect(d->action_project_print_setup, SIGNAL(triggered()),
0728             this, SLOT(slotProjectPageSetup()));
0729 #endif
0730 
0731     //EDIT MENU
0732     d->action_edit_cut = createSharedAction(KStandardAction::Cut);
0733     d->action_edit_copy = createSharedAction(KStandardAction::Copy);
0734     d->action_edit_paste = createSharedAction(KStandardAction::Paste);
0735 
0736     if (d->userMode)
0737         d->action_edit_paste_special_data_table = 0;
0738     else {
0739         d->action_edit_paste_special_data_table = addAction(
0740             "edit_paste_special_data_table",
0741             d->action_edit_paste->icon(), xi18nc("Paste Special->As Data &Table...", "Paste Special..."));
0742         d->action_edit_paste_special_data_table->setToolTip(
0743             xi18n("Paste clipboard data as a table"));
0744         d->action_edit_paste_special_data_table->setWhatsThis(
0745             xi18n("Pastes clipboard data as a table."));
0746         connect(d->action_edit_paste_special_data_table, SIGNAL(triggered()),
0747                 this, SLOT(slotEditPasteSpecialDataTable()));
0748     }
0749 
0750     d->action_edit_copy_special_data_table = addAction(
0751         "edit_copy_special_data_table",
0752         KexiIcon("table"), xi18nc("Copy Special->Table or Query Data...", "Copy Special..."));
0753     d->action_edit_copy_special_data_table->setToolTip(
0754         xi18n("Copy selected table or query data to clipboard"));
0755     d->action_edit_copy_special_data_table->setWhatsThis(
0756         xi18n("Copies selected table or query data to clipboard."));
0757     connect(d->action_edit_copy_special_data_table, SIGNAL(triggered()),
0758             this, SLOT(slotEditCopySpecialDataTable()));
0759 
0760     d->action_edit_undo = createSharedAction(KStandardAction::Undo);
0761     d->action_edit_undo->setWhatsThis(xi18n("Reverts the most recent editing action."));
0762     d->action_edit_redo = createSharedAction(KStandardAction::Redo);
0763     d->action_edit_redo->setWhatsThis(xi18n("Reverts the most recent undo action."));
0764 
0765     ac->addAction("edit_find",
0766                   d->action_edit_find = KStandardAction::find(
0767                                             this, SLOT(slotEditFind()), this));
0768     d->action_edit_find->setToolTip(xi18n("Find text"));
0769     d->action_edit_find->setWhatsThis(xi18n("Looks up the first occurrence of a piece of text."));
0770     ac->addAction("edit_findnext",
0771                   d->action_edit_findnext = KStandardAction::findNext(
0772                                                 this, SLOT(slotEditFindNext()), this));
0773     ac->addAction("edit_findprevious",
0774                   d->action_edit_findprev = KStandardAction::findPrev(
0775                                                 this, SLOT(slotEditFindPrevious()), this));
0776     d->action_edit_replace = 0;
0777 //! @todo d->action_edit_replace = KStandardAction::replace(
0778 //!  this, SLOT(slotEditReplace()), actionCollection(), "project_print_preview" );
0779     d->action_edit_replace_all = 0;
0780 //! @todo d->action_edit_replace_all = new QAction( xi18n("Replace All"), "", 0,
0781 //!   this, SLOT(slotEditReplaceAll()), actionCollection(), "edit_replaceall");
0782 
0783     d->action_edit_select_all =  createSharedAction(KStandardAction::SelectAll);
0784 
0785     d->action_edit_delete = createSharedAction(xi18n("&Delete"), koIconName("edit-delete"),
0786                             QKeySequence(), "edit_delete");
0787     d->action_edit_delete->setToolTip(xi18n("Delete selected object"));
0788     d->action_edit_delete->setWhatsThis(xi18n("Deletes currently selected object."));
0789 
0790     d->action_edit_delete_row = createSharedAction(xi18n("Delete Record"), KexiIconName("edit-table-delete-row"),
0791                                 QKeySequence(Qt::CTRL + Qt::Key_Delete), "edit_delete_row");
0792     d->action_edit_delete_row->setToolTip(xi18n("Delete the current record"));
0793     d->action_edit_delete_row->setWhatsThis(xi18n("Deletes the current record."));
0794 
0795     d->action_edit_clear_table = createSharedAction(xi18n("Clear Table Contents..."),
0796                                  KexiIconName("edit-table-clear"), QKeySequence(), "edit_clear_table");
0797     d->action_edit_clear_table->setToolTip(xi18n("Clear table contents"));
0798     d->action_edit_clear_table->setWhatsThis(xi18n("Clears table contents."));
0799     setActionVolatile(d->action_edit_clear_table, true);
0800 
0801     d->action_edit_edititem = createSharedAction(xi18n("Edit Item"), QString(),
0802                               QKeySequence(), /* CONFLICT in TV: Qt::Key_F2,  */
0803                               "edit_edititem");
0804     d->action_edit_edititem->setToolTip(xi18n("Edit currently selected item"));
0805     d->action_edit_edititem->setWhatsThis(xi18n("Edits currently selected item."));
0806 
0807     d->action_edit_insert_empty_row = createSharedAction(xi18n("&Insert Empty Row"),
0808                                       KexiIconName("edit-table-insert-row"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Insert),
0809                                       "edit_insert_empty_row");
0810     setActionVolatile(d->action_edit_insert_empty_row, true);
0811     d->action_edit_insert_empty_row->setToolTip(xi18n("Insert one empty row above"));
0812     d->action_edit_insert_empty_row->setWhatsThis(
0813         xi18n("Inserts one empty row above currently selected table row."));
0814 
0815     //VIEW MENU
0816     /* UNUSED, see KexiToggleViewModeAction
0817       if (!d->userMode) {
0818         d->action_view_mode = new QActionGroup(this);
0819         ac->addAction( "view_data_mode",
0820           d->action_view_data_mode = new KToggleAction(
0821             KexiIcon("data-view"), xi18n("&Data View"), d->action_view_mode) );
0822     //  d->action_view_data_mode->setObjectName("view_data_mode");
0823         d->action_view_data_mode->setShortcut(QKeySequence("F6"));
0824         //d->action_view_data_mode->setExclusiveGroup("view_mode");
0825         d->action_view_data_mode->setToolTip(xi18n("Switch to data view"));
0826         d->action_view_data_mode->setWhatsThis(xi18n("Switches to data view."));
0827         d->actions_for_view_modes.insert( Kexi::DataViewMode, d->action_view_data_mode );
0828         connect(d->action_view_data_mode, SIGNAL(triggered()),
0829           this, SLOT(slotViewDataMode()));
0830       }
0831       else {
0832         d->action_view_mode = 0;
0833         d->action_view_data_mode = 0;
0834       }
0835 
0836       if (!d->userMode) {
0837         ac->addAction( "view_design_mode",
0838           d->action_view_design_mode = new KToggleAction(
0839             KexiIcon("design-view"), xi18n("D&esign View"), d->action_view_mode) );
0840     //  d->action_view_design_mode->setObjectName("view_design_mode");
0841         d->action_view_design_mode->setShortcut(QKeySequence("F7"));
0842         //d->action_view_design_mode->setExclusiveGroup("view_mode");
0843         d->action_view_design_mode->setToolTip(xi18n("Switch to design view"));
0844         d->action_view_design_mode->setWhatsThis(xi18n("Switches to design view."));
0845         d->actions_for_view_modes.insert( Kexi::DesignViewMode, d->action_view_design_mode );
0846         connect(d->action_view_design_mode, SIGNAL(triggered()),
0847           this, SLOT(slotViewDesignMode()));
0848       }
0849       else
0850         d->action_view_design_mode = 0;
0851 
0852       if (!d->userMode) {
0853         ac->addAction( "view_text_mode",
0854           d->action_view_text_mode = new KToggleAction(
0855             KexiIcon("sql-view"), xi18n("&Text View"), d->action_view_mode) );
0856         d->action_view_text_mode->setObjectName("view_text_mode");
0857         d->action_view_text_mode->setShortcut(QKeySequence("F8"));
0858         //d->action_view_text_mode->setExclusiveGroup("view_mode");
0859         d->action_view_text_mode->setToolTip(xi18n("Switch to text view"));
0860         d->action_view_text_mode->setWhatsThis(xi18n("Switches to text view."));
0861         d->actions_for_view_modes.insert( Kexi::TextViewMode, d->action_view_text_mode );
0862         connect(d->action_view_text_mode, SIGNAL(triggered()),
0863           this, SLOT(slotViewTextMode()));
0864       }
0865       else
0866         d->action_view_text_mode = 0;
0867     */
0868     if (d->isProjectNavigatorVisible) {
0869         d->action_show_nav = addAction("view_navigator",
0870                                        xi18n("Show Project Navigator"),
0871                                        "Alt+0");
0872         d->action_show_nav->setToolTip(xi18n("Show the Project Navigator pane"));
0873         d->action_show_nav->setWhatsThis(xi18n("Shows the Project Navigator pane."));
0874         connect(d->action_show_nav, SIGNAL(triggered()),
0875                 this, SLOT(slotShowNavigator()));
0876     } else {
0877         d->action_show_nav = 0;
0878     }
0879 
0880     if (d->isProjectNavigatorVisible) {
0881         // Shortcut taken from "Activate Projects pane" https://doc.qt.io/qtcreator/creator-keyboard-shortcuts.html
0882         d->action_activate_nav = addAction("activate_navigator",
0883                                        xi18n("Activate Project Navigator"),
0884                                        "Alt+X");
0885         d->action_activate_nav->setToolTip(xi18n("Activate the Project Navigator pane"));
0886         d->action_activate_nav->setWhatsThis(xi18n("Activates the Project Navigator pane. If it is hidden, shows it first."));
0887         connect(d->action_activate_nav, SIGNAL(triggered()),
0888                 this, SLOT(slotActivateNavigator()));
0889     } else {
0890         d->action_activate_nav = 0;
0891     }
0892 
0893     d->action_activate_mainarea = addAction("activate_mainarea",
0894                                         xi18n("Activate main area")
0895 // , "Alt+2"?
0896 //! @todo activate_mainarea: pressing Esc in project nav or propeditor should move back to the main area
0897                                         );
0898     d->action_activate_mainarea->setToolTip(xi18n("Activate the main area"));
0899     d->action_activate_mainarea->setWhatsThis(xi18n("Activates the main area."));
0900     connect(d->action_activate_mainarea, SIGNAL(triggered()),
0901             this, SLOT(slotActivateMainArea()));
0902 
0903     //! @todo windows with "_3" prefix have conflicting auto shortcut set to Alt+3 -> remove that!
0904     if (!d->userMode) {
0905         d->action_show_propeditor = addAction("view_propeditor",
0906                                               xi18n("Show Property Editor"), "Alt+3");
0907         d->action_show_propeditor->setToolTip(xi18n("Show the Property Editor pane"));
0908         d->action_show_propeditor->setWhatsThis(xi18n("Shows the Property Editor pane."));
0909         connect(d->action_show_propeditor, SIGNAL(triggered()),
0910                 this, SLOT(slotShowPropertyEditor()));
0911     } else {
0912         d->action_show_propeditor = 0;
0913     }
0914 
0915     if (!d->userMode) {
0916         d->action_activate_propeditor = addAction("activate_propeditor",
0917                                               xi18n("Activate Property Editor"), "Alt+-");
0918         d->action_activate_propeditor->setToolTip(xi18n("Activate the Property Editor pane"));
0919         d->action_activate_propeditor->setWhatsThis(xi18n("Activates the Property Editor pane. If it is hidden, shows it first."));
0920         connect(d->action_activate_propeditor, SIGNAL(triggered()),
0921                 this, SLOT(slotActivatePropertyEditor()));
0922     } else {
0923         d->action_activate_propeditor = 0;
0924     }
0925 
0926     d->action_view_global_search = addAction("view_global_search",
0927                                              xi18n("Switch to Global Search"), "Ctrl+K");
0928     d->action_view_global_search->setToolTip(xi18n("Switch to Global Search box"));
0929     d->action_view_global_search->setWhatsThis(xi18n("Switches to Global Search box."));
0930     // (connection is added elsewhere)
0931 
0932     //DATA MENU
0933     d->action_data_save_row = createSharedAction(xi18n("&Save Record"), koIconName("dialog-ok"),
0934                               QKeySequence(Qt::SHIFT + Qt::Key_Return), "data_save_row");
0935     d->action_data_save_row->setToolTip(xi18n("Save changes made to the current record"));
0936     d->action_data_save_row->setWhatsThis(xi18n("Saves changes made to the current record."));
0937 //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_save_row, true );
0938 
0939     d->action_data_cancel_row_changes = createSharedAction(xi18n("&Cancel Record Changes"),
0940                                         koIconName("dialog-cancel"), QKeySequence(Qt::Key_Escape), "data_cancel_row_changes");
0941     d->action_data_cancel_row_changes->setToolTip(
0942         xi18n("Cancel changes made to the current record"));
0943     d->action_data_cancel_row_changes->setWhatsThis(
0944         xi18n("Cancels changes made to the current record."));
0945 //temp. disable because of problems with volatile actions setActionVolatile( d->action_data_cancel_row_changes, true );
0946 
0947     d->action_data_execute = createSharedAction(
0948                                  xi18n("&Execute"), koIconName("media-playback-start"), QKeySequence(), "data_execute");
0949     //! @todo d->action_data_execute->setToolTip(xi18n(""));
0950     //! @todo d->action_data_execute->setWhatsThis(xi18n(""));
0951 
0952 #ifdef KEXI_SHOW_UNIMPLEMENTED
0953     action = createSharedAction(futureI18n("&Filter"), koIconName("view-filter"), QKeySequence(), "data_filter");
0954     setActionVolatile(action, true);
0955 #endif
0956 //! @todo action->setToolTip(xi18n(""));
0957 //! @todo action->setWhatsThis(xi18n(""));
0958 
0959     // - record-navigation related actions
0960     createSharedAction(KexiRecordNavigator::Actions::moveToFirstRecord(), QKeySequence(), "data_go_to_first_record");
0961     createSharedAction(KexiRecordNavigator::Actions::moveToPreviousRecord(), QKeySequence(), "data_go_to_previous_record");
0962     createSharedAction(KexiRecordNavigator::Actions::moveToNextRecord(), QKeySequence(), "data_go_to_next_record");
0963     createSharedAction(KexiRecordNavigator::Actions::moveToLastRecord(), QKeySequence(), "data_go_to_last_record");
0964     createSharedAction(KexiRecordNavigator::Actions::moveToNewRecord(), QKeySequence(), "data_go_to_new_record");
0965 
0966     //FORMAT MENU
0967     d->action_format_font = createSharedAction(xi18n("&Font..."), koIconName("fonts-package"),
0968                             QKeySequence(), "format_font");
0969     d->action_format_font->setToolTip(xi18n("Change font for selected object"));
0970     d->action_format_font->setWhatsThis(xi18n("Changes font for selected object."));
0971 
0972     //TOOLS MENU
0973 
0974     // WINDOW MENU
0975     // additional 'Window' menu items
0976     d->action_window_next = addAction("window_next", xi18n("&Next Window"), "Alt+Right");
0977     d->action_window_next->setToolTip(xi18n("Next window"));
0978     d->action_window_next->setWhatsThis(xi18n("Switches to the next window."));
0979     connect(d->action_window_next, SIGNAL(triggered()),
0980             this, SLOT(activateNextWindow()));
0981 
0982     d->action_window_previous = addAction("window_previous", xi18n("&Previous Window"), "Alt+Left");
0983     d->action_window_previous->setToolTip(xi18n("Previous window"));
0984     d->action_window_previous->setWhatsThis(xi18n("Switches to the previous window."));
0985     connect(d->action_window_previous, SIGNAL(triggered()),
0986             this, SLOT(activatePreviousWindow()));
0987 
0988     d->action_tab_next = addAction("tab_next", futureI18n("&Next Tab"), "Ctrl+Tab");
0989     d->action_tab_next->setToolTip(futureI18n("Next tab"));
0990     d->action_tab_next->setWhatsThis(futureI18n("Switches to the next tab."));
0991     connect(d->action_tab_next, &QAction::triggered, this, &KexiMainWindow::activateNextTab);
0992 
0993     d->action_tab_previous = addAction("tab_previous", futureI18n("&Previous Tab"), "Ctrl+Shift+Tab");
0994     d->action_tab_previous->setToolTip(futureI18n("Previous tab"));
0995     d->action_tab_previous->setWhatsThis(futureI18n("Switches to the previous tab."));
0996     connect(d->action_tab_previous, &QAction::triggered, this, &KexiMainWindow::activatePreviousTab);
0997 
0998     d->action_window_fullscreen = KStandardAction::fullScreen(this, SLOT(toggleFullScreen(bool)), this, ac);
0999     ac->addAction("full_screen", d->action_window_fullscreen);
1000     QList<QKeySequence> shortcuts;
1001     shortcuts << d->action_window_fullscreen->shortcut() << QKeySequence("F11");
1002     d->action_window_fullscreen->setShortcuts(shortcuts);
1003     QShortcut *s = new QShortcut(d->action_window_fullscreen->shortcut(), this);
1004     connect(s, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger()));
1005     if (d->action_window_fullscreen->shortcuts().count() > 1) {
1006         QShortcut *sa = new QShortcut(d->action_window_fullscreen->shortcuts().value(1), this);
1007         connect(sa, SIGNAL(activated()), d->action_window_fullscreen, SLOT(trigger()));
1008     }
1009 
1010     //SETTINGS MENU
1011 //! @todo put 'configure keys' into settings view
1012 
1013 #ifdef KEXI_SHOW_UNIMPLEMENTED
1014     //! @todo toolbars configuration will be handled in a special way
1015 #endif
1016 
1017 #ifdef KEXI_MACROS_SUPPORT
1018     Kexi::tempShowMacros() = true;
1019 #else
1020     Kexi::tempShowMacros() = false;
1021 #endif
1022 
1023 #ifdef KEXI_SCRIPTS_SUPPORT
1024     Kexi::tempShowScripts() = true;
1025 #else
1026     Kexi::tempShowScripts() = false;
1027 #endif
1028 
1029 #ifdef KEXI_SHOW_UNIMPLEMENTED
1030 //! @todo implement settings window in a specific way
1031     ac->addAction("settings",
1032                   action = d->action_settings = new KexiMenuWidgetAction(
1033                     KStandardAction::Preferences, this));
1034     action->setObjectName("settings");
1035     action->setText(futureI18n("Settings..."));
1036     action->setToolTip(futureI18n("Show Kexi settings"));
1037     action->setWhatsThis(futureI18n("Shows Kexi settings."));
1038     connect(action, SIGNAL(triggered()), this, SLOT(slotSettings()));
1039     setupMainMenuActionShortcut(action);
1040 #else
1041     d->action_settings = d->dummy_action;
1042 #endif
1043 
1044 //! @todo reenable 'tip of the day' later
1045 #if 0
1046     KStandardAction::tipOfDay(this, SLOT(slotTipOfTheDayAction()), actionCollection())
1047     ->setWhatsThis(xi18n("This shows useful tips on the use of this application."));
1048 #endif
1049 
1050     // GLOBAL
1051     d->action_show_help_menu = addAction("help_show_menu", xi18nc("Help Menu", "Help"), "Alt+H");
1052     d->action_show_help_menu->setToolTip(xi18n("Show Help menu"));
1053     d->action_show_help_menu->setWhatsThis(xi18n("Shows Help menu."));
1054     // (connection is added elsewhere)
1055 
1056     // ----- declare action categories, so form's "assign action to button"
1057     //       (and macros in the future) will be able to recognize category
1058     //       of actions and filter them -----------------------------------
1059 //! @todo shouldn't we move this to core?
1060     Kexi::ActionCategories *acat = Kexi::actionCategories();
1061     acat->addAction("data_execute", Kexi::PartItemActionCategory);
1062 
1063     //! @todo unused for now
1064     acat->addWindowAction("data_filter",
1065                           KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1066 
1067     acat->addWindowAction("data_save_row",
1068                           KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1069 
1070     acat->addWindowAction("data_cancel_row_changes",
1071                           KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1072 
1073     acat->addWindowAction("delete_table_row",
1074                           KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1075 
1076     //! @todo support this in KexiPart::FormObjectType as well
1077     acat->addWindowAction("data_sort_az",
1078                           KexiPart::TableObjectType, KexiPart::QueryObjectType);
1079 
1080     //! @todo support this in KexiPart::FormObjectType as well
1081     acat->addWindowAction("data_sort_za",
1082                           KexiPart::TableObjectType, KexiPart::QueryObjectType);
1083 
1084     //! @todo support this in KexiPart::FormObjectType as well
1085     acat->addWindowAction("edit_clear_table",
1086                           KexiPart::TableObjectType, KexiPart::QueryObjectType);
1087 
1088     //! @todo support this in KexiPart::FormObjectType as well
1089     acat->addWindowAction("edit_copy_special_data_table",
1090                           KexiPart::TableObjectType, KexiPart::QueryObjectType);
1091 
1092     //! @todo support this in FormObjectType as well
1093     acat->addWindowAction("project_export_data_table",
1094                           KexiPart::TableObjectType, KexiPart::QueryObjectType);
1095 
1096     // GlobalActions, etc.
1097     acat->addAction("edit_copy", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory);
1098 
1099     acat->addAction("edit_cut", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory);
1100 
1101     acat->addAction("edit_paste", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory);
1102 
1103     acat->addAction("edit_delete", Kexi::GlobalActionCategory | Kexi::PartItemActionCategory | Kexi::WindowActionCategory,
1104                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1105 
1106     acat->addAction("edit_delete_row", Kexi::GlobalActionCategory | Kexi::WindowActionCategory,
1107                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1108 
1109     acat->addAction("edit_edititem", Kexi::PartItemActionCategory | Kexi::WindowActionCategory,
1110                     KexiPart::TableObjectType, KexiPart::QueryObjectType);
1111 
1112     acat->addAction("edit_find", Kexi::GlobalActionCategory | Kexi::WindowActionCategory,
1113                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1114 
1115     acat->addAction("edit_findnext", Kexi::GlobalActionCategory | Kexi::WindowActionCategory,
1116                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1117 
1118     acat->addAction("edit_findprevious", Kexi::GlobalActionCategory | Kexi::WindowActionCategory,
1119                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1120 
1121     acat->addAction("edit_replace", Kexi::GlobalActionCategory | Kexi::WindowActionCategory,
1122                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1123 
1124     acat->addAction("edit_paste_special_data_table", Kexi::GlobalActionCategory);
1125 
1126     acat->addAction("help_about_app", Kexi::GlobalActionCategory);
1127 
1128     acat->addAction("help_about_kde", Kexi::GlobalActionCategory);
1129 
1130     acat->addAction("help_contents", Kexi::GlobalActionCategory);
1131 
1132     acat->addAction("help_report_bug", Kexi::GlobalActionCategory);
1133 
1134     acat->addAction("help_whats_this", Kexi::GlobalActionCategory);
1135 
1136     acat->addAction("help_donate", Kexi::GlobalActionCategory); // disabled for now
1137 
1138     acat->addAction("switch_application_language", Kexi::GlobalActionCategory);
1139 
1140     acat->addAction("options_configure_keybinding", Kexi::GlobalActionCategory);
1141 
1142     acat->addAction("project_close", Kexi::GlobalActionCategory);
1143 
1144     acat->addAction("project_import_data_table", Kexi::GlobalActionCategory);
1145 
1146     acat->addAction("project_new", Kexi::GlobalActionCategory);
1147 
1148     acat->addAction("project_open", Kexi::GlobalActionCategory);
1149 
1150 #ifdef KEXI_QUICK_PRINTING_SUPPORT
1151     //! @todo support this in FormObjectType, ReportObjectType as well as others
1152     acat->addAction("project_print", Kexi::WindowActionCategory,
1153                     KexiPart::TableObjectType, KexiPart::QueryObjectType);
1154 
1155     //! @todo support this in FormObjectType, ReportObjectType as well as others
1156     acat->addAction("project_print_preview", Kexi::WindowActionCategory,
1157                     KexiPart::TableObjectType, KexiPart::QueryObjectType);
1158 
1159     //! @todo support this in FormObjectType, ReportObjectType as well as others
1160     acat->addAction("project_print_setup", Kexi::WindowActionCategory,
1161                     KexiPart::TableObjectType, KexiPart::QueryObjectType);
1162 #endif
1163 
1164     acat->addAction("quit", Kexi::GlobalActionCategory);
1165 
1166     acat->addAction("tools_compact_database", Kexi::GlobalActionCategory);
1167 
1168     acat->addAction("tools_import_project", Kexi::GlobalActionCategory);
1169 
1170     acat->addAction("tools_import_tables", Kexi::GlobalActionCategory);
1171 
1172     acat->addAction("view_data_mode", Kexi::GlobalActionCategory);
1173 
1174     acat->addAction("view_design_mode", Kexi::GlobalActionCategory);
1175 
1176     acat->addAction("view_text_mode", Kexi::GlobalActionCategory);
1177 
1178     acat->addAction("view_mainarea", Kexi::GlobalActionCategory);
1179 
1180     acat->addAction("view_navigator", Kexi::GlobalActionCategory);
1181 
1182     acat->addAction("activate_navigator", Kexi::GlobalActionCategory);
1183 
1184     acat->addAction("view_propeditor", Kexi::GlobalActionCategory);
1185 
1186     acat->addAction("activate_mainarea", Kexi::GlobalActionCategory);
1187 
1188     acat->addAction("activate_propeditor", Kexi::GlobalActionCategory);
1189 
1190     acat->addAction("window_close", Kexi::GlobalActionCategory | Kexi::WindowActionCategory);
1191     acat->setAllObjectTypesSupported("window_close", true);
1192 
1193     acat->addAction("window_next", Kexi::GlobalActionCategory);
1194 
1195     acat->addAction("window_previous", Kexi::GlobalActionCategory);
1196 
1197     acat->addAction("full_screen", Kexi::GlobalActionCategory);
1198 
1199     //skipped - design view only
1200     acat->addAction("format_font", Kexi::NoActionCategory);
1201     acat->addAction("project_save", Kexi::NoActionCategory);
1202     acat->addAction("edit_insert_empty_row", Kexi::NoActionCategory);
1203     //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType later
1204     acat->addAction("edit_select_all", Kexi::NoActionCategory);
1205     //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later
1206     acat->addAction("edit_redo", Kexi::NoActionCategory);
1207     //! @todo support this in KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType later
1208     acat->addAction("edit_undo", Kexi::NoActionCategory);
1209 
1210     //record-navigation related actions
1211     acat->addAction("data_go_to_first_record", Kexi::WindowActionCategory,
1212                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1213     acat->addAction("data_go_to_previous_record", Kexi::WindowActionCategory,
1214                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1215     acat->addAction("data_go_to_next_record", Kexi::WindowActionCategory,
1216                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1217     acat->addAction("data_go_to_last_record", Kexi::WindowActionCategory,
1218                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1219     acat->addAction("data_go_to_new_record", Kexi::WindowActionCategory,
1220                     KexiPart::TableObjectType, KexiPart::QueryObjectType, KexiPart::FormObjectType);
1221 
1222     //skipped - internal:
1223     acat->addAction("tablepart_create", Kexi::NoActionCategory);
1224     acat->addAction("querypart_create", Kexi::NoActionCategory);
1225     acat->addAction("formpart_create", Kexi::NoActionCategory);
1226     acat->addAction("reportpart_create", Kexi::NoActionCategory);
1227     acat->addAction("macropart_create", Kexi::NoActionCategory);
1228     acat->addAction("scriptpart_create", Kexi::NoActionCategory);
1229 }
1230 
1231 void KexiMainWindow::invalidateActions()
1232 {
1233     invalidateProjectWideActions();
1234     invalidateSharedActions();
1235 }
1236 
1237 void KexiMainWindow::invalidateSharedActions(QObject *o)
1238 {
1239     //! @todo enabling is more complex...
1240     /* d->action_edit_cut->setEnabled(true);
1241       d->action_edit_copy->setEnabled(true);
1242       d->action_edit_paste->setEnabled(true);*/
1243 
1244     if (!o)
1245         o = focusWindow();
1246     KexiSharedActionHost::invalidateSharedActions(o);
1247 }
1248 
1249 void KexiMainWindow::invalidateSharedActions()
1250 {
1251     invalidateSharedActions(0);
1252 }
1253 
1254 // unused, I think
1255 void KexiMainWindow::invalidateSharedActionsLater()
1256 {
1257     QTimer::singleShot(1, this, SLOT(invalidateSharedActions()));
1258 }
1259 
1260 void KexiMainWindow::invalidateProjectWideActions()
1261 {
1262     const bool has_window = currentWindow();
1263     const bool window_dirty = currentWindow() && currentWindow()->isDirty();
1264     const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly();
1265 
1266     //PROJECT MENU
1267     d->action_save->setEnabled(has_window && window_dirty && !readOnly);
1268     d->action_save_as->setEnabled(has_window && !readOnly);
1269     d->action_project_properties->setEnabled(d->prj);
1270     d->action_close->setEnabled(d->prj);
1271     d->action_project_relations->setEnabled(d->prj);
1272 
1273     //DATA MENU
1274     if (d->action_project_import_data_table)
1275         d->action_project_import_data_table->setEnabled(d->prj && !readOnly);
1276     if (d->action_tools_data_import)
1277         d->action_tools_data_import->setEnabled(d->prj && !readOnly);
1278     d->action_project_export_data_table->setEnabled(
1279         currentWindow() && currentWindow()->part()->info()->isDataExportSupported());
1280     if (d->action_edit_paste_special_data_table)
1281         d->action_edit_paste_special_data_table->setEnabled(d->prj && !readOnly);
1282 
1283 #ifdef KEXI_QUICK_PRINTING_SUPPORT
1284     const bool printingActionsEnabled =
1285         currentWindow() && currentWindow()->part()->info()->isPrintingSupported()
1286         && !currentWindow()->neverSaved();
1287     d->action_project_print->setEnabled(printingActionsEnabled);
1288     d->action_project_print_preview->setEnabled(printingActionsEnabled);
1289     d->action_project_print_setup->setEnabled(printingActionsEnabled);
1290 #endif
1291 
1292     //EDIT MENU
1293 //! @todo "copy special" is currently enabled only for data view mode;
1294 //!  what about allowing it to enable in design view for "kexi/table" ?
1295     if (currentWindow() && currentWindow()->currentViewMode() == Kexi::DataViewMode) {
1296         KexiPart::Info *activePartInfo = currentWindow()->part()->info();
1297         d->action_edit_copy_special_data_table->setEnabled(
1298             activePartInfo ? activePartInfo->isDataExportSupported() : false);
1299     } else {
1300         d->action_edit_copy_special_data_table->setEnabled(false);
1301     }
1302     d->action_edit_find->setEnabled(d->prj);
1303 
1304     //VIEW MENU
1305     if (d->action_show_nav)
1306         d->action_show_nav->setEnabled(d->prj);
1307     d->action_activate_mainarea->setEnabled(d->prj);
1308     if (d->action_show_propeditor)
1309         d->action_show_propeditor->setEnabled(d->prj);
1310 #ifdef KEXI_SHOW_CONTEXT_HELP
1311     d->action_show_helper->setEnabled(d->prj);
1312 #endif
1313 
1314     //CREATE MENU
1315     if (d->tabbedToolBar && d->tabbedToolBar->createWidgetToolBar())
1316         d->tabbedToolBar->createWidgetToolBar()->setEnabled(d->prj);
1317 
1318     // DATA MENU
1319 
1320     //TOOLS MENU
1321     // "compact db" supported if there's no db or the current db supports compacting and is opened r/w:
1322     d->action_tools_compact_database->setEnabled(
1323         //! @todo Support compacting of non-opened projects
1324         /*!d->prj
1325         ||*/ (!readOnly && d->prj && d->prj->dbConnection()
1326             && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported))
1327     );
1328 
1329     //DOCKS
1330     if (d->navigator) {
1331         d->navigator->setEnabled(d->prj);
1332     }
1333     if (d->propEditor)
1334         d->propEditorTabWidget->setEnabled(d->prj);
1335 }
1336 
1337 tristate KexiMainWindow::startup()
1338 {
1339     tristate result = true;
1340     switch (KexiStartupHandler::global()->action()) {
1341     case KexiStartupHandler::CreateBlankProject:
1342         d->updatePropEditorVisibility(Kexi::NoViewMode);
1343         break;
1344 #ifdef KEXI_PROJECT_TEMPLATES
1345     case KexiStartupHandler::CreateFromTemplate:
1346         result = createProjectFromTemplate(*KexiStartupHandler::global()->projectData());
1347         break;
1348 #endif
1349     case KexiStartupHandler::OpenProject:
1350         result = openProject(*KexiStartupHandler::global()->projectData());
1351         break;
1352     case KexiStartupHandler::ImportProject:
1353         result = showProjectMigrationWizard(
1354                    KexiStartupHandler::global()->importActionData().mimeType,
1355                    KexiStartupHandler::global()->importActionData().fileName
1356                );
1357         break;
1358     case KexiStartupHandler::ShowWelcomeScreen:
1359         //! @todo show welcome screen as soon as is available
1360         QTimer::singleShot(100, this, SLOT(slotProjectWelcome()));
1361         break;
1362     default:
1363         d->updatePropEditorVisibility(Kexi::NoViewMode);
1364     }
1365     return result;
1366 }
1367 
1368 static QString internalReason(const KDbResult &result)
1369 {
1370     const QString msg = result.message();
1371     if (msg.isEmpty()) {
1372         return QString();
1373     }
1374     return xi18n("<br/>(reason: <i>%1</i>)", msg);
1375 }
1376 
1377 tristate KexiMainWindow::openProject(const KexiProjectData& projectData)
1378 {
1379     //qDebug() << projectData;
1380     QScopedPointer<KexiProject> prj(createKexiProjectObject(projectData));
1381     if (~KexiDBPasswordDialog::getPasswordIfNeeded(prj->data()->connectionData(), this)) {
1382         return cancelled;
1383     }
1384     bool incompatibleWithKexi;
1385     tristate res = prj->open(&incompatibleWithKexi);
1386 
1387     if (prj->data()->connectionData()->isPasswordNeeded()) {
1388         // password was supplied in this session, and shouldn't be stored or reused afterwards,
1389         // so let's remove it
1390         prj->data()->connectionData()->setPassword(QString());
1391     }
1392 
1393     if (~res) {
1394         return cancelled;
1395     }
1396     else if (!res) {
1397         if (incompatibleWithKexi) {
1398             if (KMessageBox::Yes == KMessageBox::questionYesNo(this,
1399                     xi18nc("@info (don't add tags around %1, it's done already)",
1400                            "Database project %1 does not appear to have been created using Kexi.<nl/>"
1401                            "Do you want to import it as a new Kexi project?",
1402                            projectData.infoString()),
1403                     QString(), KGuiItem(xi18nc("@action:button Import Database", "&Import..."), KexiIconName("database-import")),
1404                     KStandardGuiItem::cancel()))
1405             {
1406                 const bool anotherProjectAlreadyOpened = prj;
1407                 tristate res = showProjectMigrationWizard("application/x-kexi-connectiondata",
1408                                    projectData.databaseName(), *projectData.connectionData());
1409 
1410                 if (!anotherProjectAlreadyOpened) //the project could have been opened within this instance
1411                     return res;
1412 
1413                 //always return cancelled because even if migration succeeded, new Kexi instance
1414                 //will be started if user wanted to open the imported db
1415                 return cancelled;
1416             }
1417             return cancelled;
1418         }
1419         return false;
1420     }
1421 
1422     // success
1423     d->prj = prj.take();
1424     setupProjectNavigator();
1425     d->prj->data()->setLastOpened(QDateTime::currentDateTime());
1426     Kexi::recentProjects()->addProjectData(*d->prj->data());
1427     updateReadOnlyState();
1428     invalidateActions();
1429     setMessagesEnabled(false);
1430 
1431     QTimer::singleShot(1, this, SLOT(slotAutoOpenObjectsLater()));
1432     if (d->tabbedToolBar) {
1433         d->tabbedToolBar->showTab("create");// not needed since create toolbar already shows toolbar! move when kexi starts
1434         d->tabbedToolBar->showTab("data");
1435         d->tabbedToolBar->showTab("external");
1436         d->tabbedToolBar->showTab("tools");
1437         d->tabbedToolBar->hideTab("form");//temporalily until createToolbar is split
1438         d->tabbedToolBar->hideTab("report");//temporalily until createToolbar is split
1439 
1440         // make sure any tab is activated
1441         d->tabbedToolBar->setCurrentTab(0);
1442     }
1443     return true;
1444 }
1445 
1446 tristate KexiMainWindow::openProject(const KexiProjectData& data, const QString& shortcutPath,
1447                                      bool *opened)
1448 {
1449     if (!shortcutPath.isEmpty() && d->prj) {
1450         const tristate result = openProjectInExternalKexiInstance(
1451             shortcutPath, QString(), QString());
1452         if (result == true) {
1453             *opened = true;
1454         }
1455         return result;
1456     }
1457     return openProject(data);
1458 }
1459 
1460 tristate KexiMainWindow::createProjectFromTemplate(const KexiProjectData& projectData)
1461 {
1462     Q_UNUSED(projectData);
1463 #ifdef KEXI_PROJECT_TEMPLATES
1464     QStringList mimetypes;
1465     mimetypes.append(KDb::defaultFileBasedDriverMimeType());
1466     QString fname;
1467     //! @todo KEXI3 add equivalent of kfiledialog:///
1468     const QString startDir("kfiledialog:///OpenExistingOrCreateNewProject"/*as in KexiNewProjectWizard*/);
1469     const QString caption(xi18nc("@window:title", "Select New Project's Location"));
1470 
1471     while (true) {
1472         if (fname.isEmpty() &&
1473                 !projectData.connectionData()->databaseName().isEmpty()) {
1474             //propose filename from db template name
1475             fname = projectData.connectionData()->databaseName();
1476         }
1477         const bool specialDir = fname.isEmpty();
1478         qDebug() << fname << ".............";
1479         QFileDialog dlg(specialDir ? QUrl(startDir) : QUrl(),
1480                         QString(), this);
1481         dlg.setModal(true);
1482         dlg.setMimeFilter(mimetypes);
1483         if (!specialDir)
1484             dlg.selectUrl(QUrl::fromLocalFile(fname);   // may also be a filename
1485         dlg.setFileMode(QFileDialog::ExistingFile);
1486         dlg.setFileMode(QFileDialog::AcceptOpen);
1487         dlg.setWindowTitle(caption);
1488         if (QDialog::Accepted != dlg.exec()) {
1489             return cancelled;
1490         }
1491         if (dlg.selectedFiles().isEmpty()) {
1492             return cancelled;
1493         }
1494         fname = dlg.selectedFiles().first();
1495         if (fname.isEmpty()) {
1496             return cancelled;
1497         }
1498         if (KexiUtils::askForFileOverwriting(fname, this)) {
1499             break;
1500         }
1501     }
1502 
1503     QFile sourceFile(projectData.connectionData()->fileName());
1504     if (!sourceFile.copy(fname)) {
1505 //! @todo show error from with QFile::FileError
1506         return false;
1507     }
1508 
1509     return openProject(fname, 0, QString(), projectData.autoopenObjects/*copy*/);
1510 #else
1511     return false;
1512 #endif
1513 }
1514 
1515 void KexiMainWindow::updateReadOnlyState()
1516 {
1517     const bool readOnly = d->prj && d->prj->dbConnection() && d->prj->dbConnection()->options()->isReadOnly();
1518     //! @todo KEXI3 show read-only flag in the GUI because we have no statusbar
1519     if (d->navigator) {
1520         d->navigator->setReadOnly(readOnly);
1521     }
1522 
1523     // update "insert ....." actions for every part
1524     KexiPart::PartInfoList *plist = Kexi::partManager().infoList();
1525     if (plist) {
1526         foreach(KexiPart::Info *info, *plist) {
1527             QAction *a = info->newObjectAction();
1528             if (a)
1529                 a->setEnabled(!readOnly);
1530         }
1531     }
1532 }
1533 
1534 void KexiMainWindow::slotAutoOpenObjectsLater()
1535 {
1536     QString not_found_msg;
1537     bool openingCancelled;
1538     //ok, now open "autoopen: objects
1539     if (d->prj) {
1540         for (const KexiProjectData::ObjectInfo &info : d->prj->data()->autoopenObjects) {
1541             KexiPart::Info *i = Kexi::partManager().infoForPluginId(info.value("type"));
1542             if (!i) {
1543                 not_found_msg += "<li>";
1544                 if (!info.value("name").isEmpty()) {
1545                     not_found_msg += (QString("\"") + info.value("name") + "\" - ");
1546                 }
1547                 if (info.value("action") == "new") {
1548                     not_found_msg += xi18n("cannot create object - unknown object type \"%1\"",
1549                                            info.value("type"));
1550                 } else {
1551                     not_found_msg += xi18n("unknown object type \"%1\"", info.value("type"));
1552                 }
1553                 not_found_msg += internalReason(Kexi::partManager().result()) + "<br></li>";
1554                 continue;
1555             }
1556             // * NEW
1557             if (info.value("action") == "new") {
1558                 if (!newObject(i, &openingCancelled) && !openingCancelled) {
1559                     not_found_msg += "<li>";
1560                     not_found_msg
1561                         += (xi18n("cannot create object of type \"%1\"", info.value("type"))
1562                             + internalReason(d->prj->result()) + "<br></li>");
1563                 } else {
1564                     d->wasAutoOpen = true;
1565                 }
1566                 continue;
1567             }
1568 
1569             KexiPart::Item *item = d->prj->item(i, info.value("name"));
1570             if (!item) {
1571                 QString taskName;
1572                 if (info.value("action") == "execute") {
1573                     taskName = xi18nc("\"executing object\" action", "executing");
1574 #ifdef KEXI_QUICK_PRINTING_SUPPORT
1575                 } else if (info->value("action") == "print-preview") {
1576                     taskName = futureI18n("making print preview for");
1577                 } else if (info->value("action") == "print") {
1578                     taskName = futureI18n("printing");
1579 #endif
1580                 } else {
1581                     taskName = xi18n("opening");
1582                 }
1583 
1584                 not_found_msg += (QString("<li>") + taskName + " \"" + info.value("name") + "\" - ");
1585                 if ("table" == info.value("type").toLower()) {
1586                     not_found_msg += xi18n("table not found");
1587                 } else if ("query" == info.value("type").toLower()) {
1588                     not_found_msg += xi18n("query not found");
1589                 } else if ("macro" == info.value("type").toLower()) {
1590                     not_found_msg += xi18n("macro not found");
1591                 } else if ("script" == info.value("type").toLower()) {
1592                     not_found_msg += xi18n("script not found");
1593                 } else {
1594                     not_found_msg += xi18n("object not found");
1595                 }
1596                 not_found_msg += (internalReason(d->prj->result()) + "<br></li>");
1597                 continue;
1598             }
1599             // * EXECUTE, PRINT, PRINT PREVIEW
1600             if (info.value("action") == "execute") {
1601                 tristate res = executeItem(item);
1602                 if (false == res) {
1603                     not_found_msg += (QString("<li>\"") + info.value("name") + "\" - "
1604                                       + xi18n("cannot execute object")
1605                                       + internalReason(d->prj->result()) + "<br></li>");
1606                 }
1607                 continue;
1608             }
1609 #ifdef KEXI_QUICK_PRINTING_SUPPORT
1610             else if (info.value("action") == "print") {
1611                 tristate res = printItem(item);
1612                 if (false == res) {
1613                     not_found_msg += (QString("<li>\"") + info.value("name") + "\" - " + futureI18n("cannot print object") +
1614                                       internalReason(d->prj->result()) + "<br></li>");
1615                 }
1616                 continue;
1617             }
1618             else if (info.value("action") == "print-preview") {
1619                 tristate res = printPreviewForItem(item);
1620                 if (false == res) {
1621                     not_found_msg += (QString("<li>\"") + info.value("name") + "\" - " + futureI18n("cannot make print preview of object") +
1622                                       internalReason(d->prj->result()) + "<br></li>");
1623                 }
1624                 continue;
1625             }
1626 #endif
1627 
1628             Kexi::ViewMode viewMode;
1629             if (info.value("action") == "open") {
1630                 viewMode = Kexi::DataViewMode;
1631             } else if (info.value("action") == "design") {
1632                 viewMode = Kexi::DesignViewMode;
1633             } else if (info.value("action") == "edittext") {
1634                 viewMode = Kexi::TextViewMode;
1635             } else {
1636                 continue; //sanity
1637             }
1638 
1639             QString openObjectMessage;
1640             if (!openObject(item, viewMode, &openingCancelled, 0, &openObjectMessage)
1641                     && (!openingCancelled || !openObjectMessage.isEmpty()))
1642             {
1643                 not_found_msg += (QString("<li>\"") + info.value("name") + "\" - ");
1644                 if (openObjectMessage.isEmpty()) {
1645                     not_found_msg += xi18n("cannot open object");
1646                 } else {
1647                     not_found_msg += openObjectMessage;
1648                 }
1649                 not_found_msg += internalReason(d->prj->result()) + "<br></li>";
1650                 continue;
1651             } else {
1652                 d->wasAutoOpen = true;
1653             }
1654         }
1655     }
1656     setMessagesEnabled(true);
1657 
1658     if (!not_found_msg.isEmpty()) {
1659         showErrorMessage(xi18n("You have requested selected objects to be automatically opened "
1660                               "or processed on startup. Several objects cannot be opened or processed."),
1661                          QString("<ul>%1</ul>").arg(not_found_msg));
1662     }
1663     d->updatePropEditorVisibility(currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode);
1664 #if defined(KDOCKWIDGET_P)
1665     if (d->propEditor) {
1666         KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget();
1667         KDockSplitter *ds = (KDockSplitter *)dw->parentWidget();
1668         if (ds)
1669             ds->setSeparatorPosInPercent(d->config->readEntry("RightDockPosition", 80/* % */));
1670     }
1671 #endif
1672 
1673     updateAppCaption();
1674     if (d->tabbedToolBar) {
1675         d->tabbedToolBar->hideMainMenu();
1676     }
1677 
1678     qApp->processEvents();
1679     emit projectOpened();
1680 }
1681 
1682 tristate KexiMainWindow::closeProject()
1683 {
1684     if (d->tabbedToolBar)
1685         d->tabbedToolBar->hideMainMenu();
1686 
1687 #ifndef KEXI_NO_PENDING_DIALOGS
1688     if (d->pendingWindowsExist()) {
1689         qDebug() << "pendingWindowsExist...";
1690         d->actionToExecuteWhenPendingJobsAreFinished = Private::CloseProjectAction;
1691         return cancelled;
1692     }
1693 #endif
1694 
1695     //only save nav. visibility setting if there is project opened
1696     d->saveSettingsForShowProjectNavigator = d->prj && d->isProjectNavigatorVisible;
1697 
1698     if (!d->prj)
1699         return true;
1700 
1701     {
1702         // make sure the project can be closed
1703         bool cancel = false;
1704         emit acceptProjectClosingRequested(&cancel);
1705         if (cancel)
1706             return cancelled;
1707     }
1708 
1709     d->windowExistedBeforeCloseProject = currentWindow();
1710 
1711 #if defined(KDOCKWIDGET_P)
1712     //remember docks position - will be used on storeSettings()
1713     if (d->propEditor) {
1714         KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget();
1715         KDockSplitter *ds = (KDockSplitter *)dw->parentWidget();
1716         if (ds)
1717             d->propEditorDockSeparatorPos = ds->separatorPosInPercent();
1718     }
1719     if (d->nav) {
1720         if (d->propEditor) {
1721 //! @todo KEXI3 if (d->openedWindowsCount() == 0)
1722 //! @todo KEXI3 makeWidgetDockVisible(d->propEditorTabWidget);
1723             KDockWidget *dw = (KDockWidget *)d->propEditorTabWidget->parentWidget();
1724             KDockSplitter *ds = (KDockSplitter *)dw->parentWidget();
1725             if (ds)
1726                 ds->setSeparatorPosInPercent(80);
1727         }
1728 
1729         KDockWidget *dw = (KDockWidget *)d->nav->parentWidget();
1730         KDockSplitter *ds = (KDockSplitter *)dw->parentWidget();
1731         int dwWidth = dw->width();
1732         if (ds) {
1733             if (d->openedWindowsCount() != 0 && d->propEditorTabWidget && d->propEditorTabWidget->isVisible()) {
1734                 d->navDockSeparatorPos = ds->separatorPosInPercent();
1735             }
1736             else {
1737                 d->navDockSeparatorPos = (100 * dwWidth) / width();
1738             }
1739         }
1740     }
1741 #endif
1742 
1743     //close each window, optionally asking if user wants to close (if data changed)
1744     while (currentWindow()) {
1745         tristate res = closeWindow(currentWindow());
1746         if (!res || ~res)
1747             return res;
1748     }
1749 
1750     // now we will close for sure
1751     emit beforeProjectClosing();
1752 
1753     if (!d->prj->closeConnection())
1754         return false;
1755 
1756     if (d->navigator) {
1757         d->navWasVisibleBeforeProjectClosing = d->navDockWidget->isVisible();
1758         d->navDockWidget->hide();
1759         d->navigator->setProject(0);
1760         slotProjectNavigatorVisibilityChanged(true); // hide side tab
1761     }
1762 
1763     if (d->propEditorDockWidget)
1764         d->propEditorDockWidget->hide();
1765 
1766     d->clearWindows(); //sanity!
1767     delete d->prj;
1768     d->prj = 0;
1769 
1770     updateReadOnlyState();
1771     invalidateActions();
1772     updateAppCaption();
1773 
1774     emit projectClosed();
1775     return true;
1776 }
1777 
1778 void KexiMainWindow::setupContextHelp()
1779 {
1780 #ifdef KEXI_SHOW_CONTEXT_HELP
1781     d->ctxHelp = new KexiContextHelp(d->mainWidget, this);
1782     //! @todo
1783     /*
1784       d->ctxHelp->setContextHelp(xi18n("Welcome"),xi18n("The <B>KEXI team</B> wishes you a lot of productive work, "
1785         "with this product. <BR><HR><BR>If you have found a <B>bug</B> or have a <B>feature</B> request, please don't "
1786         "hesitate to report it at our <A href=\"http://www.kexi-project.org/cgi-bin/bug.pl\"> issue "
1787         "tracking system </A>.<BR><HR><BR>If you would like to <B>join</B> our effort, the <B>development</B> documentation "
1788         "at <A href=\"http://www.kexi-project.org\">www.kexi-project.org</A> is a good starting point."),0);
1789     */
1790     addToolWindow(d->ctxHelp, KDockWidget::DockBottom | KDockWidget::DockLeft, getMainDockWidget(), 20);
1791 #endif
1792 }
1793 
1794 void KexiMainWindow::setupMainWidget()
1795 {
1796     QVBoxLayout *vlyr = new QVBoxLayout(this);
1797     vlyr->setContentsMargins(0, 0, 0, 0);
1798     vlyr->setSpacing(0);
1799 
1800     if (d->isMainMenuVisible) {
1801         QWidget *tabbedToolBarContainer = new QWidget(this);
1802         vlyr->addWidget(tabbedToolBarContainer);
1803         QVBoxLayout *tabbedToolBarContainerLyr = new QVBoxLayout(tabbedToolBarContainer);
1804         tabbedToolBarContainerLyr->setSpacing(0);
1805         tabbedToolBarContainerLyr->setContentsMargins(
1806             KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2,
1807             KexiUtils::marginHint() / 2, KexiUtils::marginHint() / 2);
1808 
1809         d->tabbedToolBar = new KexiTabbedToolBar(tabbedToolBarContainer);
1810         Q_ASSERT(d->action_view_global_search);
1811         connect(d->action_view_global_search, SIGNAL(triggered()),
1812                 d->tabbedToolBar, SLOT(activateSearchLineEdit()));
1813         tabbedToolBarContainerLyr->addWidget(d->tabbedToolBar);
1814         d->tabbedToolBar->hideTab("form"); //temporarily until createToolbar is split
1815         d->tabbedToolBar->hideTab("report"); //temporarily until createToolbar is split
1816     }
1817     else {
1818         d->tabbedToolBar = 0;
1819     }
1820 
1821     QWidget *mainWidgetContainer = new QWidget();
1822     vlyr->addWidget(mainWidgetContainer, 1);
1823     QHBoxLayout *mainWidgetContainerLyr = new QHBoxLayout(mainWidgetContainer);
1824     mainWidgetContainerLyr->setContentsMargins(0, 0, 0, 0);
1825     mainWidgetContainerLyr->setSpacing(0);
1826 
1827 
1828     KMultiTabBar *mtbar = new KMultiTabBar(KMultiTabBar::Left);
1829     mtbar->setStyle(KMultiTabBar::VSNET);
1830     mainWidgetContainerLyr->addWidget(mtbar);
1831     d->multiTabBars.insert(mtbar->position(), mtbar);
1832 
1833     d->mainWidget = new KexiMainWidget();
1834     d->mainWidget->setParent(this);
1835 
1836     d->mainWidget->tabWidget()->setTabsClosable(true);
1837     connect(d->mainWidget->tabWidget(), SIGNAL(tabCloseRequested(int)),
1838             this, SLOT(closeWindowForTab(int)));
1839     mainWidgetContainerLyr->addWidget(d->mainWidget, 1);
1840 
1841     mtbar = new KMultiTabBar(KMultiTabBar::Right);
1842     mtbar->setStyle(KMultiTabBar::VSNET);
1843     mainWidgetContainerLyr->addWidget(mtbar);
1844     d->multiTabBars.insert(mtbar->position(), mtbar);
1845 }
1846 
1847 void KexiMainWindow::slotSetProjectNavigatorVisible(bool set)
1848 {
1849     if (d->navDockWidget)
1850         d->navDockWidget->setVisible(set);
1851 }
1852 
1853 void KexiMainWindow::slotSetPropertyEditorVisible(bool set)
1854 {
1855     if (d->propEditorDockWidget)
1856         d->propEditorDockWidget->setVisible(set);
1857 }
1858 
1859 void KexiMainWindow::slotProjectNavigatorVisibilityChanged(bool visible)
1860 {
1861     d->setTabBarVisible(KMultiTabBar::Left, PROJECT_NAVIGATOR_TABBAR_ID,
1862                         d->navDockWidget, !visible);
1863 }
1864 
1865 void KexiMainWindow::slotPropertyEditorVisibilityChanged(bool visible)
1866 {
1867     if (!d->enable_slotPropertyEditorVisibilityChanged)
1868         return;
1869     d->setPropertyEditorTabBarVisible(!visible);
1870     if (!visible)
1871         d->propertyEditorCollapsed = true;
1872 }
1873 
1874 void KexiMainWindow::slotMultiTabBarTabClicked(int id)
1875 {
1876     if (id == PROJECT_NAVIGATOR_TABBAR_ID) {
1877         slotProjectNavigatorVisibilityChanged(true);
1878         d->navDockWidget->show();
1879     }
1880     else if (id == PROPERTY_EDITOR_TABBAR_ID) {
1881         slotPropertyEditorVisibilityChanged(true);
1882         d->propEditorDockWidget->show();
1883         d->propertyEditorCollapsed = false;
1884     }
1885 }
1886 
1887 static Qt::DockWidgetArea applyRightToLeftToDockArea(Qt::DockWidgetArea area)
1888 {
1889     if (QApplication::layoutDirection() == Qt::RightToLeft) {
1890         if (area == Qt::LeftDockWidgetArea) {
1891             return Qt::RightDockWidgetArea;
1892         }
1893         else if (area == Qt::RightDockWidgetArea) {
1894             return Qt::LeftDockWidgetArea;
1895         }
1896     }
1897     return area;
1898 }
1899 
1900 void KexiMainWindow::setupProjectNavigator()
1901 {
1902     if (!d->isProjectNavigatorVisible)
1903         return;
1904 
1905     if (d->navigator) {
1906         d->navDockWidget->show();
1907     }
1908     else {
1909         KexiDockableWidget* navDockableWidget = new KexiDockableWidget;
1910         d->navigator = new KexiProjectNavigator(navDockableWidget);
1911         kexiTester() << KexiTestObject(d->navigator, "KexiProjectNavigator");
1912 
1913         navDockableWidget->setWidget(d->navigator);
1914 
1915         d->navDockWidget = new KexiDockWidget(d->navigator->windowTitle(), d->mainWidget);
1916         d->navDockWidget->setObjectName("ProjectNavigatorDockWidget");
1917         d->mainWidget->addDockWidget(
1918             applyRightToLeftToDockArea(Qt::LeftDockWidgetArea), d->navDockWidget,
1919             Qt::Vertical);
1920         navDockableWidget->setParent(d->navDockWidget);
1921         d->navDockWidget->setWidget(navDockableWidget);
1922 
1923         KConfigGroup mainWindowGroup(d->config->group("MainWindow"));
1924         const QSize projectNavigatorSize = mainWindowGroup.readEntry<QSize>("ProjectNavigatorSize", QSize());
1925         if (!projectNavigatorSize.isNull()) {
1926             navDockableWidget->setSizeHint(projectNavigatorSize);
1927         }
1928 
1929         connect(d->navDockWidget, SIGNAL(visibilityChanged(bool)),
1930             this, SLOT(slotProjectNavigatorVisibilityChanged(bool)));
1931 
1932         //Nav2 Signals
1933         connect(d->navigator, SIGNAL(openItem(KexiPart::Item*,Kexi::ViewMode)),
1934                 this, SLOT(openObject(KexiPart::Item*,Kexi::ViewMode)));
1935         connect(d->navigator, SIGNAL(openOrActivateItem(KexiPart::Item*,Kexi::ViewMode)),
1936                 this, SLOT(openObjectFromNavigator(KexiPart::Item*,Kexi::ViewMode)));
1937         connect(d->navigator, SIGNAL(newItem(KexiPart::Info*)),
1938                 this, SLOT(newObject(KexiPart::Info*)));
1939         connect(d->navigator, SIGNAL(removeItem(KexiPart::Item*)),
1940                 this, SLOT(removeObject(KexiPart::Item*)));
1941         connect(d->navigator->model(), SIGNAL(renameItem(KexiPart::Item*,QString,bool*)),
1942                 this, SLOT(renameObject(KexiPart::Item*,QString,bool*)));
1943         connect(d->navigator->model(), SIGNAL(changeItemCaption(KexiPart::Item*,QString,bool*)),
1944                 this, SLOT(setObjectCaption(KexiPart::Item*,QString,bool*)));
1945         connect(d->navigator, SIGNAL(executeItem(KexiPart::Item*)),
1946                 this, SLOT(executeItem(KexiPart::Item*)));
1947         connect(d->navigator, SIGNAL(exportItemToClipboardAsDataTable(KexiPart::Item*)),
1948                 this, SLOT(copyItemToClipboardAsDataTable(KexiPart::Item*)));
1949         connect(d->navigator, SIGNAL(exportItemToFileAsDataTable(KexiPart::Item*)),
1950                 this, SLOT(exportItemAsDataTable(KexiPart::Item*)));
1951 #ifdef KEXI_QUICK_PRINTING_SUPPORT
1952         connect(d->navigator, SIGNAL(printItem(KexiPart::Item*)),
1953                 this, SLOT(printItem(KexiPart::Item*)));
1954         connect(d->navigator, SIGNAL(pageSetupForItem(KexiPart::Item*)),
1955                 this, SLOT(showPageSetupForItem(KexiPart::Item*)));
1956 #endif
1957         connect(d->navigator, SIGNAL(selectionChanged(KexiPart::Item*)),
1958                 this, SLOT(slotPartItemSelectedInNavigator(KexiPart::Item*)));
1959     }
1960     if (d->prj->isConnected()) {
1961         QString partManagerErrorMessages;
1962 
1963         if (!partManagerErrorMessages.isEmpty()) {
1964             showWarningContinueMessage(partManagerErrorMessages, QString(),
1965                                        "ShowWarningsRelatedToPluginsLoading");
1966         }
1967         d->navigator->setProject(d->prj, QString()/*all classes*/, &partManagerErrorMessages);
1968 
1969     }
1970     connect(d->prj, SIGNAL(newItemStored(KexiPart::Item*)), d->navigator->model(), SLOT(slotAddItem(KexiPart::Item*)));
1971     connect(d->prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->model(), SLOT(slotRemoveItem(KexiPart::Item)));
1972 
1973     d->navigator->setFocus();
1974 
1975     if (d->forceShowProjectNavigatorOnCreation) {
1976         slotShowNavigator();
1977         d->forceShowProjectNavigatorOnCreation = false;
1978     } else if (d->forceHideProjectNavigatorOnCreation) {
1979         d->forceHideProjectNavigatorOnCreation = false;
1980     }
1981 
1982     invalidateActions();
1983 }
1984 
1985 void KexiMainWindow::slotLastActions()
1986 {
1987 }
1988 
1989 void KexiMainWindow::setupPropertyEditor()
1990 {
1991     if (!d->propEditor) {
1992         KConfigGroup mainWindowGroup(d->config->group("MainWindow"));
1993 //! @todo FIX LAYOUT PROBLEMS
1994         d->propEditorDockWidget = new KexiDockWidget(xi18n("Property Editor"), d->mainWidget);
1995         d->propEditorDockWidget->setObjectName("PropertyEditorDockWidget");
1996         d->mainWidget->addDockWidget(
1997             applyRightToLeftToDockArea(Qt::RightDockWidgetArea), d->propEditorDockWidget,
1998             Qt::Vertical
1999         );
2000         connect(d->propEditorDockWidget, SIGNAL(visibilityChanged(bool)),
2001             this, SLOT(slotPropertyEditorVisibilityChanged(bool)));
2002 
2003         d->propEditorDockableWidget = new KexiDockableWidget(d->propEditorDockWidget);
2004         d->propEditorDockWidget->setWidget(d->propEditorDockableWidget);
2005         const QSize propertyEditorSize = mainWindowGroup.readEntry<QSize>("PropertyEditorSize", QSize());
2006         if (!propertyEditorSize.isNull()) {
2007             d->propEditorDockableWidget->setSizeHint(propertyEditorSize);
2008         }
2009 
2010         QWidget *propEditorDockWidgetContents = new QWidget(d->propEditorDockableWidget);
2011         d->propEditorDockableWidget->setWidget(propEditorDockWidgetContents);
2012         QVBoxLayout *propEditorDockWidgetContentsLyr = new QVBoxLayout(propEditorDockWidgetContents);
2013         propEditorDockWidgetContentsLyr->setContentsMargins(0, 0, 0, 0);
2014 
2015         d->propEditorTabWidget = new QTabWidget(propEditorDockWidgetContents);
2016         d->propEditorTabWidget->setDocumentMode(true);
2017         propEditorDockWidgetContentsLyr->addWidget(d->propEditorTabWidget);
2018         d->propEditor = new KexiPropertyEditorView(d->propEditorTabWidget);
2019         d->propEditorTabWidget->setWindowTitle(d->propEditor->windowTitle());
2020         d->propEditorTabWidget->addTab(d->propEditor, xi18n("Properties"));
2021 //! @todo REMOVE? d->propEditor->installEventFilter(this);
2022 
2023         KConfigGroup propertyEditorGroup(d->config->group("PropertyEditor"));
2024         QFont f(KexiUtils::smallestReadableFont());
2025         const qreal pointSizeF = propertyEditorGroup.readEntry("FontPointSize", -1.0f); // points are more accurate
2026         if (pointSizeF > 0.0) {
2027             f.setPointSizeF(pointSizeF);
2028         } else {
2029             const int pixelSize = propertyEditorGroup.readEntry("FontSize", -1); // compatibility with Kexi 2.x
2030             if (pixelSize > 0) {
2031                 f.setPixelSize(pixelSize);
2032             }
2033         }
2034         d->propEditorTabWidget->setFont(f);
2035 
2036         d->enable_slotPropertyEditorVisibilityChanged = false;
2037         d->propEditorDockWidget->setVisible(false);
2038         d->enable_slotPropertyEditorVisibilityChanged = true;
2039     }
2040 }
2041 
2042 void KexiMainWindow::slotPartLoaded(KexiPart::Part* p)
2043 {
2044     if (!p)
2045         return;
2046     p->createGUIClients();
2047 }
2048 
2049 void KexiMainWindow::updateAppCaption()
2050 {
2051 //! @todo allow to set custom "static" app caption
2052 
2053     d->appCaptionPrefix.clear();
2054     if (d->prj && d->prj->data()) {//add project name
2055         d->appCaptionPrefix = d->prj->data()->caption();
2056         if (d->appCaptionPrefix.isEmpty()) {
2057             d->appCaptionPrefix = d->prj->data()->databaseName();
2058         }
2059         if (d->prj->dbConnection()->options()->isReadOnly()) {
2060             d->appCaptionPrefix = xi18nc("<project-name> (read only)", "%1 (read only)", d->appCaptionPrefix);
2061         }
2062     }
2063 
2064     setWindowTitle(d->appCaptionPrefix);
2065 }
2066 
2067 bool KexiMainWindow::queryClose()
2068 {
2069 #ifndef KEXI_NO_PENDING_DIALOGS
2070     if (d->pendingWindowsExist()) {
2071         qDebug() << "pendingWindowsExist...";
2072         d->actionToExecuteWhenPendingJobsAreFinished = Private::QuitAction;
2073         return false;
2074     }
2075 #endif
2076     const tristate res = closeProject();
2077     if (~res)
2078         return false;
2079 
2080     if (res == true)
2081         storeSettings();
2082 
2083     if (! ~res) {
2084         Kexi::deleteGlobalObjects();
2085         qApp->quit();
2086     }
2087     return ! ~res;
2088 }
2089 
2090 void KexiMainWindow::closeEvent(QCloseEvent *ev)
2091 {
2092     d->mainWidget->closeEvent(ev);
2093 }
2094 
2095 static const QSize KEXI_MIN_WINDOW_SIZE(1024, 768);
2096 
2097 void
2098 KexiMainWindow::restoreSettings()
2099 {
2100     KConfigGroup mainWindowGroup(d->config->group("MainWindow"));
2101     const bool maximize = mainWindowGroup.readEntry("Maximized", false);
2102     const QRect geometry(mainWindowGroup.readEntry("Geometry", QRect()));
2103     if (geometry.isValid())
2104         setGeometry(geometry);
2105     else if (maximize)
2106         setWindowState(windowState() | Qt::WindowMaximized);
2107     else {
2108         QRect desk = QApplication::desktop()->screenGeometry(
2109             QApplication::desktop()->screenNumber(this));
2110         if (desk.width() <= KEXI_MIN_WINDOW_SIZE.width()
2111                 || desk.height() <= KEXI_MIN_WINDOW_SIZE.height())
2112         {
2113             setWindowState(windowState() | Qt::WindowMaximized);
2114         } else {
2115             resize(KEXI_MIN_WINDOW_SIZE);
2116         }
2117     }
2118     // Saved settings
2119 }
2120 
2121 void
2122 KexiMainWindow::storeSettings()
2123 {
2124     //qDebug();
2125     KConfigGroup mainWindowGroup(d->config->group("MainWindow"));
2126 
2127     if (isMaximized()) {
2128         mainWindowGroup.writeEntry("Maximized", true);
2129         mainWindowGroup.deleteEntry("Geometry");
2130     } else {
2131         mainWindowGroup.deleteEntry("Maximized");
2132         mainWindowGroup.writeEntry("Geometry", geometry());
2133     }
2134 
2135     if (d->navigator)
2136         mainWindowGroup.writeEntry("ProjectNavigatorSize", d->navigator->parentWidget()->size());
2137 
2138     if (d->propEditorDockableWidget)
2139         mainWindowGroup.writeEntry("PropertyEditorSize", d->propEditorDockableWidget->size());
2140 
2141     d->config->sync();
2142 }
2143 
2144 void
2145 KexiMainWindow::registerChild(KexiWindow *window)
2146 {
2147     //qDebug();
2148     connect(window, SIGNAL(dirtyChanged(KexiWindow*)), this, SLOT(slotDirtyFlagChanged(KexiWindow*)));
2149 
2150     if (window->id() != -1) {
2151         d->insertWindow(window);
2152     }
2153     //qDebug() << "ID=" << window->id();
2154 }
2155 
2156 void KexiMainWindow::updateCustomPropertyPanelTabs(KexiWindow *prevWindow,
2157         Kexi::ViewMode prevViewMode)
2158 {
2159     updateCustomPropertyPanelTabs(
2160         prevWindow ? prevWindow->part() : 0,
2161         prevWindow ? prevWindow->currentViewMode() : prevViewMode,
2162         currentWindow() ? currentWindow()->part() : 0,
2163         currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode
2164     );
2165 }
2166 
2167 void KexiMainWindow::updateCustomPropertyPanelTabs(
2168     KexiPart::Part *prevWindowPart, Kexi::ViewMode prevViewMode,
2169     KexiPart::Part *curWindowPart, Kexi::ViewMode curViewMode)
2170 {
2171     if (!d->propEditorTabWidget)
2172         return;
2173 
2174     if (   !curWindowPart
2175         || (/*prevWindowPart &&*/ curWindowPart
2176              && (prevWindowPart != curWindowPart || prevViewMode != curViewMode)
2177            )
2178        )
2179     {
2180         if (d->partForPreviouslySetupPropertyPanelTabs) {
2181             //remember current page number for this part
2182             if ((   prevViewMode == Kexi::DesignViewMode
2183                  && static_cast<KexiPart::Part*>(d->partForPreviouslySetupPropertyPanelTabs) != curWindowPart) //part changed
2184                 || curViewMode != Kexi::DesignViewMode)
2185             { //..or switching to other view mode
2186                 d->recentlySelectedPropertyPanelPages.insert(
2187                         d->partForPreviouslySetupPropertyPanelTabs,
2188                         d->propEditorTabWidget->currentIndex());
2189             }
2190         }
2191 
2192         //delete old custom tabs (other than 'property' tab)
2193         const int count = d->propEditorTabWidget->count();
2194         for (int i = 1; i < count; i++)
2195             d->propEditorTabWidget->removeTab(1);
2196     }
2197 
2198     //don't change anything if part is not switched nor view mode changed
2199     if ((!prevWindowPart && !curWindowPart)
2200             || (prevWindowPart == curWindowPart && prevViewMode == curViewMode)
2201             || (curWindowPart && curViewMode != Kexi::DesignViewMode))
2202     {
2203         //new part for 'previously setup tabs'
2204         d->partForPreviouslySetupPropertyPanelTabs = curWindowPart;
2205         return;
2206     }
2207 
2208     if (curWindowPart) {
2209         //recreate custom tabs
2210         curWindowPart->setupCustomPropertyPanelTabs(d->propEditorTabWidget);
2211 
2212         //restore current page number for this part
2213         if (d->recentlySelectedPropertyPanelPages.contains(curWindowPart)) {
2214             d->propEditorTabWidget->setCurrentIndex(
2215                 d->recentlySelectedPropertyPanelPages[ curWindowPart ]
2216             );
2217         }
2218     }
2219 
2220     //new part for 'previously setup tabs'
2221     d->partForPreviouslySetupPropertyPanelTabs = curWindowPart;
2222 }
2223 
2224 void KexiMainWindow::activeWindowChanged(KexiWindow *window, KexiWindow *prevWindow)
2225 {
2226     //qDebug() << "to=" << (window ? window->windowTitle() : "<none>");
2227     bool windowChanged = prevWindow != window;
2228 
2229     if (windowChanged) {
2230         if (prevWindow) {
2231             //inform previously activated dialog about deactivation
2232             prevWindow->deactivate();
2233         }
2234     }
2235 
2236     updateCustomPropertyPanelTabs(prevWindow, prevWindow ? prevWindow->currentViewMode() : Kexi::NoViewMode);
2237 
2238     // inform the current view of the new dialog about property switching
2239     // (this will also call KexiMainWindow::propertySetSwitched() to update the current property editor's set
2240     if (windowChanged && currentWindow())
2241         currentWindow()->selectedView()->propertySetSwitched();
2242 
2243     if (windowChanged) {
2244         if (currentWindow() && currentWindow()->currentViewMode() != 0 && window) {
2245             //on opening new dialog it can be 0; we don't want this
2246             d->updatePropEditorVisibility(currentWindow()->currentViewMode());
2247 
2248             restoreDesignTabIfNeeded(window->partItem()->pluginId(), window->currentViewMode(),
2249                                      prevWindow ? prevWindow->partItem()->identifier() : 0);
2250             activateDesignTabIfNeeded(window->partItem()->pluginId(),
2251                                       window->currentViewMode());
2252         }
2253     }
2254 
2255     invalidateActions();
2256     d->updateFindDialogContents();
2257     if (window)
2258         window->setFocus();
2259 }
2260 
2261 bool
2262 KexiMainWindow::activateWindow(int id)
2263 {
2264 #ifndef KEXI_NO_PENDING_DIALOGS
2265     Private::PendingJobType pendingType;
2266     return activateWindow(*d->openedWindowFor(id, pendingType));
2267 #else
2268     return activateWindow(*d->openedWindowFor(id));
2269 #endif
2270 }
2271 
2272 bool
2273 KexiMainWindow::activateWindow(KexiWindow& window)
2274 {
2275     //qDebug();
2276 
2277     d->focus_before_popup = &window;
2278     d->mainWidget->tabWidget()->setCurrentWidget(window.parentWidget()/*container*/);
2279     window.activate();
2280     return true;
2281 }
2282 
2283 void KexiMainWindow::activateNextWindow()
2284 {
2285     // Case 1: go to next assistant page
2286     KexiAssistantPage *page = d->visibleMainMenuWidgetPage();
2287     if (page) {
2288         page->next();
2289         return;
2290     }
2291     // Case 2: go to next tab
2292     activateNextTab();
2293 }
2294 
2295 void KexiMainWindow::activatePreviousWindow()
2296 {
2297     // Case 1: go to previous assistant page
2298     KexiAssistantPage *page = d->visibleMainMenuWidgetPage();
2299     if (page) {
2300         page->tryBack();
2301         return;
2302     }
2303     // Case 2: go to previous tab
2304     activatePreviousTab();
2305 }
2306 
2307 void KexiMainWindow::activateNextTab()
2308 {
2309     //! @todo
2310 }
2311 
2312 void KexiMainWindow::activatePreviousTab()
2313 {
2314     //! @todo
2315 }
2316 
2317 void
2318 KexiMainWindow::slotSettings()
2319 {
2320     if (d->tabbedToolBar) {
2321         d->tabbedToolBar->showMainMenu("settings");
2322         // dummy
2323         QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("settings")->text());
2324         d->tabbedToolBar->setMainMenuContent(dummy);
2325     }
2326 }
2327 
2328 void
2329 KexiMainWindow::slotConfigureKeys()
2330 {
2331     KShortcutsDialog::configure(actionCollection(),
2332                                 KShortcutsEditor::LetterShortcutsDisallowed, this);
2333 }
2334 
2335 void
2336 KexiMainWindow::slotConfigureToolbars()
2337 {
2338     KEditToolBar edit(actionCollection());
2339     (void) edit.exec();
2340 }
2341 
2342 void KexiMainWindow::slotProjectNew()
2343 {
2344     createNewProject();
2345 }
2346 
2347 KexiProject* KexiMainWindow::createKexiProjectObject(const KexiProjectData &data)
2348 {
2349     KexiProject *prj = new KexiProject(data, this);
2350     connect(prj, SIGNAL(itemRenamed(KexiPart::Item,QString)), this, SLOT(slotObjectRenamed(KexiPart::Item,QString)));
2351 
2352     if (d->navigator){
2353         connect(prj, SIGNAL(itemRemoved(KexiPart::Item)), d->navigator->model(), SLOT(slotRemoveItem(KexiPart::Item)));
2354     }
2355     return prj;
2356 }
2357 
2358 void KexiMainWindow::createNewProject()
2359 {
2360     if (!d->tabbedToolBar)
2361         return;
2362     d->tabbedToolBar->showMainMenu("project_new");
2363     KexiNewProjectAssistant* assistant = new KexiNewProjectAssistant;
2364     connect(assistant, SIGNAL(createProject(KexiProjectData)),
2365             this, SLOT(createNewProject(KexiProjectData)));
2366 
2367     d->tabbedToolBar->setMainMenuContent(assistant);
2368 }
2369 
2370 tristate KexiMainWindow::createNewProject(const KexiProjectData &projectData)
2371 {
2372     QScopedPointer<KexiProject> prj(createKexiProjectObject(projectData));
2373     tristate res = prj->create(true /*overwrite*/);
2374     if (res != true) {
2375         return res;
2376     }
2377     //qDebug() << "new project created ---";
2378     if (d->prj) {
2379         res = openProjectInExternalKexiInstance(
2380                 prj->data()->connectionData()->databaseName(),
2381                 prj->data()->connectionData(),
2382                 prj->data()->databaseName());
2383         Kexi::recentProjects()->addProjectData(*prj->data());
2384         if (d->tabbedToolBar) {
2385             d->tabbedToolBar->hideMainMenu();
2386         }
2387         return res;
2388     }
2389     if (d->tabbedToolBar) {
2390         d->tabbedToolBar->hideMainMenu();
2391     }
2392     d->prj = prj.take();
2393     setupProjectNavigator();
2394     d->prj->data()->setLastOpened(QDateTime::currentDateTime());
2395     Kexi::recentProjects()->addProjectData(*d->prj->data());
2396 
2397     invalidateActions();
2398     updateAppCaption();
2399     return true;
2400 }
2401 
2402 void KexiMainWindow::slotProjectOpen()
2403 {
2404     if (!d->tabbedToolBar)
2405         return;
2406     d->tabbedToolBar->showMainMenu("project_open");
2407     KexiOpenProjectAssistant* assistant = new KexiOpenProjectAssistant;
2408     connect(assistant, SIGNAL(openProject(KexiProjectData)),
2409             this, SLOT(openProject(KexiProjectData)));
2410     connect(assistant, SIGNAL(openProject(QString)),
2411             this, SLOT(openProject(QString)));
2412     d->tabbedToolBar->setMainMenuContent(assistant);
2413 }
2414 
2415 tristate KexiMainWindow::openProject(const QString& aFileName)
2416 {
2417     return openProject(aFileName, QString(), QString());
2418 }
2419 
2420 tristate KexiMainWindow::openProject(const QString& aFileName,
2421                                      const QString& fileNameForConnectionData, const QString& dbName)
2422 {
2423     if (d->prj)
2424         return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName);
2425 
2426     KDbConnectionData *cdata = 0;
2427     if (!fileNameForConnectionData.isEmpty()) {
2428         cdata = Kexi::connset().connectionDataForFileName(fileNameForConnectionData);
2429         if (!cdata) {
2430             qWarning() << "cdata?";
2431             return false;
2432         }
2433     }
2434     return openProject(aFileName, cdata, dbName);
2435 }
2436 
2437 tristate KexiMainWindow::openProject(const QString& aFileName,
2438                                      KDbConnectionData *cdata, const QString& dbName,
2439                                      const KexiProjectData::AutoOpenObjects& autoopenObjects)
2440 {
2441     if (d->prj) {
2442         return openProjectInExternalKexiInstance(aFileName, cdata, dbName);
2443     }
2444 
2445     KexiProjectData* projectData = 0;
2446     const KexiStartupHandler *h = KexiStartupHandler::global();
2447     bool readOnly = h->isSet(h->options().readOnly);
2448     bool deleteAfterOpen = false;
2449     if (cdata) {
2450         //server-based project
2451         if (dbName.isEmpty()) {//no database name given, ask user
2452             bool cancel;
2453             projectData = KexiStartupHandler::global()->selectProject(cdata, &cancel, this);
2454             if (cancel)
2455                 return cancelled;
2456         } else {
2457 //! @todo caption arg?
2458             projectData = new KexiProjectData(*cdata, dbName);
2459             deleteAfterOpen = true;
2460         }
2461     } else {
2462         if (aFileName.isEmpty()) {
2463             qWarning() << "aFileName.isEmpty()";
2464             return false;
2465         }
2466         //file-based project
2467         qDebug() << "Project File: " << aFileName;
2468         KDbConnectionData fileConnData;
2469         fileConnData.setDatabaseName(aFileName);
2470         QString detectedDriverId;
2471         int detectOptions = 0;
2472         if (readOnly) {
2473             detectOptions |= KexiStartupHandler::OpenReadOnly;
2474         }
2475         KexiStartupData::Import importActionData;
2476         bool forceReadOnly;
2477         const tristate res = KexiStartupHandler::detectActionForFile(
2478                                  &importActionData, &detectedDriverId, fileConnData.driverId(),
2479                                  aFileName, this, detectOptions, &forceReadOnly);
2480         if (forceReadOnly) {
2481             readOnly = true;
2482         }
2483         if (true != res)
2484             return res;
2485 
2486         if (importActionData) { //importing requested
2487             return showProjectMigrationWizard(importActionData.mimeType, importActionData.fileName);
2488         }
2489         fileConnData.setDriverId(detectedDriverId);
2490 
2491         if (fileConnData.driverId().isEmpty())
2492             return false;
2493 
2494         //opening requested
2495         projectData = new KexiProjectData(fileConnData);
2496         deleteAfterOpen = true;
2497     }
2498     if (!projectData)
2499         return false;
2500     projectData->setReadOnly(readOnly);
2501     projectData->autoopenObjects = autoopenObjects;
2502     const tristate res = openProject(*projectData);
2503     if (deleteAfterOpen) //projectData object has been copied
2504         delete projectData;
2505     return res;
2506 }
2507 
2508 tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName,
2509         KDbConnectionData *cdata, const QString& dbName)
2510 {
2511     QString fileNameForConnectionData;
2512     if (aFileName.isEmpty()) { //try .kexic file
2513         if (cdata)
2514             fileNameForConnectionData = Kexi::connset().fileNameForConnectionData(*cdata);
2515     }
2516     return openProjectInExternalKexiInstance(aFileName, fileNameForConnectionData, dbName);
2517 }
2518 
2519 tristate KexiMainWindow::openProjectInExternalKexiInstance(const QString& aFileName,
2520         const QString& fileNameForConnectionData, const QString& dbName)
2521 {
2522     QString fileName(aFileName);
2523     QStringList args;
2524 
2525     // open a file-based project or a server connection provided as a .kexic file
2526     // (we have no other simple way to provide the startup data to a new process)
2527     if (fileName.isEmpty()) { //try .kexic file
2528         if (!fileNameForConnectionData.isEmpty())
2529             args << "--skip-conn-dialog"; //user does not expect conn. dialog to be shown here
2530 
2531         if (dbName.isEmpty()) { //use 'kexi --skip-conn-dialog file.kexic'
2532             fileName = fileNameForConnectionData;
2533         } else { //use 'kexi --skip-conn-dialog --connection file.kexic dbName'
2534             if (fileNameForConnectionData.isEmpty()) {
2535                 qWarning() << "fileNameForConnectionData?";
2536                 return false;
2537             }
2538             args << "--connection" << fileNameForConnectionData;
2539             fileName = dbName;
2540         }
2541     }
2542     if (fileName.isEmpty()) {
2543         qWarning() << "fileName?";
2544         return false;
2545     }
2546 //! @todo use KRun
2547 //! @todo untested
2548 //Can arguments be supplied to KRun like is used here? AP
2549     args << fileName;
2550     const bool ok = QProcess::startDetached(
2551         qApp->applicationFilePath(), args,
2552         QFileInfo(fileName).absoluteDir().absolutePath());
2553     if (!ok) {
2554         d->showStartProcessMsg(args);
2555     }
2556     if (d->tabbedToolBar) {
2557         d->tabbedToolBar->hideMainMenu();
2558     }
2559     return ok;
2560 }
2561 
2562 void KexiMainWindow::slotProjectWelcome()
2563 {
2564     if (!d->tabbedToolBar)
2565         return;
2566     d->tabbedToolBar->showMainMenu("project_welcome");
2567     KexiWelcomeAssistant* assistant = new KexiWelcomeAssistant(
2568         Kexi::recentProjects(), this);
2569     connect(assistant, SIGNAL(openProject(KexiProjectData,QString,bool*)),
2570             this, SLOT(openProject(KexiProjectData,QString,bool*)));
2571     d->tabbedToolBar->setMainMenuContent(assistant);
2572 }
2573 
2574 void
2575 KexiMainWindow::slotProjectSave()
2576 {
2577     if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) {
2578         return;
2579     }
2580     saveObject(currentWindow());
2581     updateAppCaption();
2582     invalidateActions();
2583 }
2584 
2585 void
2586 KexiMainWindow::slotProjectSaveAs()
2587 {
2588     if (!currentWindow() || currentWindow()->currentViewMode() == Kexi::DataViewMode) {
2589         return;
2590     }
2591     saveObject(currentWindow(), QString(), SaveObjectAs);
2592     updateAppCaption();
2593     invalidateActions();
2594 }
2595 
2596 void
2597 KexiMainWindow::slotProjectPrint()
2598 {
2599 #ifdef KEXI_QUICK_PRINTING_SUPPORT
2600     if (currentWindow() && currentWindow()->partItem())
2601         printItem(currentWindow()->partItem());
2602 #endif
2603 }
2604 
2605 void
2606 KexiMainWindow::slotProjectPrintPreview()
2607 {
2608 #ifdef KEXI_QUICK_PRINTING_SUPPORT
2609     if (currentWindow() && currentWindow()->partItem())
2610         printPreviewForItem(currentWindow()->partItem());
2611 #endif
2612 }
2613 
2614 void
2615 KexiMainWindow::slotProjectPageSetup()
2616 {
2617 #ifdef KEXI_QUICK_PRINTING_SUPPORT
2618     if (currentWindow() && currentWindow()->partItem())
2619         showPageSetupForItem(currentWindow()->partItem());
2620 #endif
2621 }
2622 
2623 void KexiMainWindow::slotProjectExportDataTable()
2624 {
2625     if (currentWindow() && currentWindow()->partItem())
2626         exportItemAsDataTable(currentWindow()->partItem());
2627 }
2628 
2629 void KexiMainWindow::slotProjectProperties()
2630 {
2631     if (!d->tabbedToolBar)
2632         return;
2633     d->tabbedToolBar->showMainMenu("project_properties");
2634     // dummy
2635     QLabel *dummy = KEXI_UNFINISHED_LABEL(actionCollection()->action("project_properties")->text());
2636     d->tabbedToolBar->setMainMenuContent(dummy);
2637     //! @todo load the implementation not the ui :)
2638 // ProjectSettingsUI u(this);
2639 // u.exec();
2640 }
2641 
2642 void KexiMainWindow::slotProjectImportExportOrSend()
2643 {
2644     if (!d->tabbedToolBar)
2645         return;
2646     d->tabbedToolBar->showMainMenu("project_import_export_send");
2647     KexiImportExportAssistant* assistant = new KexiImportExportAssistant(
2648         d->action_project_import_export_send,
2649         d->action_tools_import_project);
2650     connect(assistant, SIGNAL(importProject()), this, SLOT(slotToolsImportProject()));
2651     d->tabbedToolBar->setMainMenuContent(assistant);
2652 }
2653 
2654 void
2655 KexiMainWindow::slotProjectClose()
2656 {
2657     closeProject();
2658 }
2659 
2660 void KexiMainWindow::slotProjectRelations()
2661 {
2662     if (!d->prj)
2663         return;
2664     KexiWindow *w = KexiInternalPart::createKexiWindowInstance("org.kexi-project.relations", this);
2665     activateWindow(*w);
2666 }
2667 
2668 void KexiMainWindow::slotImportFile()
2669 {
2670     KEXI_UNFINISHED("Import: " + xi18n("From File..."));
2671 }
2672 
2673 void KexiMainWindow::slotImportServer()
2674 {
2675     KEXI_UNFINISHED("Import: " + xi18n("From Server..."));
2676 }
2677 
2678 void
2679 KexiMainWindow::slotProjectQuit()
2680 {
2681     if (~ closeProject())
2682         return;
2683     close();
2684 }
2685 
2686 void KexiMainWindow::slotActivateNavigator()
2687 {
2688     if (!d->navigator) {
2689         return;
2690     }
2691     d->navigator->setFocus();
2692 }
2693 
2694 void KexiMainWindow::slotActivateMainArea()
2695 {
2696     if (currentWindow())
2697         currentWindow()->setFocus();
2698 }
2699 
2700 void KexiMainWindow::slotActivatePropertyEditor()
2701 {
2702     if (!d->propEditor) {
2703         return;
2704     }
2705 
2706     if (d->propEditorTabWidget->currentWidget())
2707         d->propEditorTabWidget->currentWidget()->setFocus();
2708 }
2709 
2710 void KexiMainWindow::slotShowNavigator()
2711 {
2712     if (d->navDockWidget)
2713         d->navDockWidget->setVisible(!d->navDockWidget->isVisible());
2714 }
2715 
2716 void KexiMainWindow::slotShowPropertyEditor()
2717 {
2718     if (d->propEditorDockWidget)
2719         d->propEditorDockWidget->setVisible(!d->propEditorDockWidget->isVisible());
2720 }
2721 
2722 tristate KexiMainWindow::switchToViewMode(KexiWindow& window, Kexi::ViewMode viewMode)
2723 {
2724     const Kexi::ViewMode prevViewMode = currentWindow()->currentViewMode();
2725     if (prevViewMode == viewMode)
2726         return true;
2727     if (!activateWindow(window))
2728         return false;
2729     if (!currentWindow()) {
2730         return false;
2731     }
2732     if (&window != currentWindow())
2733         return false;
2734     if (!currentWindow()->supportsViewMode(viewMode)) {
2735         showErrorMessage(xi18nc("@info",
2736                                 "Selected view is not supported for <resource>%1</resource> object.",
2737                                 currentWindow()->partItem()->name()),
2738                          xi18nc("@info",
2739                                 "Selected view (%1) is not supported by this object type (%2).",
2740                                 Kexi::nameForViewMode(viewMode),
2741                                 currentWindow()->part()->info()->name()));
2742         return false;
2743     }
2744     updateCustomPropertyPanelTabs(currentWindow()->part(), prevViewMode,
2745                                   currentWindow()->part(), viewMode);
2746     tristate res = currentWindow()->switchToViewMode(viewMode);
2747     if (!res) {
2748         updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert
2749         showErrorMessage(xi18n("Switching to other view failed (%1).",
2750                               Kexi::nameForViewMode(viewMode)), currentWindow());
2751         return false;
2752     }
2753     if (~res) {
2754         updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert
2755         return cancelled;
2756     }
2757 
2758     activateWindow(window);
2759 
2760     invalidateSharedActions();
2761     invalidateProjectWideActions();
2762     d->updateFindDialogContents();
2763     d->updatePropEditorVisibility(viewMode);
2764     QString origTabToActivate;
2765     if (viewMode == Kexi::DesignViewMode) {
2766         // Save the orig tab: we want to back to design tab
2767         // when user moved to data view and then immediately to design view.
2768         origTabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier());
2769     }
2770     restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(), viewMode,
2771                              currentWindow()->partItem()->identifier());
2772     if (viewMode == Kexi::DesignViewMode) {
2773         activateDesignTab(currentWindow()->partItem()->pluginId());
2774         // Restore the saved tab to the orig one. restoreDesignTabIfNeeded() saved tools tab probably.
2775         d->tabsToActivateOnShow.insert(currentWindow()->partItem()->identifier(), origTabToActivate);
2776     }
2777 
2778     return true;
2779 }
2780 
2781 void KexiMainWindow::slotViewDataMode()
2782 {
2783     if (currentWindow())
2784         switchToViewMode(*currentWindow(), Kexi::DataViewMode);
2785 }
2786 
2787 void KexiMainWindow::slotViewDesignMode()
2788 {
2789     if (currentWindow())
2790         switchToViewMode(*currentWindow(), Kexi::DesignViewMode);
2791 }
2792 
2793 void KexiMainWindow::slotViewTextMode()
2794 {
2795     if (currentWindow())
2796         switchToViewMode(*currentWindow(), Kexi::TextViewMode);
2797 }
2798 
2799 //! Used to control if we're not Saving-As object under the original name
2800 class SaveAsObjectNameValidator : public KexiNameDialogValidator
2801 {
2802 public:
2803     explicit SaveAsObjectNameValidator(const QString &originalObjectName)
2804      : m_originalObjectName(originalObjectName)
2805     {
2806     }
2807     virtual bool validate(KexiNameDialog *dialog) const override {
2808         if (dialog->widget()->nameText() == m_originalObjectName) {
2809             KMessageBox::information(dialog,
2810                                      xi18nc("Could not save object under the original name.",
2811                                            "Could not save under the original name."));
2812             return false;
2813         }
2814         return true;
2815     }
2816 private:
2817     QString m_originalObjectName;
2818 };
2819 
2820 tristate KexiMainWindow::getNewObjectInfo(
2821     KexiPart::Item *partItem, const QString &originalName, KexiPart::Part *part,
2822     bool allowOverwriting, bool *overwriteNeeded, const QString& messageWhenAskingForName)
2823 {
2824     //data was never saved in the past -we need to create a new object at the backend
2825     KexiPart::Info *info = part->info();
2826     if (!d->nameDialog) {
2827         d->nameDialog = new KexiNameDialog(
2828             messageWhenAskingForName, this);
2829         //check if that name is allowed
2830         d->nameDialog->widget()->addNameSubvalidator(
2831             new KDbObjectNameValidator(project()->dbConnection()->driver()));
2832         d->nameDialog->buttonBox()->button(QDialogButtonBox::Ok)->setText(xi18nc("@action:button Save object", "Save"));
2833     } else {
2834         d->nameDialog->widget()->setMessageText(messageWhenAskingForName);
2835     }
2836     d->nameDialog->widget()->setCaptionText(partItem->caption());
2837     d->nameDialog->widget()->setNameText(partItem->name());
2838     d->nameDialog->setWindowTitle(xi18nc("@title:window", "Save Object As"));
2839     d->nameDialog->setDialogIcon(info->iconName());
2840     d->nameDialog->setAllowOverwriting(allowOverwriting);
2841     if (!originalName.isEmpty()) {
2842         d->nameDialog->setValidator(new SaveAsObjectNameValidator(originalName));
2843     }
2844     if (d->nameDialog->execAndCheckIfObjectExists(*project(), *part, overwriteNeeded)
2845         != QDialog::Accepted)
2846     {
2847         return cancelled;
2848     }
2849 
2850     // close window of object that will be overwritten
2851     if (*overwriteNeeded) {
2852         KexiPart::Item* overwrittenItem = project()->item(info, d->nameDialog->widget()->nameText());
2853         if (overwrittenItem) {
2854             KexiWindow * openedWindow = d->openedWindowFor(overwrittenItem->identifier());
2855             if (openedWindow) {
2856                 const tristate res = closeWindow(openedWindow);
2857                 if (res != true) {
2858                     return res;
2859                 }
2860             }
2861         }
2862     }
2863 
2864     //update name and caption
2865     partItem->setName(d->nameDialog->widget()->nameText());
2866     partItem->setCaption(d->nameDialog->widget()->captionText());
2867     return true;
2868 }
2869 
2870 //! Used to delete part item on exit from block
2871 class PartItemDeleter : public QScopedPointer<KexiPart::Item>
2872 {
2873     public:
2874         explicit PartItemDeleter(KexiProject *prj) : m_prj(prj) {}
2875         ~PartItemDeleter() {
2876             if (!isNull()) {
2877                 m_prj->deleteUnstoredItem(take());
2878             }
2879         }
2880     private:
2881         KexiProject *m_prj;
2882 };
2883 
2884 static void showSavingObjectFailedErrorMessage(KexiMainWindow *wnd, KexiPart::Item *item)
2885 {
2886     wnd->showErrorMessage(
2887         xi18nc("@info Saving object failed",
2888               "Saving <resource>%1</resource> object failed.", item->name()),
2889               wnd->currentWindow());
2890 }
2891 
2892 tristate KexiMainWindow::saveObject(KexiWindow *window, const QString& messageWhenAskingForName,
2893                                     SaveObjectOptions options)
2894 {
2895     tristate res;
2896     bool saveAs = options & SaveObjectAs;
2897     if (!saveAs && !window->neverSaved()) {
2898         //data was saved in the past -just save again
2899         res = window->storeData(options & DoNotAsk);
2900         if (!res) {
2901             showSavingObjectFailedErrorMessage(this, window->partItem());
2902         }
2903         return res;
2904     }
2905     if (saveAs && window->neverSaved()) {
2906         //if never saved, saveAs == save
2907         saveAs = false;
2908     }
2909 
2910     const int oldItemID = window->partItem()->identifier();
2911 
2912     KexiPart::Item *partItem;
2913     KexiView::StoreNewDataOptions storeNewDataOptions;
2914     PartItemDeleter itemDeleter(d->prj);
2915     if (saveAs) {
2916         partItem = d->prj->createPartItem(window->part());
2917         if (!partItem) {
2918             //! @todo error
2919             return false;
2920         }
2921         itemDeleter.reset(partItem);
2922     }
2923     else {
2924         partItem = window->partItem();
2925     }
2926 
2927     bool overwriteNeeded;
2928     res = getNewObjectInfo(partItem, saveAs ? window->partItem()->name() : QString(),
2929                            window->part(), true /*allowOverwriting*/,
2930                            &overwriteNeeded, messageWhenAskingForName);
2931     if (res != true)
2932         return res;
2933     if (overwriteNeeded) {
2934         storeNewDataOptions |= KexiView::OverwriteExistingData;
2935     }
2936 
2937     if (saveAs) {
2938         res = window->storeDataAs(partItem, storeNewDataOptions);
2939     }
2940     else {
2941         res = window->storeNewData(storeNewDataOptions);
2942     }
2943 
2944     if (~res)
2945         return cancelled;
2946     if (!res) {
2947         showSavingObjectFailedErrorMessage(this, partItem);
2948         return false;
2949     }
2950 
2951     d->updateWindowId(window, oldItemID);
2952     invalidateProjectWideActions();
2953     itemDeleter.take();
2954     return true;
2955 }
2956 
2957 tristate KexiMainWindow::closeWindow(KexiWindow *window)
2958 {
2959     return closeWindow(window ? window : currentWindow(), true);
2960 }
2961 
2962 tristate KexiMainWindow::closeCurrentWindow()
2963 {
2964     return closeWindow(0);
2965 }
2966 
2967 tristate KexiMainWindow::closeWindowForTab(int tabIndex)
2968 {
2969     KexiWindow* window = windowForTab(tabIndex);
2970     if (!window)
2971         return false;
2972     return closeWindow(window);
2973 }
2974 
2975 tristate KexiMainWindow::closeWindow(KexiWindow *window, bool layoutTaskBar, bool doNotSaveChanges)
2976 {
2977 //! @todo KEXI3 KexiMainWindow::closeWindow()
2978     ///@note Q_UNUSED layoutTaskBar
2979     Q_UNUSED(layoutTaskBar);
2980 
2981     if (!window)
2982         return true;
2983     if (d->insideCloseWindow)
2984         return true;
2985 
2986     const int previousItemId = window->partItem()->identifier();
2987 
2988 #ifndef KEXI_NO_PENDING_DIALOGS
2989     d->addItemToPendingWindows(window->partItem(), Private::WindowClosingJob);
2990 #endif
2991 
2992     d->insideCloseWindow = true;
2993 
2994     if (window == currentWindow() && !window->isAttached()) {
2995         if (d->propEditor) {
2996             // ah, closing detached window - better switch off property buffer right now...
2997             d->propertySet = 0;
2998             d->propEditor->editor()->changeSet(0);
2999         }
3000     }
3001 
3002     bool remove_on_closing = window->partItem() ? window->partItem()->neverSaved() : false;
3003     if (window->isDirty() && !d->forceWindowClosing && !doNotSaveChanges) {
3004         //more accurate tool tips and what's this
3005         KGuiItem saveChanges(KStandardGuiItem::save());
3006         saveChanges.setToolTip(xi18n("Save changes"));
3007         saveChanges.setWhatsThis(
3008             xi18nc("@info", "Saves all recent changes made in <resource>%1</resource> object.",
3009                    window->partItem()->name()));
3010         KGuiItem discardChanges(KStandardGuiItem::discard());
3011         discardChanges.setWhatsThis(
3012             xi18nc("@info", "Discards all recent changes made in <resource>%1</resource> object.",
3013                    window->partItem()->name()));
3014 
3015         //dialog's data is dirty:
3016         //--adidional message, e.g. table designer will return
3017         //  "Note: This table is already filled with data which will be removed."
3018         //  if the window is in design view mode.
3019         const KLocalizedString additionalMessage(
3020             window->part()->i18nMessage(":additional message before saving design", window));
3021         QString additionalMessageString;
3022         if (!additionalMessage.isEmpty())
3023             additionalMessageString = additionalMessage.toString();
3024 
3025         if (additionalMessageString.startsWith(':'))
3026             additionalMessageString.clear();
3027         if (!additionalMessageString.isEmpty())
3028             additionalMessageString = "<p>" + additionalMessageString + "</p>";
3029 
3030         const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this,
3031                                 "<p>"
3032                                 + window->part()->i18nMessage("Design of object <resource>%1</resource> has been modified.", window)
3033                                 .subs(window->partItem()->name()).toString()
3034                                 + "</p><p>" + xi18n("Do you want to save changes?") + "</p>"
3035                                 + additionalMessageString /*may be empty*/,
3036                                 QString(),
3037                                 saveChanges,
3038                                 discardChanges);
3039         if (questionRes == KMessageBox::Cancel) {
3040 #ifndef KEXI_NO_PENDING_DIALOGS
3041             d->removePendingWindow(window->id());
3042 #endif
3043             d->insideCloseWindow = false;
3044             d->windowsToClose.clear(); //give up with 'close all'
3045             return cancelled;
3046         }
3047         if (questionRes == KMessageBox::Yes) {
3048             //save it
3049             tristate res = saveObject(window, QString(), DoNotAsk);
3050             if (!res || ~res) {
3051 //! @todo show error info; (retry/ignore/cancel)
3052 #ifndef KEXI_NO_PENDING_DIALOGS
3053                 d->removePendingWindow(window->id());
3054 #endif
3055                 d->insideCloseWindow = false;
3056                 d->windowsToClose.clear(); //give up with 'close all'
3057                 return res;
3058             }
3059             remove_on_closing = false;
3060         }
3061     }
3062 
3063     const int window_id = window->id(); //remember now, because removeObject() can destruct partitem object
3064     if (remove_on_closing) {
3065         //we won't save this object, and it was never saved -remove it
3066         if (!removeObject(window->partItem(), true)) {
3067 #ifndef KEXI_NO_PENDING_DIALOGS
3068             d->removePendingWindow(window->id());
3069 #endif
3070             //msg?
3071             //! @todo ask if we'd continue and return true/false
3072             d->insideCloseWindow = false;
3073             d->windowsToClose.clear(); //give up with 'close all'
3074             return false;
3075         }
3076     } else {
3077         //not dirty now
3078         if (d->navigator) {
3079             d->navigator->updateItemName(*window->partItem(), false);
3080         }
3081     }
3082 
3083     hideDesignTab(previousItemId, QString());
3084 
3085     d->removeWindow(window_id);
3086     d->setWindowContainerExistsFor(window->partItem()->identifier(), false);
3087     QWidget *windowContainer = window->parentWidget();
3088     d->mainWidget->tabWidget()->removeTab(
3089         d->mainWidget->tabWidget()->indexOf(windowContainer));
3090 
3091 #ifdef KEXI_QUICK_PRINTING_SUPPORT
3092     //also remove from 'print setup dialogs' cache, if needed
3093     int printedObjectID = 0;
3094     if (d->pageSetupWindowItemID2dataItemID_map.contains(window_id))
3095         printedObjectID = d->pageSetupWindowItemID2dataItemID_map[ window_id ];
3096     d->pageSetupWindows.remove(printedObjectID);
3097 #endif
3098 
3099     delete windowContainer;
3100 
3101     //focus navigator if nothing else available
3102     if (d->openedWindowsCount() == 0) {
3103         if (d->navigator) {
3104             d->navigator->setFocus();
3105         }
3106         d->updatePropEditorVisibility(Kexi::NoViewMode);
3107     }
3108 
3109     invalidateActions();
3110     d->insideCloseWindow = false;
3111     if (!d->windowsToClose.isEmpty()) {//continue 'close all'
3112         KexiWindow* w = d->windowsToClose.takeAt(0);
3113         closeWindow(w, true);
3114     }
3115 
3116 #ifndef KEXI_NO_PENDING_DIALOGS
3117     d->removePendingWindow(window_id);
3118 
3119     //perform pending global action that was suspended:
3120     if (!d->pendingWindowsExist()) {
3121         d->executeActionWhenPendingJobsAreFinished();
3122     }
3123 #endif
3124     d->mainWidget->slotCurrentTabIndexChanged(d->mainWidget->tabWidget()->currentIndex());
3125     showDesignTabIfNeeded(0);
3126 
3127     if (currentWindow()) {
3128         restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(),
3129                                  currentWindow()->currentViewMode(),
3130                                  0);
3131     }
3132     d->tabsToActivateOnShow.remove(previousItemId);
3133     return true;
3134 }
3135 
3136 QWidget* KexiMainWindow::findWindow(QWidget *w)
3137 {
3138     while (w && !acceptsSharedActions(w)) {
3139         if (w == d->propEditorDockWidget)
3140             return currentWindow();
3141         w = w->parentWidget();
3142     }
3143     return w;
3144 }
3145 
3146 KexiWindow* KexiMainWindow::openedWindowFor(int identifier)
3147 {
3148     return d->openedWindowFor(identifier);
3149 }
3150 
3151 KexiWindow* KexiMainWindow::openedWindowFor(const KexiPart::Item* item)
3152 {
3153     return item ? openedWindowFor(item->identifier()) : 0;
3154 }
3155 
3156 KDbQuerySchema* KexiMainWindow::unsavedQuery(int queryId)
3157 {
3158     KexiWindow * queryWindow = openedWindowFor(queryId);
3159     if (!queryWindow || !queryWindow->isDirty()) {
3160         return 0;
3161     }
3162 
3163     return queryWindow->part()->currentQuery(queryWindow->viewForMode(Kexi::DataViewMode));
3164 }
3165 
3166 QList<QVariant> KexiMainWindow::currentParametersForQuery(int queryId) const
3167 {
3168     KexiWindow *queryWindow = d->openedWindowFor(queryId);
3169     if (!queryWindow) {
3170         return QList<QVariant>();
3171     }
3172 
3173     KexiView *view = queryWindow->viewForMode(Kexi::DataViewMode);
3174     if (!view) {
3175         return QList<QVariant>();
3176     }
3177 
3178     return view->currentParameters();
3179 }
3180 
3181 bool KexiMainWindow::acceptsSharedActions(QObject *w)
3182 {
3183     return w->inherits("KexiWindow") || w->inherits("KexiView");
3184 }
3185 
3186 bool KexiMainWindow::openingAllowed(KexiPart::Item* item, Kexi::ViewMode viewMode, QString* errorMessage)
3187 {
3188     //qDebug() << viewMode;
3189     //! @todo this can be more complex once we deliver ACLs...
3190     if (!d->userMode)
3191         return true;
3192     KexiPart::Part * part = Kexi::partManager().partForPluginId(item->pluginId());
3193     if (!part) {
3194         if (errorMessage) {
3195             *errorMessage = Kexi::partManager().result().message();
3196         }
3197     }
3198     //qDebug() << part << item->pluginId();
3199     //if (part)
3200     //    qDebug() << item->pluginId() << part->info()->supportedUserViewModes();
3201     return part && (part->info()->supportedUserViewModes() & viewMode);
3202 }
3203 
3204 KexiWindow *
3205 KexiMainWindow::openObject(const QString& pluginId, const QString& name,
3206                            Kexi::ViewMode viewMode, bool *openingCancelled, QMap<QString, QVariant>* staticObjectArgs)
3207 {
3208     KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name);
3209     if (!item)
3210         return 0;
3211     return openObject(item, viewMode, openingCancelled, staticObjectArgs);
3212 }
3213 
3214 KexiWindow *
3215 KexiMainWindow::openObject(KexiPart::Item* item, Kexi::ViewMode viewMode, bool *openingCancelled,
3216                            QMap<QString, QVariant>* staticObjectArgs, QString* errorMessage)
3217 {
3218     Q_ASSERT(openingCancelled);
3219     if (!d->prj || !item) {
3220         return 0;
3221     }
3222 
3223     if (!openingAllowed(item, viewMode, errorMessage)) {
3224         if (errorMessage)
3225             *errorMessage = xi18nc(
3226                                 "opening is not allowed in \"data view/design view/text view\" mode",
3227                                 "opening is not allowed in \"%1\" mode", Kexi::nameForViewMode(viewMode));
3228         *openingCancelled = true;
3229         return 0;
3230     }
3231     //qDebug() << d->prj << item;
3232 
3233     KexiWindow *prevWindow = currentWindow();
3234 
3235     KexiUtils::WaitCursor wait;
3236 #ifndef KEXI_NO_PENDING_DIALOGS
3237     Private::PendingJobType pendingType;
3238     KexiWindow *window = d->openedWindowFor(item, pendingType);
3239     if (pendingType != Private::NoJob) {
3240         *openingCancelled = true;
3241         return 0;
3242     }
3243 #else
3244     KexiWindow *window = openedWindowFor(item);
3245 #endif
3246     int previousItemId = currentWindow() ? currentWindow()->partItem()->identifier() : 0;
3247     *openingCancelled = false;
3248 
3249     bool alreadyOpened = false;
3250     KexiWindowContainer *windowContainer = 0;
3251 
3252     if (window) {
3253         if (viewMode != window->currentViewMode()) {
3254             if (true != switchToViewMode(*window, viewMode))
3255                 return 0;
3256         } else
3257             activateWindow(*window);
3258         alreadyOpened = true;
3259     } else {
3260         if (d->windowContainerExistsFor(item->identifier())) {
3261             // window not yet present but window container exists: return 0 and wait
3262             return 0;
3263         }
3264         KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId());
3265         d->updatePropEditorVisibility(viewMode, part ? part->info() : 0);
3266         //update tabs before opening
3267         updateCustomPropertyPanelTabs(currentWindow() ? currentWindow()->part() : 0,
3268                                       currentWindow() ? currentWindow()->currentViewMode() : Kexi::NoViewMode,
3269                                       part, viewMode);
3270 
3271         // open new tab earlier
3272         windowContainer = new KexiWindowContainer(d->mainWidget->tabWidget());
3273         d->setWindowContainerExistsFor(item->identifier(), true);
3274         const int tabIndex = d->mainWidget->tabWidget()->addTab(
3275             windowContainer,
3276             QIcon::fromTheme(part ? part->info()->iconName() : QString()),
3277             KexiWindow::windowTitleForItem(*item));
3278         d->mainWidget->tabWidget()->setTabToolTip(tabIndex, KexiPart::fullCaptionForItem(item, part));
3279         QString whatsThisText;
3280         if (part) {
3281             whatsThisText = xi18nc("@info",
3282                                    "Tab for <resource>%1</resource> (%2).",
3283                                    item->captionOrName(), part->info()->name());
3284         }
3285         else {
3286             whatsThisText = xi18nc("@info",
3287                                    "Tab for <resource>%1</resource>.", item->captionOrName());
3288         }
3289         d->mainWidget->tabWidget()->setTabWhatsThis(tabIndex, whatsThisText);
3290         d->mainWidget->tabWidget()->setCurrentWidget(windowContainer);
3291 
3292 #ifndef KEXI_NO_PENDING_DIALOGS
3293         d->addItemToPendingWindows(item, Private::WindowOpeningJob);
3294 #endif
3295         window = d->prj->openObject(windowContainer, item, viewMode, staticObjectArgs);
3296         if (window) {
3297             windowContainer->setWindow(window);
3298             // update text and icon
3299             d->mainWidget->tabWidget()->setTabText(
3300                 d->mainWidget->tabWidget()->indexOf(windowContainer),
3301                 window->windowTitle());
3302             d->mainWidget->tabWidget()->setTabIcon(
3303                 d->mainWidget->tabWidget()->indexOf(windowContainer),
3304                 window->windowIcon());
3305         }
3306     }
3307 
3308     if (!window || !activateWindow(*window)) {
3309 #ifndef KEXI_NO_PENDING_DIALOGS
3310         d->removePendingWindow(item->identifier());
3311 #endif
3312         d->setWindowContainerExistsFor(item->identifier(), false);
3313         d->mainWidget->tabWidget()->removeTab(
3314             d->mainWidget->tabWidget()->indexOf(windowContainer));
3315         delete windowContainer;
3316         updateCustomPropertyPanelTabs(0, Kexi::NoViewMode); //revert
3317         //! @todo add error msg...
3318         return 0;
3319     }
3320 
3321     if (viewMode != window->currentViewMode())
3322         invalidateSharedActions();
3323 
3324 #ifndef KEXI_NO_PENDING_DIALOGS
3325     d->removePendingWindow(window->id());
3326 
3327     //perform pending global action that was suspended:
3328     if (!d->pendingWindowsExist()) {
3329         d->executeActionWhenPendingJobsAreFinished();
3330     }
3331 #endif
3332     if (window && !alreadyOpened) {
3333         // Call switchToViewMode() and propertySetSwitched() again here because
3334         // this is the time when then new window is the current one - previous call did nothing.
3335         switchToViewMode(*window, window->currentViewMode());
3336         currentWindow()->selectedView()->propertySetSwitched();
3337     }
3338     invalidateProjectWideActions();
3339     restoreDesignTabIfNeeded(item->pluginId(), viewMode, previousItemId);
3340     activateDesignTabIfNeeded(item->pluginId(), viewMode);
3341     QString origTabToActivate;
3342     if (prevWindow) {
3343         // Save the orig tab for prevWindow that was stored in the restoreDesignTabIfNeeded() call above
3344         origTabToActivate = d->tabsToActivateOnShow.value(prevWindow->partItem()->identifier());
3345     }
3346     activeWindowChanged(window, prevWindow);
3347     if (prevWindow) {
3348         // Restore the orig tab
3349         d->tabsToActivateOnShow.insert(prevWindow->partItem()->identifier(), origTabToActivate);
3350     }
3351     return window;
3352 }
3353 
3354 KexiWindow *
3355 KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode)
3356 {
3357     bool openingCancelled;
3358     return openObjectFromNavigator(item, viewMode, &openingCancelled);
3359 }
3360 
3361 KexiWindow *
3362 KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode viewMode,
3363                                         bool *openingCancelled)
3364 {
3365     Q_ASSERT(openingCancelled);
3366     if (!openingAllowed(item, viewMode)) {
3367         *openingCancelled = true;
3368         return 0;
3369     }
3370     if (!d->prj || !item)
3371         return 0;
3372 #ifndef KEXI_NO_PENDING_DIALOGS
3373     Private::PendingJobType pendingType;
3374     KexiWindow *window = d->openedWindowFor(item, pendingType);
3375     if (pendingType != Private::NoJob) {
3376         *openingCancelled = true;
3377         return 0;
3378     }
3379 #else
3380     KexiWindow *window = openedWindowFor(item);
3381 #endif
3382     *openingCancelled = false;
3383     if (window) {
3384         if (activateWindow(*window)) {
3385             return window;
3386         }
3387     }
3388     //if DataViewMode is not supported, try Design, then Text mode (currently useful for script part)
3389     KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId());
3390     if (!part)
3391         return 0;
3392     if (viewMode == Kexi::DataViewMode && !(part->info()->supportedViewModes() & Kexi::DataViewMode)) {
3393         if (part->info()->supportedViewModes() & Kexi::DesignViewMode)
3394             return openObjectFromNavigator(item, Kexi::DesignViewMode, openingCancelled);
3395         else if (part->info()->supportedViewModes() & Kexi::TextViewMode)
3396             return openObjectFromNavigator(item, Kexi::TextViewMode, openingCancelled);
3397     }
3398     //do the same as in openObject()
3399     return openObject(item, viewMode, openingCancelled);
3400 }
3401 
3402 tristate KexiMainWindow::closeObject(KexiPart::Item* item)
3403 {
3404 #ifndef KEXI_NO_PENDING_DIALOGS
3405     Private::PendingJobType pendingType;
3406     KexiWindow *window = d->openedWindowFor(item, pendingType);
3407     if (pendingType == Private::WindowClosingJob)
3408         return true;
3409     else if (pendingType == Private::WindowOpeningJob)
3410         return cancelled;
3411 #else
3412     KexiWindow *window = openedWindowFor(item);
3413 #endif
3414     if (!window)
3415         return cancelled;
3416     return closeWindow(window);
3417 }
3418 
3419 bool KexiMainWindow::newObject(KexiPart::Info *info, bool* openingCancelled)
3420 {
3421     Q_ASSERT(openingCancelled);
3422     if (d->userMode) {
3423         *openingCancelled = true;
3424         return false;
3425     }
3426     *openingCancelled = false;
3427     if (!d->prj || !info)
3428         return false;
3429     KexiPart::Part *part = Kexi::partManager().part(info);
3430     if (!part)
3431         return false;
3432 
3433     KexiPart::Item *it = d->prj->createPartItem(info);
3434     if (!it) {
3435         //! @todo error
3436         return false;
3437     }
3438 
3439     if (!it->neverSaved()) { //only add stored objects to the browser
3440         d->navigator->model()->slotAddItem(it);
3441     }
3442     Kexi::ViewMode viewMode = (info->supportedViewModes() & Kexi::DesignViewMode)
3443             ? Kexi::DesignViewMode
3444             : Kexi::TextViewMode;
3445     return openObject(it, viewMode, openingCancelled);
3446 }
3447 
3448 tristate KexiMainWindow::removeObject(KexiPart::Item *item, bool dontAsk)
3449 {
3450     if (d->userMode)
3451         return cancelled;
3452     if (!d->prj || !item)
3453         return false;
3454 
3455     KexiPart::Part *part = Kexi::partManager().partForPluginId(item->pluginId());
3456     if (!part)
3457         return false;
3458 
3459     if (!dontAsk) {
3460         if (KMessageBox::No == KMessageBox::questionYesNo(this,
3461                 xi18nc("@info Delete <objecttype> <objectname>?",
3462                       "<para>Do you want to permanently delete the following object?<nl/>"
3463                       "<nl/>%1 <resource>%2</resource></para>"
3464                       "<para><note>If you click <interface>Delete</interface>, "
3465                       "you will not be able to undo the deletion.</note></para>",
3466                       part->info()->name(), item->name()),
3467                 xi18nc("@title:window Delete Object %1.",
3468                       "Delete <resource>%1</resource>?", item->name()),
3469                 KStandardGuiItem::del(),
3470                 KStandardGuiItem::no(), QString(), KMessageBox::Notify | KMessageBox::Dangerous))
3471         {
3472             return cancelled;
3473         }
3474     }
3475 
3476     tristate res = true;
3477 #ifdef KEXI_QUICK_PRINTING_SUPPORT
3478     //also close 'print setup' dialog for this item, if any
3479     KexiWindow * pageSetupWindow = d->pageSetupWindows[ item->identifier()];
3480     const bool oldInsideCloseWindow = d->insideCloseWindow;
3481     {
3482         d->insideCloseWindow = false;
3483         if (pageSetupWindow)
3484             res = closeWindow(pageSetupWindow);
3485     }
3486     d->insideCloseWindow = oldInsideCloseWindow;
3487     if (!res || ~res) {
3488         return res;
3489     }
3490 #endif
3491 
3492 #ifndef KEXI_NO_PENDING_DIALOGS
3493     Private::PendingJobType pendingType;
3494     KexiWindow *window = d->openedWindowFor(item, pendingType);
3495     if (pendingType != Private::NoJob) {
3496         return cancelled;
3497     }
3498 #else
3499     KexiWindow *window = openedWindowFor(item);
3500 #endif
3501 
3502     if (window) {//close existing window
3503         const bool tmp = d->forceWindowClosing;
3504         d->forceWindowClosing = true;
3505         res = closeWindow(window);
3506         d->forceWindowClosing = tmp; //restore
3507         if (!res || ~res) {
3508             return res;
3509         }
3510     }
3511 
3512 #ifdef KEXI_QUICK_PRINTING_SUPPORT
3513     //in case the dialog is a 'print setup' dialog, also update d->pageSetupWindows
3514     int dataItemID = d->pageSetupWindowItemID2dataItemID_map[item->identifier()];
3515     d->pageSetupWindowItemID2dataItemID_map.remove(item->identifier());
3516     d->pageSetupWindows.remove(dataItemID);
3517 #endif
3518 
3519     if (!d->prj->removeObject(item)) {
3520         //! @todo better msg
3521         showSorryMessage(xi18n("Could not delete object."));
3522         return false;
3523     }
3524     return true;
3525 }
3526 
3527 void KexiMainWindow::renameObject(KexiPart::Item *item, const QString& _newName, bool *success)
3528 {
3529     Q_ASSERT(success);
3530     if (d->userMode) {
3531         *success = false;
3532         return;
3533     }
3534     QString newName = _newName.trimmed();
3535     if (newName.isEmpty()) {
3536         showSorryMessage(xi18n("Could not set empty name for this object."));
3537         *success = false;
3538         return;
3539     }
3540 
3541     KexiWindow *window = openedWindowFor(item);
3542     if (window) {
3543         QString msg = xi18nc("@info",
3544                             "<para>Before renaming object <resource>%1</resource> it should be closed.</para>"
3545                             "<para>Do you want to close it?</para>",
3546                             item->name());
3547         KGuiItem closeAndRenameItem(KStandardGuiItem::closeWindow());
3548         closeAndRenameItem.setText(xi18n("Close Window and Rename"));
3549         const int r = KMessageBox::questionYesNo(this, msg, QString(), closeAndRenameItem,
3550                                            KStandardGuiItem::cancel());
3551         if (r != KMessageBox::Yes) {
3552             *success = false;
3553             return;
3554         }
3555         const tristate closeResult = closeWindow(window);
3556         if (closeResult != true) {
3557             *success = false;
3558             return;
3559         }
3560     }
3561     setMessagesEnabled(false); //to avoid double messages
3562     const bool res = d->prj->renameObject(item, newName);
3563     setMessagesEnabled(true);
3564     if (!res) {
3565         showErrorMessage(xi18nc("@info", "Renaming object <resource>%1</resource> failed.",
3566                                 newName), d->prj);
3567         *success = false;
3568         return;
3569     }
3570     *success = true;
3571 }
3572 
3573 void KexiMainWindow::setObjectCaption(KexiPart::Item *item, const QString& _newCaption, bool *success)
3574 {
3575     Q_ASSERT(success);
3576     if (d->userMode) {
3577         *success = false;
3578         return;
3579     }
3580     QString newCaption = _newCaption.trimmed();
3581     setMessagesEnabled(false); //to avoid double messages
3582     const bool res = d->prj->setObjectCaption(item, newCaption);
3583     setMessagesEnabled(true);
3584     if (!res) {
3585         showErrorMessage(xi18nc("@info",
3586                                 "Setting caption for object <resource>%1</resource> failed.",
3587                                 newCaption), d->prj);
3588         *success = false;
3589         return;
3590     }
3591     *success = true;
3592 }
3593 
3594 void KexiMainWindow::slotObjectRenamed(const KexiPart::Item &item, const QString& oldName)
3595 {
3596     Q_UNUSED(oldName);
3597 #ifndef KEXI_NO_PENDING_DIALOGS
3598     Private::PendingJobType pendingType;
3599     KexiWindow *window = d->openedWindowFor(&item, pendingType);
3600     if (pendingType != Private::NoJob)
3601         return;
3602 #else
3603     KexiWindow *window = openedWindowFor(&item);
3604 #endif
3605     if (!window)
3606         return;
3607 
3608     //change item
3609     window->updateCaption();
3610     if (static_cast<KexiWindow*>(currentWindow()) == window)//optionally, update app. caption
3611         updateAppCaption();
3612 }
3613 
3614 void KexiMainWindow::acceptPropertySetEditing()
3615 {
3616     if (d->propEditor)
3617         d->propEditor->editor()->acceptInput();
3618 }
3619 
3620 void KexiMainWindow::propertySetSwitched(KexiWindow *window, bool force,
3621         bool preservePrevSelection, bool sortedProperties, const QByteArray& propertyToSelect)
3622 {
3623     KexiWindow* _currentWindow = currentWindow();
3624     //qDebug() << "currentWindow(): "
3625     //    << (_currentWindow ? _currentWindow->windowTitle() : QString("NULL"))
3626     //    << " window: " << (window ? window->windowTitle() : QString("NULL"));
3627     if (_currentWindow && _currentWindow != window) {
3628         d->propertySet = 0; //we'll need to move to another prop. set
3629         return;
3630     }
3631     if (d->propEditor) {
3632         KPropertySet *newSet = _currentWindow ? _currentWindow->propertySet() : 0;
3633         if (!newSet || (force || static_cast<KPropertySet*>(d->propertySet) != newSet)) {
3634             d->propertySet = newSet;
3635             if (preservePrevSelection || force) {
3636                 KPropertyEditorView::SetOptions options;
3637                 if (preservePrevSelection) {
3638                     options |= KPropertyEditorView::SetOption::PreservePreviousSelection;
3639                 }
3640                 if (sortedProperties) {
3641                     options |= KPropertyEditorView::SetOption::AlphabeticalOrder;
3642                 }
3643 
3644                 if (propertyToSelect.isEmpty()) {
3645                     d->propEditor->editor()->changeSet(d->propertySet, options);
3646                 }
3647                 else {
3648                     d->propEditor->editor()->changeSet(d->propertySet, propertyToSelect, options);
3649                 }
3650             }
3651         }
3652     }
3653 }
3654 
3655 void KexiMainWindow::slotDirtyFlagChanged(KexiWindow* window)
3656 {
3657     KexiPart::Item *item = window->partItem();
3658     //update text in navigator and app. caption
3659     if (!d->userMode) {
3660         d->navigator->updateItemName(*item, window->isDirty());
3661     }
3662 
3663     invalidateActions();
3664     updateAppCaption();
3665     d->mainWidget->tabWidget()->setTabText(
3666         d->mainWidget->tabWidget()->indexOf(window->parentWidget()),
3667         window->windowTitle());
3668 }
3669 
3670 void KexiMainWindow::slotTipOfTheDay()
3671 {
3672     //! @todo
3673 }
3674 
3675 void KexiMainWindow::slotReportBug()
3676 {
3677     KexiBugReportDialog bugReport(this);
3678     bugReport.exec();
3679 }
3680 
3681 bool KexiMainWindow::userMode() const
3682 {
3683     return d->userMode;
3684 }
3685 
3686 void
3687 KexiMainWindow::setupUserActions()
3688 {
3689 }
3690 
3691 void KexiMainWindow::slotToolsImportProject()
3692 {
3693     if (d->tabbedToolBar)
3694         d->tabbedToolBar->hideMainMenu();
3695     showProjectMigrationWizard(QString(), QString());
3696 }
3697 
3698 void KexiMainWindow::slotToolsImportTables()
3699 {
3700     if (project()) {
3701         QMap<QString, QString> args;
3702         QDialog *dlg = KexiInternalPart::createModalDialogInstance("org.kexi-project.migration", "importtable", this, 0, &args);
3703         if (!dlg)
3704             return; //error msg has been shown by KexiInternalPart
3705 
3706         const int result = dlg->exec();
3707         delete dlg;
3708         if (result != QDialog::Accepted)
3709             return;
3710 
3711         QString destinationTableName(args["destinationTableName"]);
3712         if (!destinationTableName.isEmpty()) {
3713             QString pluginId = "org.kexi-project.table";
3714             bool openingCancelled;
3715             KexiMainWindow::openObject(pluginId, destinationTableName, Kexi::DataViewMode, &openingCancelled);
3716         }
3717     }
3718 }
3719 
3720 void KexiMainWindow::slotToolsCompactDatabase()
3721 {
3722     KexiProjectData *data = 0;
3723     KDbDriver *drv = 0;
3724     const bool projectWasOpened = d->prj;
3725 
3726     if (!d->prj) {
3727         //! @todo Support compacting of non-opened projects
3728         return;
3729 #if 0
3730         KexiStartupDialog dlg(
3731             KexiStartupDialog::OpenExisting, 0, Kexi::connset(), this);
3732 
3733         if (dlg.exec() != QDialog::Accepted)
3734             return;
3735 
3736         if (dlg.selectedFile().isEmpty()) {
3737 //! @todo add support for server based if needed?
3738             return;
3739         }
3740         KDbConnectionData cdata;
3741         cdata.setDatabaseName(dlg.selectedFile());
3742 
3743         //detect driver name for the selected file
3744         KexiStartupData::Import detectedImportAction;
3745         QString detectedDriverId;
3746         tristate res = KexiStartupHandler::detectActionForFile(
3747                            &detectedImportAction, &detectedDriverId,
3748                            QString() /*suggestedDriverId*/, cdata.databaseName(), 0,
3749                            KexiStartupHandler::SkipMessages | KexiStartupHandler::ThisIsAProjectFile
3750                            | KexiStartupHandler::DontConvert);
3751 
3752         if (true == res && !detectedImportAction) {
3753             cdata.setDriverId(detectedDriverId);
3754             drv = Kexi::driverManager().driver(cdata.driverId());
3755         }
3756         if (!drv || !(drv->features() & KDbDriver::CompactingDatabaseSupported)) {
3757             KMessageBox::information(this,
3758                                      xi18n("Compacting database file <filename>%1</filename> is not supported.",
3759                                            QDir::toNativeSeparators(cdata.databaseName())));
3760             return;
3761         }
3762         data = new KexiProjectData(cdata);
3763 #endif
3764     } else {
3765         //sanity
3766         if (!(d->prj && d->prj->dbConnection()
3767                 && (d->prj->dbConnection()->driver()->features() & KDbDriver::CompactingDatabaseSupported)))
3768             return;
3769 
3770         KGuiItem yesItem(KStandardGuiItem::cont());
3771         yesItem.setText(xi18nc("@action:button Compact database", "Compact"));
3772         if (KMessageBox::Yes != KMessageBox::questionYesNo(this,
3773                 xi18n("The current project has to be closed before compacting the database. "
3774                      "It will be open again after compacting.\n\nDo you want to continue?"),
3775                 QString(), yesItem, KStandardGuiItem::cancel()))
3776         {
3777             return;
3778         }
3779         data = new KexiProjectData(*d->prj->data()); // a copy
3780         drv = d->prj->dbConnection()->driver();
3781         const tristate res = closeProject();
3782         if (~res || !res) {
3783             delete data;
3784             return;
3785         }
3786     }
3787 
3788     if (!drv->adminTools().vacuum(*data->connectionData(), data->databaseName())) {
3789       showErrorMessage(QString(), &drv->adminTools());
3790     }
3791 
3792     if (projectWasOpened)
3793       openProject(*data);
3794 
3795     delete data;
3796 }
3797 
3798 tristate KexiMainWindow::showProjectMigrationWizard(const QString& mimeType, const QString& databaseName)
3799 {
3800     return d->showProjectMigrationWizard(mimeType, databaseName, 0);
3801 }
3802 
3803 tristate KexiMainWindow::showProjectMigrationWizard(
3804     const QString& mimeType, const QString& databaseName, const KDbConnectionData &cdata)
3805 {
3806     return d->showProjectMigrationWizard(mimeType, databaseName, &cdata);
3807 }
3808 
3809 tristate KexiMainWindow::executeItem(KexiPart::Item* item)
3810 {
3811     KexiPart::Info *info = item ? Kexi::partManager().infoForPluginId(item->pluginId()) : 0;
3812     if ((! info) || (! info->isExecuteSupported()))
3813         return false;
3814     KexiPart::Part *part = Kexi::partManager().part(info);
3815     if (!part)
3816         return false;
3817     return part->execute(item);
3818 }
3819 
3820 void KexiMainWindow::slotProjectImportDataTable()
3821 {
3822 //! @todo allow data appending (it is not possible now)
3823     if (d->userMode)
3824         return;
3825     QMap<QString, QString> args;
3826     args.insert("sourceType", "file");
3827     QDialog *dlg = KexiInternalPart::createModalDialogInstance(
3828                        "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args);
3829     if (!dlg)
3830         return; //error msg has been shown by KexiInternalPart
3831     dlg->exec();
3832     delete dlg;
3833 }
3834 
3835 tristate KexiMainWindow::executeCustomActionForObject(KexiPart::Item* item,
3836         const QString& actionName)
3837 {
3838     if (actionName == "exportToCSV")
3839         return exportItemAsDataTable(item);
3840     else if (actionName == "copyToClipboardAsCSV")
3841         return copyItemToClipboardAsDataTable(item);
3842 
3843     qWarning() << "no such action:" << actionName;
3844     return false;
3845 }
3846 
3847 tristate KexiMainWindow::exportItemAsDataTable(KexiPart::Item* item)
3848 {
3849     if (!item)
3850         return false;
3851 
3852     QMap<QString, QString> args;
3853 
3854     if (!checkForDirtyFlagOnExport(item, &args)) {
3855             return false;
3856     }
3857 
3858     //! @todo: accept record changes...
3859     args.insert("destinationType", "file");
3860     args.insert("itemId", QString::number(item->identifier()));
3861     QDialog *dlg = KexiInternalPart::createModalDialogInstance(
3862                        "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args);
3863     if (!dlg)
3864         return false; //error msg has been shown by KexiInternalPart
3865     int result = dlg->exec();
3866     delete dlg;
3867     return result == QDialog::Rejected ? tristate(cancelled) : tristate(true);
3868 }
3869 
3870 bool KexiMainWindow::checkForDirtyFlagOnExport(KexiPart::Item *item, QMap<QString, QString> *args)
3871 {
3872     //! @todo: handle tables
3873     if (item->pluginId() != "org.kexi-project.query") {
3874         return true;
3875     }
3876 
3877     KexiWindow * itemWindow = openedWindowFor(item);
3878     if (itemWindow && itemWindow->isDirty()) {
3879         tristate result;
3880         if (item->neverSaved()) {
3881             result = true;
3882         } else {
3883             int prevWindowId = 0;
3884             if (!itemWindow->isVisible()) {
3885                 prevWindowId = currentWindow()->id();
3886                 activateWindow(itemWindow->id());
3887             }
3888             result = askOnExportingChangedQuery(item);
3889 
3890             if (prevWindowId != 0) {
3891                 activateWindow(prevWindowId);
3892             }
3893         }
3894 
3895         if (~result) {
3896             return false;
3897         } else if (true == result) {
3898             args->insert("useTempQuery","1");
3899         }
3900     }
3901 
3902     return true;
3903 }
3904 
3905 tristate KexiMainWindow::askOnExportingChangedQuery(KexiPart::Item *item) const
3906 {
3907     const KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancel(const_cast<KexiMainWindow*>(this),
3908         xi18nc("@info", "Design of query <resource>%1</resource> that you want to export data"
3909                                          " from is changed and has not yet been saved. Do you want to use data"
3910                                          " from the changed query for exporting or from its original (saved)"
3911                                          " version?", item->captionOrName()),
3912         QString(),
3913         KGuiItem(xi18nc("@action:button Export query data", "Use the Changed Query")),
3914         KGuiItem(xi18nc("@action:button Export query data", "Use the Original Query")),
3915         KStandardGuiItem::cancel(),
3916         QString(),
3917         KMessageBox::Notify | KMessageBox::Dangerous);
3918     if (result == KMessageBox::Yes) {
3919         return true;
3920     } else if (result == KMessageBox::No) {
3921         return false;
3922     }
3923 
3924     return cancelled;
3925 }
3926 
3927 bool KexiMainWindow::printItem(KexiPart::Item* item, const QString& titleText)
3928 {
3929     //! @todo printItem(item, KexiSimplePrintingSettings::load(), titleText);
3930     Q_UNUSED(item)
3931     Q_UNUSED(titleText)
3932     return false;
3933 }
3934 
3935 tristate KexiMainWindow::printItem(KexiPart::Item* item)
3936 {
3937     return printItem(item, QString());
3938 }
3939 
3940 bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item, const QString& titleText, bool reload)
3941 {
3942 //! @todo printPreviewForItem(item, KexiSimplePrintingSettings::load(), titleText, reload);
3943     Q_UNUSED(item)
3944     Q_UNUSED(titleText)
3945     Q_UNUSED(reload)
3946     return false;
3947 }
3948 
3949 tristate KexiMainWindow::printPreviewForItem(KexiPart::Item* item)
3950 {
3951     return printPreviewForItem(item, QString(),
3952 //! @todo store cached record data?
3953                                true/*reload*/);
3954 }
3955 
3956 tristate KexiMainWindow::showPageSetupForItem(KexiPart::Item* item)
3957 {
3958     Q_UNUSED(item)
3959 //! @todo check if changes to this object's design are saved, if not: ask for saving
3960 //! @todo accept record changes...
3961 //! @todo printActionForItem(item, PageSetupForItem);
3962     return false;
3963 }
3964 
3965 //! @todo reenable printItem() when ported
3966 #if 0
3967 bool KexiMainWindow::printItem(KexiPart::Item* item, const KexiSimplePrintingSettings& settings,
3968                                const QString& titleText)
3969 {
3970 //! @todo: check if changes to this object's design are saved, if not: ask for saving
3971 //! @todo: accept record changes...
3972     KexiSimplePrintingCommand cmd(this, item->identifier());
3973     //modal
3974     return cmd.print(settings, titleText);
3975 }
3976 
3977 bool KexiMainWindow::printPreviewForItem(KexiPart::Item* item,
3978         const KexiSimplePrintingSettings& settings, const QString& titleText, bool reload)
3979 {
3980 //! @todo: check if changes to this object's design are saved, if not: ask for saving
3981 //! @todo: accept record changes...
3982     KexiSimplePrintingCommand* cmd = d->openedCustomObjectsForItem<KexiSimplePrintingCommand>(
3983                                          item, "KexiSimplePrintingCommand");
3984     if (!cmd) {
3985         d->addOpenedCustomObjectForItem(
3986             item,
3987             cmd = new KexiSimplePrintingCommand(this, item->identifier()),
3988             "KexiSimplePrintingCommand"
3989         );
3990     }
3991     return cmd->showPrintPreview(settings, titleText, reload);
3992 }
3993 
3994 tristate KexiMainWindow::printActionForItem(KexiPart::Item* item, PrintActionType action)
3995 {
3996     if (!item)
3997         return false;
3998     KexiPart::Info *info = Kexi::partManager().infoForPluginId(item->pluginId());
3999     if (!info->isPrintingSupported())
4000         return false;
4001 
4002     KexiWindow *printingWindow = d->pageSetupWindows[ item->identifier()];
4003     if (printingWindow) {
4004         if (!activateWindow(*printingWindow))
4005             return false;
4006         if (action == PreviewItem || action == PrintItem) {
4007             QTimer::singleShot(0, printingWindow->selectedView(),
4008                                (action == PreviewItem) ? SLOT(printPreview()) : SLOT(print()));
4009         }
4010         return true;
4011     }
4012 
4013 #ifndef KEXI_NO_PENDING_DIALOGS
4014     Private::PendingJobType pendingType;
4015     KexiWindow *window = d->openedWindowFor(item, pendingType);
4016     if (pendingType != Private::NoJob)
4017         return cancelled;
4018 #else
4019     KexiWindow *window = openedWindowFor(item);
4020 #endif
4021 
4022     if (window) {
4023         // accept record changes
4024         QWidget *prevFocusWidget = focusWidget();
4025         window->setFocus();
4026         d->action_data_save_row->activate(QAction::Trigger);
4027         if (prevFocusWidget)
4028             prevFocusWidget->setFocus();
4029 
4030         // opened: check if changes made to this dialog are saved, if not: ask for saving
4031         if (window->neverSaved()) //sanity check
4032             return false;
4033         if (window->isDirty()) {
4034             KGuiItem saveChanges(KStandardGuiItem::save());
4035             saveChanges.setToolTip(futureI18n("Save changes"));
4036             saveChanges.setWhatsThis(
4037                 futureI18n("Pressing this button will save all recent changes made in \"%1\" object.",
4038                      item->name()));
4039             KGuiItem doNotSave(KStandardGuiItem::no());
4040             doNotSave.setWhatsThis(
4041                 futureI18n("Pressing this button will ignore all unsaved changes made in \"%1\" object.",
4042                      window->partItem()->name()));
4043 
4044             QString question;
4045             if (action == PrintItem)
4046                 question = futureI18n("Do you want to save changes before printing?");
4047             else if (action == PreviewItem)
4048                 question = futureI18n("Do you want to save changes before making print preview?");
4049             else if (action == PageSetupForItem)
4050                 question = futureI18n("Do you want to save changes before showing page setup?");
4051             else
4052                 return false;
4053 
4054             const KMessageBox::ButtonCode questionRes = KMessageBox::warningYesNoCancel(this,
4055                                     "<p>"
4056                                     + window->part()->i18nMessage("Design of object <resource>%1</resource> has been modified.", window)
4057                                     .subs(item->name())
4058                                     + "</p><p>" + question + "</p>",
4059                                     QString(),
4060                                     saveChanges,
4061                                     doNotSave);
4062             if (KMessageBox::Cancel == questionRes)
4063                 return cancelled;
4064             if (KMessageBox::Yes == questionRes) {
4065                 tristate savingRes = saveObject(window, QString(), DoNotAsk);
4066                 if (true != savingRes)
4067                     return savingRes;
4068             }
4069         }
4070     }
4071     KexiPart::Part * printingPart = Kexi::partManager().partForClass("org.kexi-project.simpleprinting");
4072     if (!printingPart)
4073         printingPart = new KexiSimplePrintingPart(); //hardcoded as there're no .desktop file
4074     KexiPart::Item* printingPartItem = d->prj->createPartItem(
4075                                            printingPart, item->name() //<-- this will look like "table1 : printing" on the window list
4076                                        );
4077     QMap<QString, QVariant> staticObjectArgs;
4078     staticObjectArgs["identifier"] = QString::number(item->identifier());
4079     if (action == PrintItem)
4080         staticObjectArgs["action"] = "print";
4081     else if (action == PreviewItem)
4082         staticObjectArgs["action"] = "printPreview";
4083     else if (action == PageSetupForItem)
4084         staticObjectArgs["action"] = "pageSetup";
4085     else
4086         return false;
4087     bool openingCancelled;
4088     printingWindow = openObject(printingPartItem, Kexi::DesignViewMode,
4089                                 &openingCancelled, &staticObjectArgs);
4090     if (openingCancelled)
4091         return cancelled;
4092     if (!printingWindow) //sanity
4093         return false;
4094     d->pageSetupWindows.insert(item->identifier(), printingWindow);
4095     d->pageSetupWindowItemID2dataItemID_map.insert(
4096         printingWindow->partItem()->identifier(), item->identifier());
4097 
4098     return true;
4099 }
4100 #endif
4101 
4102 void KexiMainWindow::slotEditCopySpecialDataTable()
4103 {
4104     KexiPart::Item* item = d->navigator->selectedPartItem();
4105     if (item)
4106         copyItemToClipboardAsDataTable(item);
4107 }
4108 
4109 tristate KexiMainWindow::copyItemToClipboardAsDataTable(KexiPart::Item* item)
4110 {
4111     if (!item)
4112         return false;
4113 
4114     QMap<QString, QString> args;
4115 
4116     if (!checkForDirtyFlagOnExport(item, &args)) {
4117             return false;
4118     }
4119 
4120     args.insert("destinationType", "clipboard");
4121     args.insert("itemId", QString::number(item->identifier()));
4122     QDialog *dlg = KexiInternalPart::createModalDialogInstance(
4123                        "org.kexi-project.importexport.csv", "KexiCSVExportWizard", this, 0, &args);
4124     if (!dlg)
4125         return false; //error msg has been shown by KexiInternalPart
4126     const int result = dlg->exec();
4127     delete dlg;
4128     return result == QDialog::Rejected ? tristate(cancelled) : tristate(true);
4129 }
4130 
4131 void KexiMainWindow::slotEditPasteSpecialDataTable()
4132 {
4133 //! @todo allow data appending (it is not possible now)
4134     if (d->userMode)
4135         return;
4136     QMap<QString, QString> args;
4137     args.insert("sourceType", "clipboard");
4138     QDialog *dlg = KexiInternalPart::createModalDialogInstance(
4139                        "org.kexi-project.importexport.csv", "KexiCSVImportDialog", this, 0, &args);
4140     if (!dlg)
4141         return; //error msg has been shown by KexiInternalPart
4142     dlg->exec();
4143     delete dlg;
4144 }
4145 
4146 void KexiMainWindow::slotEditFind()
4147 {
4148     KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface();
4149     if (!iface)
4150         return;
4151     d->updateFindDialogContents(true/*create if does not exist*/);
4152     d->findDialog()->setReplaceMode(false);
4153 
4154     d->findDialog()->show();
4155     d->findDialog()->activateWindow();
4156     d->findDialog()->raise();
4157 }
4158 
4159 void KexiMainWindow::slotEditFind(bool next)
4160 {
4161     KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface();
4162     if (!iface)
4163         return;
4164     tristate res = iface->find(
4165                        d->findDialog()->valueToFind(), d->findDialog()->options(), next);
4166     if (~res)
4167         return;
4168     d->findDialog()->updateMessage(true == res);
4169 //! @todo result
4170 }
4171 
4172 void KexiMainWindow::slotEditFindNext()
4173 {
4174     slotEditFind(true);
4175 }
4176 
4177 void KexiMainWindow::slotEditFindPrevious()
4178 {
4179     slotEditFind(false);
4180 }
4181 
4182 void KexiMainWindow::slotEditReplace()
4183 {
4184     KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface();
4185     if (!iface)
4186         return;
4187     d->updateFindDialogContents(true/*create if does not exist*/);
4188     d->findDialog()->setReplaceMode(true);
4189 //! @todo slotEditReplace()
4190     d->findDialog()->show();
4191     d->findDialog()->activateWindow();
4192 }
4193 
4194 void KexiMainWindow::slotEditReplaceNext()
4195 {
4196     slotEditReplace(false);
4197 }
4198 
4199 void KexiMainWindow::slotEditReplace(bool all)
4200 {
4201     KexiSearchAndReplaceViewInterface* iface = d->currentViewSupportingSearchAndReplaceInterface();
4202     if (!iface)
4203         return;
4204 //! @todo add question: "Do you want to replace every occurrence of \"%1\" with \"%2\"?
4205 //!       You won't be able to undo this." + "Do not ask again".
4206     tristate res = iface->findNextAndReplace(
4207                        d->findDialog()->valueToFind(), d->findDialog()->valueToReplaceWith(),
4208                        d->findDialog()->options(), all);
4209     d->findDialog()->updateMessage(true == res);
4210 //! @todo result
4211 }
4212 
4213 void KexiMainWindow::slotEditReplaceAll()
4214 {
4215     slotEditReplace(true);
4216 }
4217 
4218 void KexiMainWindow::highlightObject(const QString& pluginId, const QString& name)
4219 {
4220     if (!d->prj)
4221         return;
4222     KexiPart::Item *item = d->prj->itemForPluginId(pluginId, name);
4223     if (!item)
4224         return;
4225     if (d->navigator) {
4226         slotSetProjectNavigatorVisible(true);
4227         d->navigator->selectItem(*item);
4228     }
4229 }
4230 
4231 void KexiMainWindow::slotPartItemSelectedInNavigator(KexiPart::Item* item)
4232 {
4233     Q_UNUSED(item);
4234 }
4235 
4236 KToolBar *KexiMainWindow::toolBar(const QString& name) const
4237 {
4238     return d->tabbedToolBar ? d->tabbedToolBar->toolBar(name) : 0;
4239 }
4240 
4241 void KexiMainWindow::appendWidgetToToolbar(const QString& name, QWidget* widget)
4242 {
4243     if (d->tabbedToolBar)
4244         d->tabbedToolBar->appendWidgetToToolbar(name, widget);
4245 }
4246 
4247 void KexiMainWindow::setWidgetVisibleInToolbar(QWidget* widget, bool visible)
4248 {
4249     if (d->tabbedToolBar)
4250         d->tabbedToolBar->setWidgetVisibleInToolbar(widget, visible);
4251 }
4252 
4253 void KexiMainWindow::addToolBarAction(const QString& toolBarName, QAction *action)
4254 {
4255     if (d->tabbedToolBar)
4256         d->tabbedToolBar->addAction(toolBarName, action);
4257 }
4258 
4259 void KexiMainWindow::updatePropertyEditorInfoLabel(const QString& textToDisplayForNullSet)
4260 {
4261     d->propEditor->updateInfoLabelForPropertySet(d->propertySet, textToDisplayForNullSet);
4262 }
4263 
4264 void KexiMainWindow::addSearchableModel(KexiSearchableModel *model)
4265 {
4266     if (d->tabbedToolBar) {
4267         d->tabbedToolBar->addSearchableModel(model);
4268     }
4269 }
4270 
4271 void KexiMainWindow::removeSearchableModel(KexiSearchableModel *model)
4272 {
4273     if (d->tabbedToolBar) {
4274         d->tabbedToolBar->removeSearchableModel(model);
4275     }
4276 }
4277 
4278 void KexiMainWindow::setReasonableDialogSize(QDialog *dialog)
4279 {
4280     dialog->setMinimumSize(600, 400);
4281     dialog->resize(size() * 0.8);
4282 }
4283 
4284 void KexiMainWindow::restoreDesignTabAndActivateIfNeeded(const QString &tabName)
4285 {
4286     if (!d->tabbedToolBar) {
4287         return;
4288     }
4289     d->tabbedToolBar->showTab(tabName);
4290     if (currentWindow() && currentWindow()->partItem()
4291         && currentWindow()->partItem()->identifier() != 0) // for unstored items id can be < 0
4292     {
4293         const QString tabToActivate = d->tabsToActivateOnShow.value(
4294                                           currentWindow()->partItem()->identifier());
4295         //qDebug() << "tabToActivate:" << tabToActivate << "tabName:" << tabName;
4296         if (tabToActivate == tabName) {
4297             d->tabbedToolBar->setCurrentTab(tabToActivate);
4298         }
4299     }
4300 }
4301 
4302 void KexiMainWindow::restoreDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode,
4303                                               int previousItemId)
4304 {
4305     //qDebug() << pluginId << viewMode << previousItemId;
4306     if (viewMode == Kexi::DesignViewMode) {
4307         switch (d->prj->typeIdForPluginId(pluginId)) {
4308         case KexiPart::FormObjectType: {
4309             hideDesignTab(previousItemId, "org.kexi-project.report");
4310             restoreDesignTabAndActivateIfNeeded("form");
4311             break;
4312         }
4313         case KexiPart::ReportObjectType: {
4314             hideDesignTab(previousItemId, "org.kexi-project.form");
4315             restoreDesignTabAndActivateIfNeeded("report");
4316             break;
4317         }
4318         default:
4319             hideDesignTab(previousItemId);
4320         }
4321     }
4322     else {
4323         hideDesignTab(previousItemId);
4324     }
4325 }
4326 
4327 void KexiMainWindow::activateDesignTab(const QString &pluginId)
4328 {
4329     if (!d->tabbedToolBar) {
4330         return;
4331     }
4332     switch (d->prj->typeIdForPluginId(pluginId)) {
4333     case KexiPart::FormObjectType:
4334         d->tabbedToolBar->setCurrentTab("form");
4335         break;
4336     case KexiPart::ReportObjectType:
4337         d->tabbedToolBar->setCurrentTab("report");
4338         break;
4339     default:;
4340     }
4341 }
4342 
4343 void KexiMainWindow::activateDesignTabIfNeeded(const QString &pluginId, Kexi::ViewMode viewMode)
4344 {
4345     if (!d->tabbedToolBar) {
4346         return;
4347     }
4348     const QString tabToActivate = d->tabsToActivateOnShow.value(currentWindow()->partItem()->identifier());
4349     //qDebug() << pluginId << viewMode << tabToActivate;
4350 
4351     if (viewMode == Kexi::DesignViewMode && tabToActivate.isEmpty()) {
4352         activateDesignTab(pluginId);
4353     }
4354     else {
4355         d->tabbedToolBar->setCurrentTab(tabToActivate);
4356     }
4357 }
4358 
4359 void KexiMainWindow::hideDesignTab(int itemId, const QString &pluginId)
4360 {
4361     if (!d->tabbedToolBar) {
4362         return;
4363     }
4364     //qDebug() << itemId << pluginId;
4365     if (   itemId > 0
4366         && d->tabbedToolBar->currentWidget())
4367     {
4368         const QString currentTab = d->tabbedToolBar->currentWidget()->objectName();
4369         //qDebug() << "d->tabsToActivateOnShow.insert" << itemId << currentTab;
4370         d->tabsToActivateOnShow.insert(itemId, currentTab);
4371     }
4372     switch (d->prj->typeIdForPluginId(pluginId)) {
4373     case KexiPart::FormObjectType:
4374         d->tabbedToolBar->hideTab("form");
4375         break;
4376     case KexiPart::ReportObjectType:
4377         d->tabbedToolBar->hideTab("report");
4378         break;
4379     default:
4380         d->tabbedToolBar->hideTab("form");
4381         d->tabbedToolBar->hideTab("report");
4382     }
4383 }
4384 
4385 void KexiMainWindow::showDesignTabIfNeeded(int previousItemId)
4386 {
4387     if (d->insideCloseWindow && d->tabbedToolBar)
4388         return;
4389     if (currentWindow()) {
4390         restoreDesignTabIfNeeded(currentWindow()->partItem()->pluginId(),
4391                                  currentWindow()->currentViewMode(), previousItemId);
4392     } else {
4393         hideDesignTab(previousItemId);
4394     }
4395 }
4396 
4397 KexiUserFeedbackAgent* KexiMainWindow::userFeedbackAgent() const
4398 {
4399     return &d->userFeedback;
4400 }
4401 
4402 KexiMigrateManagerInterface* KexiMainWindow::migrateManager()
4403 {
4404     if (!d->migrateManager) {
4405         d->migrateManager = dynamic_cast<KexiMigrateManagerInterface*>(
4406                     KexiInternalPart::createObjectInstance(
4407                         "org.kexi-project.migration", "manager", this, this, nullptr));
4408     }
4409     return d->migrateManager;
4410 }
4411 
4412 void KexiMainWindow::toggleFullScreen(bool isFullScreen)
4413 {
4414     if (d->tabbedToolBar) {
4415         static bool isTabbarRolledDown;
4416         if (isFullScreen) {
4417             isTabbarRolledDown = !d->tabbedToolBar->isRolledUp();
4418             if (isTabbarRolledDown) {
4419                 d->tabbedToolBar->toggleRollDown();
4420             }
4421         } else {
4422             if (isTabbarRolledDown && d->tabbedToolBar->isRolledUp()) {
4423                 d->tabbedToolBar->toggleRollDown();
4424             }
4425         }
4426     }
4427     const Qt::WindowStates s = windowState() & Qt::WindowMaximized;
4428     if (isFullScreen) {
4429         setWindowState(windowState() | Qt::WindowFullScreen | s);
4430     } else {
4431         setWindowState((windowState() & ~Qt::WindowFullScreen));
4432         showMaximized();
4433     }
4434 }