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