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

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
0003    Copyright (C) 2003-2014 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 "kexipart.h"
0022 #include "kexipartinfo.h"
0023 #include "kexipartitem.h"
0024 //! @todo KEXI3 #include "kexistaticpart.h"
0025 #include "KexiWindow.h"
0026 #include "KexiWindowData.h"
0027 #include "KexiView.h"
0028 #include "kexipartguiclient.h"
0029 #include "KexiMainWindowIface.h"
0030 #include "kexi.h"
0031 #include <kexiutils/utils.h>
0032 
0033 #include <KDbConnection>
0034 
0035 #include <KActionCollection>
0036 #include <KMessageBox>
0037 
0038 #include <QDebug>
0039 
0040 namespace KexiPart
0041 {
0042 
0043 KEXICORE_EXPORT QString version()
0044 {
0045     return QString::fromLatin1("%1.%2").arg(KEXI_STABLE_VERSION_MAJOR).arg(KEXI_STABLE_VERSION_MINOR);
0046 }
0047 
0048 //! @internal
0049 class Q_DECL_HIDDEN Part::Private
0050 {
0051 public:
0052     Private()
0053     : guiClient(0)
0054     , newObjectsAreDirty(false)
0055     , instanceActionsInitialized(false)
0056     {
0057     }
0058 
0059     //! Helper, used in Part::openInstance()
0060     tristate askForOpeningInTextMode(KexiWindow *window, KexiPart::Item *item,
0061                                      Kexi::ViewModes supportedViewModes, Kexi::ViewMode viewMode) {
0062         if (viewMode != Kexi::TextViewMode
0063                 && supportedViewModes & Kexi::TextViewMode
0064                 && window->data()->proposeOpeningInTextViewModeBecauseOfProblems) {
0065             //ask
0066             KexiUtils::WaitCursorRemover remover;
0067             //! @todo use message handler for this to enable non-gui apps
0068             QString singleStatusString(window->singleStatusString());
0069             if (!singleStatusString.isEmpty())
0070                 singleStatusString.prepend(QString("\n\n") + xi18n("Details:") + " ");
0071             if (KMessageBox::No == KMessageBox::questionYesNo(0,
0072                     ((viewMode == Kexi::DesignViewMode)
0073                      ? xi18nc("@info",
0074                               "Object <resource>%1</resource> could not be opened in Design View.", item->name())
0075                      : xi18n("Object could not be opened in Data View.")) + "\n"
0076                     + xi18n("Do you want to open it in Text View?") + singleStatusString, 0,
0077                     KStandardGuiItem::open(), KStandardGuiItem::cancel())) {
0078                 return false;
0079             }
0080             return true;
0081         }
0082         return cancelled;
0083     }
0084 
0085     QString toolTip;
0086     QString whatsThis;
0087     QString instanceName;
0088 
0089     GUIClient *guiClient;
0090     QMap<int, GUIClient*> instanceGuiClients;
0091     Kexi::ObjectStatus status;
0092 
0093     bool newObjectsAreDirty;
0094     bool instanceActionsInitialized;
0095 };
0096 }
0097 
0098 //----------------------------------------------------------------
0099 
0100 using namespace KexiPart;
0101 
0102 Part::Part(QObject *parent,
0103            const QString& instanceName,
0104            const QString& toolTip,
0105            const QString& whatsThis,
0106            const QVariantList& list)
0107     : PartBase(parent, list)
0108     , d(new Private())
0109 {
0110     d->instanceName = KDb::stringToIdentifier(
0111         instanceName.isEmpty()
0112         ? xi18nc("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
0113                 "Use '_' character instead of spaces. First character should be a..z character. "
0114                 "If you cannot use latin characters in your language, use english word.",
0115                 "object").toLower()
0116         : instanceName);
0117     d->toolTip = toolTip;
0118     d->whatsThis = whatsThis;
0119 }
0120 
0121 /*! @todo KEXI3
0122 Part::Part(QObject* parent, StaticPartInfo *info)
0123     : PartBase(parent, QVariantList())
0124         , d(new Private())
0125 {
0126     setObjectName("StaticPart");
0127     setInfo(info);
0128 }*/
0129 
0130 Part::~Part()
0131 {
0132     delete d;
0133 }
0134 
0135 void Part::createGUIClients()//KexiMainWindow *win)
0136 {
0137     if (!d->guiClient) {
0138         //create part's gui client
0139         d->guiClient = new GUIClient(this, false, "part");
0140 
0141         //default actions for part's gui client:
0142         QAction* act = info()->newObjectAction();
0143         // - update action's tooltip and "what's this"
0144         QString tip(toolTip());
0145         if (!tip.isEmpty()) {
0146             act->setToolTip(tip);
0147         }
0148         QString what(whatsThis());
0149         if (!what.isEmpty()) {
0150             act->setWhatsThis(what);
0151         }
0152 
0153         //default actions for part instance's gui client:
0154         //NONE
0155         //let init specific actions for part instances
0156         for (int mode = 1; mode <= 0x01000; mode <<= 1) {
0157             if (info()->supportedViewModes() & (Kexi::ViewMode)mode) {
0158                 GUIClient *instanceGuiClient = new GUIClient(
0159                     this, true, Kexi::nameForViewMode((Kexi::ViewMode)mode).toLatin1());
0160                 d->instanceGuiClients.insert((Kexi::ViewMode)mode, instanceGuiClient);
0161             }
0162         }
0163         // also add an instance common for all modes (mode==0)
0164         GUIClient *instanceGuiClient = new GUIClient(this, true, "allViews");
0165         d->instanceGuiClients.insert(Kexi::AllViewModes, instanceGuiClient);
0166 
0167         initPartActions();
0168     }
0169 }
0170 
0171 KActionCollection* Part::actionCollectionForMode(Kexi::ViewMode viewMode) const
0172 {
0173     GUIClient *cli = d->instanceGuiClients.value((int)viewMode);
0174     return cli ? cli->actionCollection() : 0;
0175 }
0176 
0177 QAction * Part::createSharedAction(Kexi::ViewMode mode, const QString &text,
0178                                   const QString &pix_name, const QKeySequence &cut, const char *name,
0179                                   const char *subclassName)
0180 {
0181     GUIClient *instanceGuiClient = d->instanceGuiClients.value((int)mode);
0182     if (!instanceGuiClient) {
0183         qWarning() << "no gui client for mode " << mode << "!";
0184         return 0;
0185     }
0186     return KexiMainWindowIface::global()->createSharedAction(text, pix_name, cut, name,
0187             instanceGuiClient->actionCollection(), subclassName);
0188 }
0189 
0190 QAction * Part::createSharedPartAction(const QString &text,
0191                                       const QString &pix_name, const QKeySequence &cut, const char *name,
0192                                       const char *subclassName)
0193 {
0194     if (!d->guiClient)
0195         return 0;
0196     return KexiMainWindowIface::global()->createSharedAction(text, pix_name, cut, name,
0197             d->guiClient->actionCollection(), subclassName);
0198 }
0199 
0200 QAction * Part::createSharedToggleAction(Kexi::ViewMode mode, const QString &text,
0201                                         const QString &pix_name, const QKeySequence &cut, const char *name)
0202 {
0203     return createSharedAction(mode, text, pix_name, cut, name, "KToggleAction");
0204 }
0205 
0206 QAction * Part::createSharedPartToggleAction(const QString &text,
0207         const QString &pix_name, const QKeySequence &cut, const char *name)
0208 {
0209     return createSharedPartAction(text, pix_name, cut, name, "KToggleAction");
0210 }
0211 
0212 void Part::setActionAvailable(const char *action_name, bool avail)
0213 {
0214     for (QMap<int, GUIClient*>::Iterator it = d->instanceGuiClients.begin(); it != d->instanceGuiClients.end(); ++it) {
0215         QAction *act = it.value()->actionCollection()->action(action_name);
0216         if (act) {
0217             act->setEnabled(avail);
0218             return;
0219         }
0220     }
0221     KexiMainWindowIface::global()->setActionAvailable(action_name, avail);
0222 }
0223 
0224 KexiWindow* Part::openInstance(QWidget* parent, KexiPart::Item *item, Kexi::ViewMode viewMode,
0225                                QMap<QString, QVariant>* staticObjectArgs)
0226 {
0227     Q_ASSERT(item);
0228     //now it's the time for creating instance actions
0229     if (!d->instanceActionsInitialized) {
0230         initInstanceActions();
0231         d->instanceActionsInitialized = true;
0232     }
0233 
0234     d->status.clearStatus();
0235     KexiWindow *window = new KexiWindow(parent,
0236                                         info()->supportedViewModes(), this, item);
0237 
0238     KexiProject *project = KexiMainWindowIface::global()->project();
0239     KDbObject object(project->typeIdForPluginId(info()->pluginId()));
0240     object.setName(item->name());
0241     object.setCaption(item->caption());
0242     object.setDescription(item->description());
0243 
0244     /*! @todo js: apply settings for caption displaying method; there can be option for
0245      - displaying item.caption() as caption, if not empty, without instanceName
0246      - displaying the same as above in tabCaption (or not) */
0247     window->setId(item->identifier()); //not needed, but we did it
0248     window->setWindowIcon(QIcon::fromTheme(window->iconName()));
0249     KexiWindowData *windowData = createWindowData(window);
0250     if (!windowData) {
0251         d->status = Kexi::ObjectStatus(KexiMainWindowIface::global()->project()->dbConnection(),
0252                                        xi18n("Could not create object's window."), xi18n("The plugin or object definition may be corrupted."));
0253         delete window;
0254         return 0;
0255     }
0256     window->setData(windowData);
0257 
0258     if (!item->neverSaved()) {
0259         //we have to load object data for this dialog
0260         loadAndSetSchemaObject(window, object, viewMode);
0261         if (!window->schemaObject()) {
0262             //last chance:
0263             if (false == d->askForOpeningInTextMode(
0264                         window, item, window->supportedViewModes(), viewMode)) {
0265                 delete window;
0266                 return 0;
0267             }
0268             viewMode = Kexi::TextViewMode;
0269             loadAndSetSchemaObject(window, object, viewMode);
0270         }
0271         if (!window->schemaObject()) {
0272             if (!d->status.error())
0273                 d->status = Kexi::ObjectStatus(KexiMainWindowIface::global()->project()->dbConnection(),
0274                                                xi18n("Could not load object's definition."), xi18n("Object design may be corrupted."));
0275             d->status.append(
0276                 Kexi::ObjectStatus(xi18nc("@info",
0277                                           "You can delete <resource>%1</resource> object and create it again.",
0278                                           item->name()), QString()));
0279 
0280             window->close();
0281             delete window;
0282             return 0;
0283         }
0284     }
0285 
0286     bool switchingFailed = false;
0287     bool dummy;
0288     tristate res = window->switchToViewMode(viewMode, staticObjectArgs, &dummy);
0289     if (!res) {
0290         tristate askForOpeningInTextModeRes
0291         = d->askForOpeningInTextMode(window, item, window->supportedViewModes(), viewMode);
0292         if (true == askForOpeningInTextModeRes) {
0293             window->close();
0294             delete window;
0295             //try in text mode
0296             return openInstance(parent, item, Kexi::TextViewMode, staticObjectArgs);
0297         } else if (false == askForOpeningInTextModeRes) {
0298             window->close();
0299             delete window;
0300             qWarning() << "!window, cannot switch to a view mode" <<
0301                 Kexi::nameForViewMode(viewMode);
0302             return 0;
0303         }
0304         //the window has an error info
0305         switchingFailed = true;
0306     }
0307     if (~res)
0308         switchingFailed = true;
0309 
0310     if (switchingFailed) {
0311         d->status = window->status();
0312         window->close();
0313         delete window;
0314         qWarning() << "!window, switching to view mode failed, " <<
0315             Kexi::nameForViewMode(viewMode);
0316         return 0;
0317     }
0318     window->registerWindow(); //ok?
0319     window->show();
0320 
0321     window->setMinimumSize(window->minimumSizeHint().width(), window->minimumSizeHint().height());
0322 
0323     //dirty only if it's a new object
0324     if (window->selectedView()) {
0325         window->selectedView()->setDirty(
0326             internalPropertyValue("newObjectsAreDirty", false).toBool() ? item->neverSaved() : false);
0327     }
0328     return window;
0329 }
0330 
0331 KDbObject* Part::loadSchemaObject(KexiWindow *window, const KDbObject& object,
0332         Kexi::ViewMode viewMode, bool *ownedByWindow)
0333 {
0334     Q_UNUSED(window);
0335     Q_UNUSED(viewMode);
0336     Q_ASSERT(ownedByWindow);
0337     KDbObject *newObject = new KDbObject();
0338     *newObject = object;
0339     *ownedByWindow = true;
0340     return newObject;
0341 }
0342 
0343 void Part::loadAndSetSchemaObject(KexiWindow *window, const KDbObject& object,
0344     Kexi::ViewMode viewMode)
0345 {
0346     bool schemaObjectOwned = true;
0347     KDbObject* sd = loadSchemaObject(window, object, viewMode, &schemaObjectOwned);
0348     window->setSchemaObject(sd);
0349     window->setSchemaObjectOwned(schemaObjectOwned);
0350 }
0351 
0352 bool Part::loadDataBlock(KexiWindow *window, QString *dataString, const QString& dataID)
0353 {
0354     if (true != KexiMainWindowIface::global()->project()->dbConnection()->loadDataBlock(
0355                 window->id(), dataString, dataID))
0356     {
0357         d->status = Kexi::ObjectStatus(KexiMainWindowIface::global()->project()->dbConnection(),
0358                                        xi18n("Could not load object's data."),
0359                                        xi18nc("@info",
0360                                               "Data identifier: <resource>%1</resource>.", dataID));
0361         d->status.append(*window);
0362         return false;
0363     }
0364     return true;
0365 }
0366 
0367 void Part::initPartActions()
0368 {
0369 }
0370 
0371 void Part::initInstanceActions()
0372 {
0373 }
0374 
0375 tristate Part::remove(KexiPart::Item *item)
0376 {
0377     Q_ASSERT(item);
0378     KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection();
0379     if (!conn)
0380         return false;
0381     return conn->removeObject(item->identifier());
0382 }
0383 
0384 KexiWindowData* Part::createWindowData(KexiWindow* window)
0385 {
0386     return new KexiWindowData(window);
0387 }
0388 
0389 QString Part::instanceName() const
0390 {
0391     return d->instanceName;
0392 }
0393 
0394 QString Part::toolTip() const
0395 {
0396     return d->toolTip;
0397 }
0398 
0399 QString Part::whatsThis() const
0400 {
0401     return d->whatsThis;
0402 }
0403 
0404 tristate Part::rename(KexiPart::Item *item, const QString& newName)
0405 {
0406     Q_UNUSED(item);
0407     Q_UNUSED(newName);
0408     return true;
0409 }
0410 
0411 GUIClient* Part::instanceGuiClient(Kexi::ViewMode mode) const
0412 {
0413     return d->instanceGuiClients.value((int)mode);
0414 }
0415 
0416 GUIClient* Part::guiClient() const
0417 {
0418     return d->guiClient;
0419 }
0420 
0421 const Kexi::ObjectStatus& Part::lastOperationStatus() const
0422 {
0423     return d->status;
0424 }
0425 
0426 KDbQuerySchema* Part::currentQuery(KexiView* view)
0427 {
0428     Q_UNUSED(view);
0429     return 0;
0430 }
0431 
0432 KEXICORE_EXPORT QString KexiPart::fullCaptionForItem(KexiPart::Item *item, KexiPart::Part *part)
0433 {
0434     Q_ASSERT(item);
0435     Q_ASSERT(part);
0436     if (part)
0437         return item->name() + " : " + part->info()->name();
0438     return item->name();
0439 }