1 /* This file is part of the KDE project
2    Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
3    Copyright (C) 2003-2014 Jarosław Staniek <staniek@kde.org>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19 */
20 
21 #include "kexipart.h"
22 #include "kexipartinfo.h"
23 #include "kexipartitem.h"
24 //! @todo KEXI3 #include "kexistaticpart.h"
25 #include "KexiWindow.h"
26 #include "KexiWindowData.h"
27 #include "KexiView.h"
28 #include "kexipartguiclient.h"
29 #include "KexiMainWindowIface.h"
30 #include "kexi.h"
31 #include <kexiutils/utils.h>
32 
33 #include <KDbConnection>
34 
35 #include <KActionCollection>
36 #include <KMessageBox>
37 
38 #include <QDebug>
39 
40 namespace KexiPart
41 {
42 
version()43 KEXICORE_EXPORT QString version()
44 {
45     return QString::fromLatin1("%1.%2").arg(KEXI_STABLE_VERSION_MAJOR).arg(KEXI_STABLE_VERSION_MINOR);
46 }
47 
48 //! @internal
49 class Q_DECL_HIDDEN Part::Private
50 {
51 public:
Private()52     Private()
53     : guiClient(0)
54     , newObjectsAreDirty(false)
55     , instanceActionsInitialized(false)
56     {
57     }
58 
59     //! Helper, used in Part::openInstance()
askForOpeningInTextMode(KexiWindow * window,KexiPart::Item * item,Kexi::ViewModes supportedViewModes,Kexi::ViewMode viewMode)60     tristate askForOpeningInTextMode(KexiWindow *window, KexiPart::Item *item,
61                                      Kexi::ViewModes supportedViewModes, Kexi::ViewMode viewMode) {
62         if (viewMode != Kexi::TextViewMode
63                 && supportedViewModes & Kexi::TextViewMode
64                 && window->data()->proposeOpeningInTextViewModeBecauseOfProblems) {
65             //ask
66             KexiUtils::WaitCursorRemover remover;
67             //! @todo use message handler for this to enable non-gui apps
68             QString singleStatusString(window->singleStatusString());
69             if (!singleStatusString.isEmpty())
70                 singleStatusString.prepend(QString("\n\n") + xi18n("Details:") + " ");
71             if (KMessageBox::No == KMessageBox::questionYesNo(0,
72                     ((viewMode == Kexi::DesignViewMode)
73                      ? xi18nc("@info",
74                               "Object <resource>%1</resource> could not be opened in Design View.", item->name())
75                      : xi18n("Object could not be opened in Data View.")) + "\n"
76                     + xi18n("Do you want to open it in Text View?") + singleStatusString, 0,
77                     KStandardGuiItem::open(), KStandardGuiItem::cancel())) {
78                 return false;
79             }
80             return true;
81         }
82         return cancelled;
83     }
84 
85     QString toolTip;
86     QString whatsThis;
87     QString instanceName;
88 
89     GUIClient *guiClient;
90     QMap<int, GUIClient*> instanceGuiClients;
91     Kexi::ObjectStatus status;
92 
93     bool newObjectsAreDirty;
94     bool instanceActionsInitialized;
95 };
96 }
97 
98 //----------------------------------------------------------------
99 
100 using namespace KexiPart;
101 
Part(QObject * parent,const QString & instanceName,const QString & toolTip,const QString & whatsThis,const QVariantList & list)102 Part::Part(QObject *parent,
103            const QString& instanceName,
104            const QString& toolTip,
105            const QString& whatsThis,
106            const QVariantList& list)
107     : PartBase(parent, list)
108     , d(new Private())
109 {
110     d->instanceName = KDb::stringToIdentifier(
111         instanceName.isEmpty()
112         ? xi18nc("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
113                 "Use '_' character instead of spaces. First character should be a..z character. "
114                 "If you cannot use latin characters in your language, use english word.",
115                 "object").toLower()
116         : instanceName);
117     d->toolTip = toolTip;
118     d->whatsThis = whatsThis;
119 }
120 
121 /*! @todo KEXI3
122 Part::Part(QObject* parent, StaticPartInfo *info)
123     : PartBase(parent, QVariantList())
124         , d(new Private())
125 {
126     setObjectName("StaticPart");
127     setInfo(info);
128 }*/
129 
~Part()130 Part::~Part()
131 {
132     delete d;
133 }
134 
createGUIClients()135 void Part::createGUIClients()//KexiMainWindow *win)
136 {
137     if (!d->guiClient) {
138         //create part's gui client
139         d->guiClient = new GUIClient(this, false, "part");
140 
141         //default actions for part's gui client:
142         QAction* act = info()->newObjectAction();
143         // - update action's tooltip and "what's this"
144         QString tip(toolTip());
145         if (!tip.isEmpty()) {
146             act->setToolTip(tip);
147         }
148         QString what(whatsThis());
149         if (!what.isEmpty()) {
150             act->setWhatsThis(what);
151         }
152 
153         //default actions for part instance's gui client:
154         //NONE
155         //let init specific actions for part instances
156         for (int mode = 1; mode <= 0x01000; mode <<= 1) {
157             if (info()->supportedViewModes() & (Kexi::ViewMode)mode) {
158                 GUIClient *instanceGuiClient = new GUIClient(
159                     this, true, Kexi::nameForViewMode((Kexi::ViewMode)mode).toLatin1());
160                 d->instanceGuiClients.insert((Kexi::ViewMode)mode, instanceGuiClient);
161             }
162         }
163         // also add an instance common for all modes (mode==0)
164         GUIClient *instanceGuiClient = new GUIClient(this, true, "allViews");
165         d->instanceGuiClients.insert(Kexi::AllViewModes, instanceGuiClient);
166 
167         initPartActions();
168     }
169 }
170 
actionCollectionForMode(Kexi::ViewMode viewMode) const171 KActionCollection* Part::actionCollectionForMode(Kexi::ViewMode viewMode) const
172 {
173     GUIClient *cli = d->instanceGuiClients.value((int)viewMode);
174     return cli ? cli->actionCollection() : 0;
175 }
176 
createSharedAction(Kexi::ViewMode mode,const QString & text,const QString & pix_name,const QKeySequence & cut,const char * name,const char * subclassName)177 QAction * Part::createSharedAction(Kexi::ViewMode mode, const QString &text,
178                                   const QString &pix_name, const QKeySequence &cut, const char *name,
179                                   const char *subclassName)
180 {
181     GUIClient *instanceGuiClient = d->instanceGuiClients.value((int)mode);
182     if (!instanceGuiClient) {
183         qWarning() << "no gui client for mode " << mode << "!";
184         return 0;
185     }
186     return KexiMainWindowIface::global()->createSharedAction(text, pix_name, cut, name,
187             instanceGuiClient->actionCollection(), subclassName);
188 }
189 
createSharedPartAction(const QString & text,const QString & pix_name,const QKeySequence & cut,const char * name,const char * subclassName)190 QAction * Part::createSharedPartAction(const QString &text,
191                                       const QString &pix_name, const QKeySequence &cut, const char *name,
192                                       const char *subclassName)
193 {
194     if (!d->guiClient)
195         return 0;
196     return KexiMainWindowIface::global()->createSharedAction(text, pix_name, cut, name,
197             d->guiClient->actionCollection(), subclassName);
198 }
199 
createSharedToggleAction(Kexi::ViewMode mode,const QString & text,const QString & pix_name,const QKeySequence & cut,const char * name)200 QAction * Part::createSharedToggleAction(Kexi::ViewMode mode, const QString &text,
201                                         const QString &pix_name, const QKeySequence &cut, const char *name)
202 {
203     return createSharedAction(mode, text, pix_name, cut, name, "KToggleAction");
204 }
205 
createSharedPartToggleAction(const QString & text,const QString & pix_name,const QKeySequence & cut,const char * name)206 QAction * Part::createSharedPartToggleAction(const QString &text,
207         const QString &pix_name, const QKeySequence &cut, const char *name)
208 {
209     return createSharedPartAction(text, pix_name, cut, name, "KToggleAction");
210 }
211 
setActionAvailable(const char * action_name,bool avail)212 void Part::setActionAvailable(const char *action_name, bool avail)
213 {
214     for (QMap<int, GUIClient*>::Iterator it = d->instanceGuiClients.begin(); it != d->instanceGuiClients.end(); ++it) {
215         QAction *act = it.value()->actionCollection()->action(action_name);
216         if (act) {
217             act->setEnabled(avail);
218             return;
219         }
220     }
221     KexiMainWindowIface::global()->setActionAvailable(action_name, avail);
222 }
223 
openInstance(QWidget * parent,KexiPart::Item * item,Kexi::ViewMode viewMode,QMap<QString,QVariant> * staticObjectArgs)224 KexiWindow* Part::openInstance(QWidget* parent, KexiPart::Item *item, Kexi::ViewMode viewMode,
225                                QMap<QString, QVariant>* staticObjectArgs)
226 {
227     Q_ASSERT(item);
228     //now it's the time for creating instance actions
229     if (!d->instanceActionsInitialized) {
230         initInstanceActions();
231         d->instanceActionsInitialized = true;
232     }
233 
234     d->status.clearStatus();
235     KexiWindow *window = new KexiWindow(parent,
236                                         info()->supportedViewModes(), this, item);
237 
238     KexiProject *project = KexiMainWindowIface::global()->project();
239     KDbObject object(project->typeIdForPluginId(info()->pluginId()));
240     object.setName(item->name());
241     object.setCaption(item->caption());
242     object.setDescription(item->description());
243 
244     /*! @todo js: apply settings for caption displaying method; there can be option for
245      - displaying item.caption() as caption, if not empty, without instanceName
246      - displaying the same as above in tabCaption (or not) */
247     window->setId(item->identifier()); //not needed, but we did it
248     window->setWindowIcon(QIcon::fromTheme(window->iconName()));
249     KexiWindowData *windowData = createWindowData(window);
250     if (!windowData) {
251         d->status = Kexi::ObjectStatus(KexiMainWindowIface::global()->project()->dbConnection(),
252                                        xi18n("Could not create object's window."), xi18n("The plugin or object definition may be corrupted."));
253         delete window;
254         return 0;
255     }
256     window->setData(windowData);
257 
258     if (!item->neverSaved()) {
259         //we have to load object data for this dialog
260         loadAndSetSchemaObject(window, object, viewMode);
261         if (!window->schemaObject()) {
262             //last chance:
263             if (false == d->askForOpeningInTextMode(
264                         window, item, window->supportedViewModes(), viewMode)) {
265                 delete window;
266                 return 0;
267             }
268             viewMode = Kexi::TextViewMode;
269             loadAndSetSchemaObject(window, object, viewMode);
270         }
271         if (!window->schemaObject()) {
272             if (!d->status.error())
273                 d->status = Kexi::ObjectStatus(KexiMainWindowIface::global()->project()->dbConnection(),
274                                                xi18n("Could not load object's definition."), xi18n("Object design may be corrupted."));
275             d->status.append(
276                 Kexi::ObjectStatus(xi18nc("@info",
277                                           "You can delete <resource>%1</resource> object and create it again.",
278                                           item->name()), QString()));
279 
280             window->close();
281             delete window;
282             return 0;
283         }
284     }
285 
286     bool switchingFailed = false;
287     bool dummy;
288     tristate res = window->switchToViewMode(viewMode, staticObjectArgs, &dummy);
289     if (!res) {
290         tristate askForOpeningInTextModeRes
291         = d->askForOpeningInTextMode(window, item, window->supportedViewModes(), viewMode);
292         if (true == askForOpeningInTextModeRes) {
293             window->close();
294             delete window;
295             //try in text mode
296             return openInstance(parent, item, Kexi::TextViewMode, staticObjectArgs);
297         } else if (false == askForOpeningInTextModeRes) {
298             window->close();
299             delete window;
300             qWarning() << "!window, cannot switch to a view mode" <<
301                 Kexi::nameForViewMode(viewMode);
302             return 0;
303         }
304         //the window has an error info
305         switchingFailed = true;
306     }
307     if (~res)
308         switchingFailed = true;
309 
310     if (switchingFailed) {
311         d->status = window->status();
312         window->close();
313         delete window;
314         qWarning() << "!window, switching to view mode failed, " <<
315             Kexi::nameForViewMode(viewMode);
316         return 0;
317     }
318     window->registerWindow(); //ok?
319     window->show();
320 
321     window->setMinimumSize(window->minimumSizeHint().width(), window->minimumSizeHint().height());
322 
323     //dirty only if it's a new object
324     if (window->selectedView()) {
325         window->selectedView()->setDirty(
326             internalPropertyValue("newObjectsAreDirty", false).toBool() ? item->neverSaved() : false);
327     }
328     return window;
329 }
330 
loadSchemaObject(KexiWindow * window,const KDbObject & object,Kexi::ViewMode viewMode,bool * ownedByWindow)331 KDbObject* Part::loadSchemaObject(KexiWindow *window, const KDbObject& object,
332         Kexi::ViewMode viewMode, bool *ownedByWindow)
333 {
334     Q_UNUSED(window);
335     Q_UNUSED(viewMode);
336     Q_ASSERT(ownedByWindow);
337     KDbObject *newObject = new KDbObject();
338     *newObject = object;
339     *ownedByWindow = true;
340     return newObject;
341 }
342 
loadAndSetSchemaObject(KexiWindow * window,const KDbObject & object,Kexi::ViewMode viewMode)343 void Part::loadAndSetSchemaObject(KexiWindow *window, const KDbObject& object,
344     Kexi::ViewMode viewMode)
345 {
346     bool schemaObjectOwned = true;
347     KDbObject* sd = loadSchemaObject(window, object, viewMode, &schemaObjectOwned);
348     window->setSchemaObject(sd);
349     window->setSchemaObjectOwned(schemaObjectOwned);
350 }
351 
loadDataBlock(KexiWindow * window,QString * dataString,const QString & dataID)352 bool Part::loadDataBlock(KexiWindow *window, QString *dataString, const QString& dataID)
353 {
354     if (true != KexiMainWindowIface::global()->project()->dbConnection()->loadDataBlock(
355                 window->id(), dataString, dataID))
356     {
357         d->status = Kexi::ObjectStatus(KexiMainWindowIface::global()->project()->dbConnection(),
358                                        xi18n("Could not load object's data."),
359                                        xi18nc("@info",
360                                               "Data identifier: <resource>%1</resource>.", dataID));
361         d->status.append(*window);
362         return false;
363     }
364     return true;
365 }
366 
initPartActions()367 void Part::initPartActions()
368 {
369 }
370 
initInstanceActions()371 void Part::initInstanceActions()
372 {
373 }
374 
remove(KexiPart::Item * item)375 tristate Part::remove(KexiPart::Item *item)
376 {
377     Q_ASSERT(item);
378     KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection();
379     if (!conn)
380         return false;
381     return conn->removeObject(item->identifier());
382 }
383 
createWindowData(KexiWindow * window)384 KexiWindowData* Part::createWindowData(KexiWindow* window)
385 {
386     return new KexiWindowData(window);
387 }
388 
instanceName() const389 QString Part::instanceName() const
390 {
391     return d->instanceName;
392 }
393 
toolTip() const394 QString Part::toolTip() const
395 {
396     return d->toolTip;
397 }
398 
whatsThis() const399 QString Part::whatsThis() const
400 {
401     return d->whatsThis;
402 }
403 
rename(KexiPart::Item * item,const QString & newName)404 tristate Part::rename(KexiPart::Item *item, const QString& newName)
405 {
406     Q_UNUSED(item);
407     Q_UNUSED(newName);
408     return true;
409 }
410 
instanceGuiClient(Kexi::ViewMode mode) const411 GUIClient* Part::instanceGuiClient(Kexi::ViewMode mode) const
412 {
413     return d->instanceGuiClients.value((int)mode);
414 }
415 
guiClient() const416 GUIClient* Part::guiClient() const
417 {
418     return d->guiClient;
419 }
420 
lastOperationStatus() const421 const Kexi::ObjectStatus& Part::lastOperationStatus() const
422 {
423     return d->status;
424 }
425 
currentQuery(KexiView * view)426 KDbQuerySchema* Part::currentQuery(KexiView* view)
427 {
428     Q_UNUSED(view);
429     return 0;
430 }
431 
fullCaptionForItem(KexiPart::Item * item,KexiPart::Part * part)432 KEXICORE_EXPORT QString KexiPart::fullCaptionForItem(KexiPart::Item *item, KexiPart::Part *part)
433 {
434     Q_ASSERT(item);
435     Q_ASSERT(part);
436     if (part)
437         return item->name() + " : " + part->info()->name();
438     return item->name();
439 }
440