File indexing completed on 2024-05-12 16:39:42

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
0003    Copyright (C) 2003-2015 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 "KexiWindow.h"
0022 #include "KexiWindowData.h"
0023 #include "KexiView.h"
0024 #include "KexiMainWindowIface.h"
0025 #include "kexipart.h"
0026 //! @todo KEXI3 #include "kexistaticpart.h"
0027 #include "kexipartitem.h"
0028 #include "kexipartinfo.h"
0029 #include "kexiproject.h"
0030 #include <kexiutils/utils.h>
0031 #include <kexiutils/SmallToolButton.h>
0032 #include <kexiutils/FlowLayout.h>
0033 
0034 #include <KDbConnection>
0035 #include <KDbTransactionGuard>
0036 
0037 #include <KStandardGuiItem>
0038 #include <KMessageBox>
0039 
0040 #include <QStackedWidget>
0041 #include <QEvent>
0042 #include <QCloseEvent>
0043 #include <QDebug>
0044 
0045 //----------------------------------------------------------
0046 
0047 //! @internal
0048 class Q_DECL_HIDDEN KexiWindow::Private
0049 {
0050 public:
0051     explicit Private(KexiWindow *window)
0052             : win(window)
0053             , schemaObject(0)
0054             , schemaObjectOwned(false)
0055             , isRegistered(false)
0056             , dirtyChangedEnabled(true)
0057             , switchToViewModeEnabled(true)
0058     {
0059         supportedViewModes = Kexi::NoViewMode; //will be set by KexiPart
0060         openedViewModes = Kexi::NoViewMode;
0061         currentViewMode = Kexi::NoViewMode; //no view available yet
0062         creatingViewsMode = Kexi::NoViewMode;
0063         id = -1;
0064         item = 0;
0065     }
0066 
0067     ~Private() {
0068         setSchemaObject(0);
0069     }
0070 
0071     void setSchemaObject(KDbObject* data)
0072     {
0073         if (schemaObjectOwned) {
0074             delete schemaObject;
0075         }
0076         schemaObject = data;
0077     }
0078 
0079     bool setupSchemaObject(KDbObject *object, KexiPart::Item *item,
0080                            KexiView::StoreNewDataOptions options) const
0081     {
0082         object->setName(item->name());
0083         object->setCaption(item->caption());
0084         object->setDescription(item->description());
0085 
0086         KexiProject *project = KexiMainWindowIface::global()->project();
0087         KexiPart::Item* existingItem = project->item(part->info(), object->name());
0088         if (existingItem && !(options & KexiView::OverwriteExistingData)) {
0089             KMessageBox::information(win,
0090                                      xi18n("Could not create new object.")
0091                                      + win->part()->i18nMessage("Object <resource>%1</resource> already exists.", win)
0092                                        .subs(object->name()).toString());
0093             return false;
0094         }
0095         return true;
0096     }
0097 
0098     KexiWindow *win;
0099     QVBoxLayout* mainLyr;
0100     QStackedWidget* stack;
0101 
0102     Kexi::ViewModes supportedViewModes;
0103     Kexi::ViewModes openedViewModes;
0104     Kexi::ViewMode currentViewMode;
0105 
0106 #ifdef KEXI_SHOW_CONTEXT_HELP
0107     KexiContextHelpInfo *contextHelpInfo;
0108 #endif
0109     int id;
0110     QPointer<KexiPart::Part> part;
0111     KexiPart::Item *item;
0112     KDbObject* schemaObject;
0113     bool schemaObjectOwned;
0114     QPointer<KexiView> newlySelectedView; //!< Used in isDirty(), temporary set in switchToViewMode()
0115     //!< during view setup, when a new view is not yet raised.
0116     //! Used in viewThatRecentlySetDirtyFlag(), modified in dirtyChanged().
0117     QPointer<KexiView> viewThatRecentlySetDirtyFlag;
0118     QPointer<KexiWindowData> data; //!< temporary data shared between views
0119 
0120     /*! Created view's mode - helper for switchToViewMode(),
0121      KexiView ctor uses that info. >0 values are useful. */
0122     Kexi::ViewMode creatingViewsMode;
0123 
0124     bool isRegistered;
0125     bool dirtyChangedEnabled; //!< used in setDirty(), affects dirtyChanged()
0126     bool switchToViewModeEnabled; //!< used internally switchToViewMode() to avoid infinite loop
0127     QMap<Kexi::ViewMode, KexiView*> views;
0128 };
0129 
0130 //----------------------------------------------------------
0131 
0132 KexiWindow::KexiWindow(QWidget *parent, Kexi::ViewModes supportedViewModes,
0133                        KexiPart::Part *part, KexiPart::Item *item)
0134         : QWidget(parent)
0135         , KexiActionProxy(this, KexiMainWindowIface::global())
0136         , d(new Private(this))
0137         , m_destroying(false)
0138 {
0139     d->part = part;
0140     d->item = item;
0141     d->supportedViewModes = supportedViewModes;
0142     createSubwidgets();
0143 #ifdef KEXI_SHOW_CONTEXT_HELP
0144     d->contextHelpInfo = new KexiContextHelpInfo();
0145 #endif
0146     updateCaption();
0147 }
0148 
0149 KexiWindow::KexiWindow()
0150         : QWidget(0)
0151         , KexiActionProxy(this, KexiMainWindowIface::global())
0152         , d(new Private(this))
0153         , m_destroying(false)
0154 {
0155     createSubwidgets();
0156 #ifdef KEXI_SHOW_CONTEXT_HELP
0157     d->contextHelpInfo = new KexiContextHelpInfo();
0158 #endif
0159     updateCaption();
0160 }
0161 
0162 KexiWindow::~KexiWindow()
0163 {
0164     close(true /*force*/);
0165     m_destroying = true;
0166     delete d;
0167     d = 0;
0168 }
0169 
0170 void KexiWindow::createSubwidgets()
0171 {
0172     d->mainLyr = new QVBoxLayout(this);
0173     d->mainLyr->setContentsMargins(0, KexiUtils::marginHint() / 2, 0, 0);
0174     d->stack = new QStackedWidget(this);
0175     d->mainLyr->addWidget(d->stack);
0176 }
0177 
0178 KexiView *KexiWindow::selectedView() const
0179 {
0180     if (m_destroying)
0181         return 0;
0182     return static_cast<KexiView*>(d->stack->currentWidget());
0183 }
0184 
0185 KexiView *KexiWindow::viewForMode(Kexi::ViewMode mode) const
0186 {
0187     return d->views.value(mode);
0188 }
0189 
0190 void KexiWindow::addView(KexiView *view)
0191 {
0192     addView(view, Kexi::NoViewMode);
0193 }
0194 
0195 void KexiWindow::addView(KexiView *view, Kexi::ViewMode mode)
0196 {
0197     d->stack->addWidget(view);
0198     d->views.insert(mode, view);
0199     d->openedViewModes |= mode;
0200 }
0201 
0202 void KexiWindow::removeView(Kexi::ViewMode mode)
0203 {
0204     removeView(viewForMode(mode));
0205     d->openedViewModes |= mode;
0206     d->openedViewModes ^= mode;
0207 }
0208 
0209 void KexiWindow::removeView(KexiView *view)
0210 {
0211     if (view) {
0212         d->stack->removeWidget(view);
0213         d->views.remove(view->viewMode());
0214         d->openedViewModes |= view->viewMode();
0215         d->openedViewModes ^= view->viewMode();
0216     }
0217 }
0218 
0219 QSize KexiWindow::minimumSizeHint() const
0220 {
0221     KexiView *v = selectedView();
0222     if (!v)
0223         return QWidget::minimumSizeHint();
0224     return v->minimumSizeHint();
0225 }
0226 
0227 QSize KexiWindow::sizeHint() const
0228 {
0229     KexiView *v = selectedView();
0230     if (!v)
0231         return QWidget::sizeHint();
0232     return v->preferredSizeHint(v->sizeHint());
0233 }
0234 
0235 void KexiWindow::setId(int id)
0236 {
0237     d->id = id;
0238 }
0239 
0240 KexiPart::Part* KexiWindow::part() const
0241 {
0242     return d->part;
0243 }
0244 
0245 KexiPart::Item *KexiWindow::partItem() const
0246 {
0247     return d->item;
0248 }
0249 
0250 bool KexiWindow::supportsViewMode(Kexi::ViewMode mode) const
0251 {
0252     return d->supportedViewModes & mode;
0253 }
0254 
0255 Kexi::ViewModes KexiWindow::supportedViewModes() const
0256 {
0257     return d->supportedViewModes;
0258 }
0259 
0260 Kexi::ViewMode KexiWindow::currentViewMode() const
0261 {
0262     return d->currentViewMode;
0263 }
0264 
0265 KexiView* KexiWindow::viewThatRecentlySetDirtyFlag() const
0266 {
0267     return d->viewThatRecentlySetDirtyFlag;
0268 }
0269 
0270 void KexiWindow::registerWindow()
0271 {
0272     if (d->isRegistered)
0273         return;
0274     KexiMainWindowIface::global()->registerChild(this);
0275     d->isRegistered = true;
0276 }
0277 
0278 bool KexiWindow::isRegistered() const
0279 {
0280     return d->isRegistered;
0281 }
0282 
0283 int KexiWindow::id() const
0284 {
0285     return (partItem() && partItem()->identifier() > 0)
0286            ? partItem()->identifier() : d->id;
0287 }
0288 
0289 void KexiWindow::setContextHelp(const QString& caption,
0290                                 const QString& text, const QString& iconName)
0291 {
0292 #ifdef KEXI_SHOW_CONTEXT_HELP
0293     d->contextHelpInfo->caption = caption;
0294     d->contextHelpInfo->text = text;
0295     d->contextHelpInfo->text = iconName;
0296     updateContextHelp();
0297 #else
0298     Q_UNUSED(caption);
0299     Q_UNUSED(text);
0300     Q_UNUSED(iconName);
0301 #endif
0302 }
0303 
0304 bool KexiWindow::close(bool force)
0305 {
0306     KexiMainWindowIface::global()->acceptPropertySetEditing();
0307 
0308     //let any view send "closing" signal
0309     QList<KexiView *> list(findChildren<KexiView*>());
0310     QList< QPointer<KexiView> > listPtr;
0311     foreach(KexiView * view, list) { // use QPointers for sanity
0312         listPtr.append(QPointer<KexiView>(view));
0313     }
0314     foreach(QPointer<KexiView> viewPtr, listPtr) {
0315         if (viewPtr && viewPtr->parent() == d->stack) {
0316             bool cancel = false;
0317             emit viewPtr->closing(&cancel);
0318             if (!force && cancel) {
0319                      return false;
0320             }
0321         }
0322     }
0323     emit closing();
0324     foreach(QPointer<KexiView> viewPtr, listPtr) {
0325         if (viewPtr && viewPtr->parent() == d->stack) {
0326             removeView(viewPtr.data());
0327             delete viewPtr.data();
0328         }
0329     }
0330     return true;
0331 }
0332 
0333 void KexiWindow::closeEvent(QCloseEvent * e)
0334 {
0335     if (!close(false /* !force*/)) {
0336         e->ignore();
0337         return;
0338     }
0339     QWidget::closeEvent(e);
0340 }
0341 
0342 bool KexiWindow::isDirty() const
0343 {
0344     //look for "dirty" flag
0345     int m = d->openedViewModes;
0346     int mode = 1;
0347     while (m > 0) {
0348         if (m & 1) {
0349             KexiView *view = viewForMode(static_cast<Kexi::ViewMode>(mode));
0350             if (view && view->isDirty()) {
0351                 return true;
0352             }
0353         }
0354         m >>= 1;
0355         mode <<= 1;
0356     }
0357     return false;
0358 }
0359 
0360 void KexiWindow::setDirty(bool dirty)
0361 {
0362     d->dirtyChangedEnabled = false;
0363     int m = d->openedViewModes;
0364     int mode = 1;
0365     while (m > 0) {
0366         if (m & 1) {
0367             KexiView *view = viewForMode(static_cast<Kexi::ViewMode>(mode));
0368             if (view) {
0369                 view->setDirty(dirty);
0370             }
0371         }
0372         m >>= 1;
0373         mode <<= 1;
0374     }
0375     d->dirtyChangedEnabled = true;
0376     dirtyChanged(d->viewThatRecentlySetDirtyFlag); //update
0377 }
0378 
0379 QString KexiWindow::iconName()
0380 {
0381     if (!d->part || !d->part->info()) {
0382         KexiView *v = selectedView();
0383         if (v) {
0384             return v->defaultIconName();
0385         }
0386         return QString();
0387     }
0388     return d->part->info()->iconName();
0389 }
0390 
0391 KexiPart::GUIClient* KexiWindow::guiClient() const
0392 {
0393     if (!d->part || d->currentViewMode == 0)
0394         return 0;
0395     return d->part->instanceGuiClient(d->currentViewMode);
0396 }
0397 
0398 KexiPart::GUIClient* KexiWindow::commonGUIClient() const
0399 {
0400     if (!d->part)
0401         return 0;
0402     return d->part->instanceGuiClient(Kexi::AllViewModes);
0403 }
0404 
0405 bool KexiWindow::isDesignModePreloadedForTextModeHackUsed(Kexi::ViewMode newViewMode) const
0406 {
0407     return newViewMode == Kexi::TextViewMode
0408            && !viewForMode(Kexi::DesignViewMode)
0409            && supportsViewMode(Kexi::DesignViewMode);
0410 }
0411 
0412 tristate KexiWindow::switchToViewMode(
0413     Kexi::ViewMode newViewMode,
0414     QMap<QString, QVariant>* staticObjectArgs,
0415     bool *proposeOpeningInTextViewModeBecauseOfProblems)
0416 {
0417     Q_ASSERT(proposeOpeningInTextViewModeBecauseOfProblems);
0418     clearStatus();
0419     KexiMainWindowIface::global()->acceptPropertySetEditing();
0420 
0421     const bool designModePreloadedForTextModeHack = isDesignModePreloadedForTextModeHackUsed(newViewMode);
0422     tristate res = true;
0423     if (designModePreloadedForTextModeHack) {
0424         /* A HACK: open design BEFORE text mode: otherwise Query schema becames crazy */
0425         bool _proposeOpeningInTextViewModeBecauseOfProblems = false; // used because even if opening the view failed,
0426         // text view can be opened
0427         res = switchToViewMode(Kexi::DesignViewMode, staticObjectArgs, &_proposeOpeningInTextViewModeBecauseOfProblems);
0428         if ((!res && !_proposeOpeningInTextViewModeBecauseOfProblems) || ~res)
0429             return res;
0430     }
0431 
0432     bool dontStore = false;
0433     KexiView *view = selectedView();
0434 
0435     if (d->currentViewMode == newViewMode)
0436         return true;
0437     if (!supportsViewMode(newViewMode)) {
0438         qWarning() << "!" << Kexi::nameForViewMode(newViewMode);
0439         return false;
0440     }
0441 
0442     if (view) {
0443         res = true;
0444         if (view->isDataEditingInProgress()) {
0445             KGuiItem saveItem(KStandardGuiItem::save());
0446             saveItem.setText(xi18n("Save Changes"));
0447             KGuiItem dontSaveItem(KStandardGuiItem::dontSave());
0448             KGuiItem cancelItem(KStandardGuiItem::cancel());
0449             cancelItem.setText(xi18n("Do Not Switch"));
0450             const int res = KMessageBox::questionYesNoCancel(
0451                 selectedView(),
0452                 xi18n("<para>There are unsaved changes in object <resource>%1</resource>.</para>"
0453                      "<para>Do you want to save these changes before switching to other view?</para>",
0454                      partItem()->captionOrName()),
0455                     xi18n("Confirm Saving Changes"),
0456                     saveItem, dontSaveItem, cancelItem, QString(),
0457                     KMessageBox::Notify | KMessageBox::Dangerous
0458             );
0459             if (res == KMessageBox::Yes) {
0460                 if (true != view->saveDataChanges())
0461                     return cancelled;
0462             }
0463             else if (res == KMessageBox::No) {
0464                 if (true != view->cancelDataChanges())
0465                     return cancelled;
0466             }
0467             else { // Cancel:
0468                 return cancelled;
0469             }
0470         }
0471         if (!designModePreloadedForTextModeHack) {
0472             const bool wasDirty = view->isDirty(); // remember and restore the flag if the view was clean
0473             res = view->beforeSwitchTo(newViewMode, &dontStore);
0474             if (!wasDirty) {
0475                 view->setDirty(false);
0476             }
0477         }
0478         if (~res || !res)
0479             return res;
0480         if (!dontStore && view->isDirty()) {
0481             res = KexiMainWindowIface::global()->saveObject(this, xi18n("Design has been changed. "
0482                     "You must save it before switching to other view."));
0483             if (~res || !res)
0484                 return res;
0485 //   KMessageBox::questionYesNo(0, xi18n("Design has been changed. You must save it before switching to other view."))
0486 //    ==KMessageBox::No
0487         }
0488     }
0489 
0490     //get view for viewMode
0491     KexiView *newView = viewForMode(newViewMode);
0492     if (newView && !newView->inherits("KexiView")) {
0493         newView = 0;
0494     }
0495     if (!newView) {
0496         KexiUtils::setWaitCursor();
0497         //ask the part to create view for the new mode
0498         d->creatingViewsMode = newViewMode;
0499 /*! @todo KEXI3 StaticPart
0500         KexiPart::StaticPart *staticPart = dynamic_cast<KexiPart::StaticPart*>((KexiPart::Part*)d->part);
0501         if (staticPart)
0502             newView = staticPart->createView(this, this, d->item, newViewMode, staticObjectArgs);
0503         else*/
0504             newView = d->part->createView(this, this, d->item, newViewMode, staticObjectArgs);
0505         KexiUtils::removeWaitCursor();
0506         if (!newView) {
0507             //js TODO error?
0508             qWarning() << "Switching to mode " << newViewMode << " failed. Previous mode "
0509             << d->currentViewMode << " restored.";
0510             return false;
0511         }
0512         d->creatingViewsMode = Kexi::NoViewMode;
0513         newView->initViewActions();
0514         newView->initMainMenuActions();
0515         addView(newView, newViewMode);
0516     }
0517     const Kexi::ViewMode prevViewMode = d->currentViewMode;
0518     res = true;
0519     if (designModePreloadedForTextModeHack) {
0520         d->currentViewMode = Kexi::NoViewMode; //SAFE?
0521     }
0522     bool wasDirty = newView->isDirty(); // remember and restore the flag if the view was clean
0523     res = newView->beforeSwitchTo(newViewMode, &dontStore);
0524     if (!wasDirty) {
0525         newView->setDirty(false);
0526     }
0527     *proposeOpeningInTextViewModeBecauseOfProblems
0528         = data()->proposeOpeningInTextViewModeBecauseOfProblems;
0529     if (!res) {
0530         removeView(newViewMode);
0531         delete newView;
0532         qWarning() << "Switching to mode " << newViewMode << " failed. Previous mode "
0533         << d->currentViewMode << " restored.";
0534         return false;
0535     }
0536     d->currentViewMode = newViewMode;
0537     d->newlySelectedView = newView;
0538     if (prevViewMode == Kexi::NoViewMode)
0539         d->newlySelectedView->setDirty(false);
0540 
0541     if ((prevViewMode == Kexi::DesignViewMode && d->currentViewMode == Kexi::TextViewMode)
0542             || (prevViewMode == Kexi::TextViewMode && d->currentViewMode == Kexi::DesignViewMode)) {
0543         if (view) {
0544             wasDirty = view->isDirty(); // synchronize the dirty flag between Design and Text views
0545         }
0546     } else {
0547         wasDirty = newView->isDirty(); // remember and restore the flag if the view was clean
0548     }
0549 
0550     res = newView->afterSwitchFrom(
0551               designModePreloadedForTextModeHack ? Kexi::NoViewMode : prevViewMode);
0552     newView->setDirty(wasDirty);
0553 
0554     *proposeOpeningInTextViewModeBecauseOfProblems
0555         = data()->proposeOpeningInTextViewModeBecauseOfProblems;
0556     if (!res) {
0557         removeView(newViewMode);
0558         delete newView;
0559         qWarning() << "Switching to mode " << newViewMode << " failed. Previous mode "
0560         << prevViewMode << " restored.";
0561         const Kexi::ObjectStatus status(*this);
0562         setStatus(KexiMainWindowIface::global()->project()->dbConnection(),
0563                   xi18n("Switching to other view failed (%1).", Kexi::nameForViewMode(newViewMode)), "");
0564         append(status);
0565         d->currentViewMode = prevViewMode;
0566         return false;
0567     }
0568     d->newlySelectedView = 0;
0569     if (~res) {
0570         d->currentViewMode = prevViewMode;
0571         return cancelled;
0572     }
0573     if (view) {
0574         takeActionProxyChild(view);   //take current proxy child
0575         // views have distinct local toolbars, and user has switched the mode button so switch it back
0576         //view->toggleViewModeButtonBack();
0577     }
0578     addActionProxyChild(newView);   //new proxy child
0579     d->stack->setCurrentWidget(newView);
0580     newView->propertySetSwitched();
0581     KexiMainWindowIface::global()->invalidateSharedActions(newView);
0582     newView->setFocus();
0583     return true;
0584 }
0585 
0586 tristate KexiWindow::switchToViewModeInternal(Kexi::ViewMode newViewMode)
0587 {
0588     return KexiMainWindowIface::global()->switchToViewMode(*this, newViewMode);
0589 }
0590 
0591 tristate KexiWindow::switchToViewMode(Kexi::ViewMode newViewMode)
0592 {
0593     if (newViewMode == d->currentViewMode)
0594         return true;
0595     if (!d->switchToViewModeEnabled)
0596         return false;
0597     bool dummy;
0598     return switchToViewMode(newViewMode, 0, &dummy);
0599 }
0600 
0601 void KexiWindow::setFocus()
0602 {
0603     if (d->stack->currentWidget()) {
0604         if (d->stack->currentWidget()->inherits("KexiView"))
0605             static_cast<KexiView*>(d->stack->currentWidget())->setFocus();
0606         else
0607             d->stack->currentWidget()->setFocus();
0608     } else {
0609         QWidget::setFocus();
0610     }
0611     activate();
0612 }
0613 
0614 KPropertySet*
0615 KexiWindow::propertySet()
0616 {
0617     KexiView *v = selectedView();
0618     if (!v)
0619         return 0;
0620     return v->propertySet();
0621 }
0622 
0623 void KexiWindow::setSchemaObject(KDbObject* object)
0624 {
0625     d->setSchemaObject(object);
0626 }
0627 
0628 KDbObject* KexiWindow::schemaObject() const
0629 {
0630     return d->schemaObject;
0631 }
0632 
0633 void KexiWindow::setSchemaObjectOwned(bool set)
0634 {
0635     d->schemaObjectOwned = set;
0636 }
0637 
0638 KexiWindowData *KexiWindow::data() const
0639 {
0640     return d->data;
0641 }
0642 
0643 void KexiWindow::setData(KexiWindowData* data)
0644 {
0645     if (data != d->data)
0646         delete d->data;
0647     d->data = data;
0648 }
0649 
0650 bool KexiWindow::eventFilter(QObject *obj, QEvent *e)
0651 {
0652     if (QWidget::eventFilter(obj, e))
0653         return true;
0654     /*if (e->type()==QEvent::FocusIn) {
0655       QWidget *w = m_parentWindow->activeWindow();
0656       w=0;
0657     }*/
0658     if ((e->type() == QEvent::FocusIn && KexiMainWindowIface::global()->currentWindow() == this)
0659             || e->type() == QEvent::MouseButtonPress) {
0660         if (d->stack->currentWidget() && KDbUtils::hasParent(d->stack->currentWidget(), obj)) {
0661             //pass the activation
0662             activate();
0663         }
0664     }
0665     return false;
0666 }
0667 
0668 void KexiWindow::dirtyChanged(KexiView* view)
0669 {
0670     if (!d->dirtyChangedEnabled)
0671         return;
0672     d->viewThatRecentlySetDirtyFlag = isDirty() ? view : 0;
0673     updateCaption();
0674     emit dirtyChanged(this);
0675 }
0676 
0677 //static
0678 QString KexiWindow::windowTitleForItem(const KexiPart::Item &item)
0679 {
0680     return item.name();
0681 }
0682 
0683 void KexiWindow::updateCaption()
0684 {
0685     if (!d->item || !d->part)
0686         return;
0687     const QString fullCapt(windowTitleForItem(*d->item));
0688     setWindowTitle(isDirty() ? xi18nc("@title:window with dirty indicator", "%1*", fullCapt)
0689                              : fullCapt);
0690 }
0691 
0692 bool KexiWindow::neverSaved() const
0693 {
0694     return d->item ? d->item->neverSaved() : true;
0695 }
0696 
0697 tristate KexiWindow::storeNewData(KexiView::StoreNewDataOptions options)
0698 {
0699     if (!neverSaved()) {
0700         return false;
0701     }
0702     if (d->schemaObject) {
0703         return false; //schema must not exist
0704     }
0705     KexiView *v = selectedView();
0706     if (!v) {
0707         return false;
0708     }
0709     //create schema object and assign information
0710     KexiProject *project = KexiMainWindowIface::global()->project();
0711     KDbObject object(project->typeIdForPluginId(d->part->info()->pluginId()));
0712     if (!d->setupSchemaObject(&object, d->item, options)) {
0713         return false;
0714     }
0715 
0716     bool cancel = false;
0717     d->schemaObject = v->storeNewData(object, options, &cancel);
0718     if (cancel)
0719         return cancelled;
0720     if (!d->schemaObject) {
0721         setStatus(project->dbConnection(), xi18n("Saving object's definition failed."), "");
0722         return false;
0723     }
0724 
0725     if (project->typeIdForPluginId(part()->info()->pluginId()) < 0) {
0726         if (!project->createIdForPart(*part()->info()))
0727             return false;
0728     }
0729     /* Sets 'dirty' flag on every dialog's view. */
0730     setDirty(false);
0731     //new object data has now ID updated to a unique value
0732     //-assign that to item's identifier
0733     d->item->setIdentifier(d->schemaObject->id());
0734     project->addStoredItem(part()->info(), d->item);
0735 
0736     return true;
0737 }
0738 
0739 tristate KexiWindow::storeData(bool dontAsk)
0740 {
0741     if (neverSaved())
0742         return false;
0743     KexiView *v = selectedView();
0744     if (!v)
0745         return false;
0746 
0747 #define storeData_ERR \
0748     setStatus(KexiMainWindowIface::global()->project()->dbConnection(), \
0749         xi18n("Saving object's data failed."),"");
0750 
0751     //save changes using transaction
0752     KDbTransaction transaction = KexiMainWindowIface::global()
0753                                       ->project()->dbConnection()->beginTransaction();
0754     if (transaction.isNull()) {
0755         storeData_ERR;
0756         return false;
0757     }
0758     KDbTransactionGuard tg(transaction);
0759 
0760     const tristate res = v->storeData(dontAsk);
0761     if (~res) //trans. will be cancelled
0762         return res;
0763     if (!res) {
0764         storeData_ERR;
0765         return res;
0766     }
0767     if (!tg.commit()) {
0768         storeData_ERR;
0769         return false;
0770     }
0771     /* Sets 'dirty' flag on every dialog's view. */
0772     setDirty(false);
0773     return true;
0774 }
0775 
0776 tristate KexiWindow::storeDataAs(KexiPart::Item *item, KexiView::StoreNewDataOptions options)
0777 {
0778     if (neverSaved()) {
0779         qWarning() << "The data was never saved, so storeNewData() should be called instead, giving up.";
0780         return false;
0781     }
0782     KexiView *v = selectedView();
0783     if (!v) {
0784         return false;
0785     }
0786     //create schema object and assign information
0787     KexiProject *project = KexiMainWindowIface::global()->project();
0788     KDbObject object(project->typeIdForPluginId(d->part->info()->pluginId()));
0789     if (!d->setupSchemaObject(&object, item, options)) {
0790         return false;
0791     }
0792 
0793     bool cancel = false;
0794     KDbObject *newSchemaObject;
0795     if (isDirty()) { // full save of new data
0796         newSchemaObject = v->storeNewData(object, options, &cancel);
0797     }
0798     else { // there were no changes; full copy of the data is enough
0799            // - gives better performance (e.g. tables are copied on server side)
0800            // - works without bothering the user (no unnecessary questions)
0801         newSchemaObject = v->copyData(object, options, &cancel);
0802     }
0803 
0804     if (cancel) {
0805         return cancelled;
0806     }
0807     if (!newSchemaObject) {
0808         setStatus(project->dbConnection(), xi18n("Saving object's definition failed."), "");
0809         return false;
0810     }
0811     setSchemaObject(newSchemaObject); // deletes previous schema if owned
0812 
0813     if (project->typeIdForPluginId(part()->info()->pluginId()) < 0) {
0814         if (!project->createIdForPart(*part()->info()))
0815             return false;
0816     }
0817     // clear 'dirty' for old window
0818     setDirty(false);
0819     // for now this Window has new item assigned
0820     d->item = item;
0821 
0822     // new object data has now ID updated to a unique value
0823     // -assign that to item's identifier
0824     item->setIdentifier(d->schemaObject->id());
0825 
0826     project->addStoredItem(part()->info(), d->item);
0827 
0828     // set 'dirty' flag on every dialog's view
0829     setDirty(false);
0830 
0831     return true;
0832 }
0833 
0834 void KexiWindow::activate()
0835 {
0836     KexiView *v = selectedView();
0837     //qDebug() << "focusWidget(): " << focusWidget()->name();
0838     if (!KDbUtils::hasParent(v, KexiMainWindowIface::global()->focusWidget())) {
0839         //ah, focused widget is not in this view, move focus:
0840         if (v)
0841             v->setFocus();
0842     }
0843     if (v)
0844         v->updateActions(true);
0845 }
0846 
0847 void KexiWindow::deactivate()
0848 {
0849     KexiView *v = selectedView();
0850     if (v)
0851         v->updateActions(false);
0852 }
0853 
0854 void KexiWindow::sendDetachedStateToCurrentView()
0855 {
0856     KexiView *v = selectedView();
0857     if (v)
0858         v->windowDetached();
0859 }
0860 
0861 void KexiWindow::sendAttachedStateToCurrentView()
0862 {
0863     KexiView *v = selectedView();
0864     if (v)
0865         v->windowAttached();
0866 }
0867 
0868 bool KexiWindow::saveSettings()
0869 {
0870     bool result = true;
0871     for (int i = 0; i < d->stack->count(); ++i) {
0872         KexiView *view = qobject_cast<KexiView*>(d->stack->widget(i));
0873         if (!view->saveSettings()) {
0874             result = false;
0875         }
0876     }
0877     return result;
0878 }
0879 
0880 Kexi::ViewMode KexiWindow::creatingViewsMode() const
0881 {
0882     return d->creatingViewsMode;
0883 }
0884 
0885 QVariant KexiWindow::internalPropertyValue(const QByteArray& name,
0886         const QVariant& defaultValue) const
0887 {
0888     return d->part->internalPropertyValue(name, defaultValue);
0889 }
0890