Krita Source Code Documentation
Loading...
Searching...
No Matches
KisPart.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 1998-1999 Torben Weis <weis@kde.org>
3 * SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
4 * SPDX-FileCopyrightText: 2007-2008 Thorsten Zachmann <zachmann@kde.org>
5 * SPDX-FileCopyrightText: 2010-2012 Boudewijn Rempt <boud@valdyas.org>
6 * SPDX-FileCopyrightText: 2011 Inge Wallin <ingwa@kogmbh.com>
7 * SPDX-FileCopyrightText: 2015 Michael Abrahams <miabraha@gmail.com>
8 *
9 * SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11
12#include "KisPart.h"
13
14#include <config-mlt.h>
15
16#include "KoProgressProxy.h"
17#include <KoCanvasController.h>
19#include <KoColorSpaceEngine.h>
20#include <KoCanvasBase.h>
21#include <KoToolManager.h>
24#include <kis_icon.h>
25
26#include "KisApplication.h"
27#include "KisMainWindow.h"
28#include "KisDocument.h"
29#include "KisView.h"
30#include "KisViewManager.h"
32#include "KoDocumentInfo.h"
33#include "KisUsageLogger.h"
34
35#include <kis_debug.h>
36#include <KoResourcePaths.h>
37#include <KoDialog.h>
38#include <QMessageBox>
39#include <QMenu>
40#include <QScopedPointer>
41#include <QMap>
42#include <QRegularExpression>
43
44#include <QMenuBar>
45#include <klocalizedstring.h>
46#include <kactioncollection.h>
47#include <kconfig.h>
48#include <kconfiggroup.h>
49#include <QKeySequence>
50
51#include <QApplication>
52#include <QDomDocument>
53#include <QDomElement>
54#include <QGlobalStatic>
55#include <KisMimeDatabase.h>
57
58#include <kis_group_layer.h>
59#include "kis_config.h"
64#include "kis_time_span.h"
65#include "kis_idle_watcher.h"
66#include "kis_image.h"
68
70#include "kis_action.h"
71#include "kis_action_registry.h"
72#include "KisSessionResource.h"
73#include "KisBusyWaitBroker.h"
78#include "KisPlaybackEngine.h"
79#include "KisPlaybackEngineQT.h"
80
81#ifdef HAVE_MLT
83#endif
84
85Q_GLOBAL_STATIC(KisPart, s_instance)
86
87
88class Q_DECL_HIDDEN KisPart::Private
89{
90public:
92 : part(_part)
93 , idleWatcher(2500)
94 , animationCachePopulator(_part)
95 , playbackEngine(nullptr)
96 {
97 }
98
100 {
101 }
102
104
110 std::unique_ptr<KisPlaybackEngine> playbackEngine;
111
113 bool closingSession{false};
115
116 QMap<QUrl, QUrl> pendingAddRecentUrlMap;
117
119 Q_FOREACH(auto view, views) {
120 if (view && view->isVisible() && view->document() == document) {
121 return view->queryClose();
122 }
123 }
124
125 return true;
126 }
127};
128
129
131{
132 return s_instance;
133}
134
135namespace {
136void busyWaitWithFeedback(KisImageSP image)
137{
138 const int busyWaitDelay = 1000;
139 if (KisPart::instance()->currentMainwindow()) {
140 KisDelayedSaveDialog dialog(image, KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, KisPart::instance()->currentMainwindow());
141 dialog.blockIfImageIsBusy();
142 }
143}
144}
145
147 : d(new Private(this))
148{
149 // Preload all the resources in the background
152
153 connect(this, SIGNAL(documentOpened(QString)),
154 this, SLOT(updateIdleWatcherConnections()));
155
156 connect(this, SIGNAL(documentClosed(QString)),
157 this, SLOT(updateIdleWatcherConnections()));
158
159 connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()),
160 this, SLOT(updateShortcuts()));
161 connect(&d->idleWatcher, SIGNAL(startedIdleMode()),
162 &d->animationCachePopulator, SLOT(slotRequestRegeneration()));
163 connect(&d->idleWatcher, SIGNAL(startedIdleMode()),
164 KisMemoryStatisticsServer::instance(), SLOT(tryForceUpdateMemoryStatisticsWhileIdle()));
165
166 // We start by loading the simple QTimer-based anim playback engine first.
167 // To save RAM, the MLT-based engine will be loaded later, once the KisImage in question becomes animated.
169
170 d->animationCachePopulator.slotRequestRegeneration();
171 KisBusyWaitBroker::instance()->setFeedbackCallback(&busyWaitWithFeedback);
172}
173
175{
176 while (!d->documents.isEmpty()) {
177 delete d->documents.takeFirst();
178 }
179
180 while (!d->views.isEmpty()) {
181 delete d->views.takeFirst();
182 }
183
184 while (!d->mainWindows.isEmpty()) {
185 delete d->mainWindows.takeFirst();
186 }
187
188 delete d;
189}
190
192{
193 QVector<KisImageSP> images;
194
195 Q_FOREACH (QPointer<KisDocument> document, documents()) {
196 if (document->image()) {
197 images << document->image();
198 }
199 }
200
201 d->idleWatcher.setTrackedImages(images);
202
206 d->idleWatcher.forceImageModified();
207}
208
209void KisPart::addDocument(KisDocument *document, bool notify)
210{
211 //dbgUI << "Adding document to part list" << document;
212 Q_ASSERT(document);
213 if (!d->documents.contains(document)) {
214 d->documents.append(document);
215 if (notify){
216 Q_EMIT documentOpened('/'+ objectName());
217 Q_EMIT sigDocumentAdded(document);
218 }
219 connect(document, SIGNAL(sigSavingFinished(QString)), SLOT(slotDocumentSaved(QString)));
220 }
221}
222
224{
225 return d->documents;
226}
227
229{
230 KisDocument *doc = new KisDocument();
231 return doc;
232}
233
235{
236 KisDocument *doc = new KisDocument(false);
237 return doc;
238}
239
240
242{
243 return d->documents.size();
244}
245
246void KisPart::removeDocument(KisDocument *document, bool deleteDocument)
247{
248 if (document) {
249 d->documents.removeAll(document);
250 Q_EMIT documentClosed('/' + objectName());
251 Q_EMIT sigDocumentRemoved(document->path());
252 if (deleteDocument) {
253 document->deleteLater();
254 }
255 }
256}
257
259{
260 KisMainWindow *mw = new KisMainWindow(id);
261 dbgUI <<"mainWindow" << (void*)mw << "added to view" << this;
262 d->mainWindows.append(mw);
263
265
266 return mw;
267}
268
273
274
276 KisViewManager *viewManager,
277 QWidget *parent)
278{
279 // If creating the canvas fails, record this and disable OpenGL next time
280 KisConfig cfg(false);
281 KConfigGroup grp( KSharedConfig::openConfig(), "crashprevention");
282 if (grp.readEntry("CreatingCanvas", false)) {
283 cfg.disableOpenGL();
284 }
285 if (cfg.canvasState() == "OPENGL_FAILED") {
286 cfg.disableOpenGL();
287 }
288 grp.writeEntry("CreatingCanvas", true);
289 grp.sync();
290
291 KisView *view = nullptr;
292 {
293 KisCursorOverrideLock cursorLock(Qt::WaitCursor);
294 view = new KisView(document, viewManager, parent);
295 }
296
297 // Record successful canvas creation
298 grp.writeEntry("CreatingCanvas", false);
299 grp.sync();
300
301 addView(view);
302
303 return view;
304}
305
307{
308 if (!view)
309 return;
310
311 if (!d->views.contains(view)) {
312 d->views.append(view);
313 }
314
315 Q_EMIT sigViewAdded(view);
316}
317
319{
320 if (!view) return;
321
329
330 Q_EMIT sigViewRemoved(view);
331
332 QPointer<KisDocument> doc = view->document();
333 d->views.removeAll(view);
334
335 if (doc) {
336 bool found = false;
337 Q_FOREACH (QPointer<KisView> view, d->views) {
338 if (view && view->document() == doc) {
339 found = true;
340 break;
341 }
342 }
343 if (!found) {
344 removeDocument(doc);
345 }
346 }
347}
348
350{
351 return d->views;
352}
353
355{
356 if (!doc) {
357 return d->views.count();
358 }
359 else {
360 int count = 0;
361 Q_FOREACH (QPointer<KisView> view, d->views) {
362 if (view && view->isVisible() && view->document() == doc) {
363 count++;
364 }
365 }
366 return count;
367 }
368}
369
370bool KisPart::closingSession() const
371{
372 return d->closingSession;
373}
374
376{
377 return s_instance.exists();
378}
379
380bool KisPart::closeSession(bool keepWindows)
381{
382 d->closingSession = true;
383
384 Q_FOREACH(auto document, d->documents) {
385 if (!d->queryCloseDocument(document.data())) {
386 d->closingSession = false;
387 return false;
388 }
389 }
390
391 if (d->currentSession) {
392 KisConfig kisCfg(false);
393 if (kisCfg.saveSessionOnQuit(false)) {
394
395 d->currentSession->storeCurrentWindows();
397 bool result = model.updateResource(d->currentSession);
398 Q_UNUSED(result);
399
400 KConfigGroup cfg = KSharedConfig::openConfig()->group("session");
401 cfg.writeEntry("previousSession", d->currentSession->name());
402 }
403
404 d->currentSession = nullptr;
405 }
406
407 if (!keepWindows) {
408 Q_FOREACH (auto window, d->mainWindows) {
409 window->close();
410 }
411
412 if (d->sessionManager) {
413 d->sessionManager->close();
414 }
415 }
416
417 d->closingSession = false;
418 return true;
419}
420
421void KisPart::slotDocumentSaved(const QString &filePath)
422{
423 // We used to use doc->path(), but it does not contain the correct output
424 // file path when doing an export, therefore we now pass it directly from
425 // the sigSavingFinished signal.
426 // KisDocument *doc = qobject_cast<KisDocument*>(sender());
427 Q_EMIT sigDocumentSaved(filePath);
428
429 QUrl url = QUrl::fromLocalFile(filePath);
431 if (!d->pendingAddRecentUrlMap.contains(url)) {
432 return;
433 }
434 QUrl oldUrl = d->pendingAddRecentUrlMap.take(url);
435 addRecentURLToAllMainWindows(url, oldUrl);
436}
437
439{
440 dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this;
441 if (mainWindow) {
442 d->mainWindows.removeAll(mainWindow);
443 }
444}
445
447{
448 return d->mainWindows;
449}
450
452{
453 return d->mainWindows.count();
454}
455
456
458{
459 QWidget *widget = qApp->activeWindow();
460 KisMainWindow *mainWindow = qobject_cast<KisMainWindow*>(widget);
461 while (!mainWindow && widget) {
462 widget = widget->parentWidget();
463 mainWindow = qobject_cast<KisMainWindow*>(widget);
464 }
465
466 if (!mainWindow && mainWindows().size() > 0) {
467 mainWindow = mainWindows().first();
468 }
469 return mainWindow;
470
471}
472
474{
475 return currentMainwindow();
476}
477
479{
480 Q_FOREACH(QPointer<KisMainWindow> mainWindow, d->mainWindows) {
481 if (mainWindow->id() == id) {
482 return mainWindow;
483 }
484 }
485
486 return nullptr;
487}
488
490{
491 return &d->idleWatcher;
492}
493
495{
496 return &d->animationCachePopulator;
497}
498
500{
501 return d->playbackEngine.get();
502}
503
505 KisImageAnimationInterface* animInterface = image->animationInterface();
506 if ( animInterface && animInterface->documentPlaybackRange().contains(frame)) {
507 d->animationCachePopulator.requestRegenerationWithPriorityFrame(image, frame);
508 }
509}
510
511void KisPart::openExistingFile(const QString &path)
512{
513 // TODO: refactor out this method!
514
517
519}
520
522{
523 // Update the UI actions. KisActionRegistry also takes care of updating
524 // shortcut hints in tooltips.
525 Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) {
526 KisKActionCollection *ac = mainWindow->actionCollection();
527
528 ac->updateShortcuts();
529 }
530}
531
532void KisPart::openTemplate(const QUrl &url)
533{
534 KisCursorOverrideLock cursorLock(Qt::BusyCursor);
535
536 KisDocument *document = createDocument();
537
538 bool ok = document->loadNativeFormat(url.toLocalFile());
539 document->setModified(false);
540 document->undoStack()->clear();
541 document->documentInfo()->resetMetaData();
542
543 if (ok) {
544 QString mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile());
545 // in case this is a open document template remove the -template from the end
546 mimeType.remove( QRegularExpression( "-template$" ) );
547 document->setMimeTypeAfterLoading(mimeType);
548 document->resetPath();
549 document->setReadWrite(true);
550 }
551 else {
552 if (document->errorMessage().isEmpty()) {
553 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1", document->localFilePath()));
554 }
555 else {
556 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1\nReason: %2", document->localFilePath(), document->errorMessage()));
557 }
558 delete document;
559 return;
560 }
561 QMap<QString, QString> dictionary;
562 // XXX: fill the dictionary from the desktop file
564 document->image()->rootLayer()->accept(v);
565
566 addDocument(document);
567
570}
571
572void KisPart::addRecentURLToAllMainWindows(QUrl url, QUrl oldUrl)
573{
574 // Add entry to recent documents list
575 // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.)
576 if (!url.isEmpty()) {
577 bool ok = true;
578 if (url.isLocalFile()) {
579 QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile();
580 const QStringList tmpDirs = QStandardPaths::locateAll(QStandardPaths::TempLocation, "", QStandardPaths::LocateDirectory);
581 for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) {
582 if (path.contains(*it)) {
583 ok = false; // it's in the tmp resource
584 }
585 }
586
587 const QStringList templateDirs = KoResourcePaths::findDirs("templates");
588 for (QStringList::ConstIterator it = templateDirs.begin() ; ok && it != templateDirs.end() ; ++it) {
589 if (path.contains(*it)) {
590 ok = false; // it's in the templates directory.
591 break;
592 }
593 }
594 }
595 if (ok) {
596 if (!oldUrl.isEmpty()) {
598 }
600 }
601 }
602}
603
605{
606 d->pendingAddRecentUrlMap.insert(url, oldUrl);
607}
608
616
618{
620 KisViewManager *manager = mw ? mw->viewManager() : 0;
621 return manager ? manager->inputManager() : 0;
622}
623
625{
626 if (d->sessionManager.isNull()) {
627 d->sessionManager = new KisSessionManagerDialog();
628 d->sessionManager->setAttribute(Qt::WA_DeleteOnClose);
629 }
630
631 d->sessionManager->show();
632 d->sessionManager->activateWindow();
633}
634
636{
638 window->initializeGeometry();
639 window->show();
640
641}
642
643bool KisPart::restoreSession(const QString &sessionName)
644{
645 if (sessionName.isNull()) return false;
646
648 KisSessionResourceSP session = rserver->resource("", "", sessionName);
649 if (!session || !session->valid()) return false;
650
651 return restoreSession(session);
652}
653
655{
656 session->restore();
657 d->currentSession = session;
658 return true;
659}
660
662{
663 d->currentSession = session;
664}
665
667{
668#ifdef HAVE_MLT
669
670 // TODO: This is a slightly hacky workaround to loading the MLT engine over itself,
671 // as the QT-based engine no longer supports audio. Is there a better way?
672 if (d->playbackEngine->supportsAudio()) {
673 return;
674 }
675
677
678 if (canvas) {
679 d->playbackEngine->setObservedCanvas(canvas);
680 }
681
682#endif //HAVE_MLT
683}
684
686{
687#ifdef HAVE_MLT
688
689 d->playbackEngine.reset();
690
691#endif //HAVE_MLT
692}
693
695{
696 // make sure that the old engine is still alive until the end
697 // of the emitted signal
698 std::unique_ptr<KisPlaybackEngine> backup(p_playbackEngine);
699 d->playbackEngine.swap(backup);
700
701 // Log all changes to playback engine for easier debugging.
702 // (See `krita.log` or `Help > Show Krita log for bug reports`.)
703 KisUsageLogger::log("Animation Playback Engine: " + QString(p_playbackEngine->metaObject()->className()));
704
705 Q_EMIT playbackEngineChanged(p_playbackEngine);
706}
707
708#include "moc_KisPart.cpp"
qreal v
quint64 part(quint64 n1, quint64 n2, int p)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
static KisActionRegistry * instance()
void setFeedbackCallback(std::function< void(KisImageSP)> callback)
static KisBusyWaitBroker * instance()
void disableOpenGL() const
QString canvasState(bool defaultValue=false) const
bool saveSessionOnQuit(bool defaultValue) const
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
KisImageAnimationInterface * animationInterface() const
Central object to manage canvas input.
A container for a set of QAction objects.
virtual KisKActionCollection * actionCollection() const
Main window for Krita.
bool openDocument(const QString &path, OpenFlags flags)
KisView * addViewAndNotifyLoadingCompleted(KisDocument *document, QMdiSubWindow *subWindow=0)
KisViewManager * viewManager
bool hackIsSaving() const
void synchronizeDynamicActions()
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
KisAnimationCachePopulator animationCachePopulator
Definition KisPart.cpp:109
bool closeSession(bool keepWindows=false)
Definition KisPart.cpp:380
void sigDocumentSaved(const QString &url)
QList< QPointer< KisDocument > > documents
Definition KisPart.cpp:107
Private(KisPart *_part)
Definition KisPart.cpp:91
void sigViewAdded(KisView *view)
void removeMainWindow(KisMainWindow *mainWindow)
Definition KisPart.cpp:438
QPointer< KisSessionManagerDialog > sessionManager
Definition KisPart.cpp:114
void updateShortcuts()
Definition KisPart.cpp:521
bool queryCloseDocument(KisDocument *document)
Definition KisPart.cpp:118
void upgradeToPlaybackEngineMLT(class KoCanvasBase *canvas)
Definition KisPart.cpp:666
QList< QPointer< KisMainWindow > > mainWindows
Definition KisPart.cpp:106
void addRecentURLToAllMainWindows(QUrl url, QUrl oldUrl=QUrl())
Definition KisPart.cpp:572
void setPlaybackEngine(KisPlaybackEngine *p_playbackEngine)
Definition KisPart.cpp:694
void documentClosed(const QString &ref)
KisView * createView(KisDocument *document, KisViewManager *viewManager, QWidget *parent)
Definition KisPart.cpp:275
void openTemplate(const QUrl &url)
Definition KisPart.cpp:532
KisInputManager * currentInputManager()
Definition KisPart.cpp:617
KisSessionResourceSP currentSession
Definition KisPart.cpp:112
static KisPart * instance()
Definition KisPart.cpp:130
void sigDocumentAdded(KisDocument *document)
std::unique_ptr< KisPlaybackEngine > playbackEngine
Definition KisPart.cpp:110
bool restoreSession(const QString &sessionName)
Definition KisPart.cpp:643
Private *const d
Definition KisPart.h:328
void slotDocumentSaved(const QString &filePath)
Definition KisPart.cpp:421
void addView(KisView *view)
Definition KisPart.cpp:306
~Private()
Definition KisPart.cpp:99
void removeView(KisView *view)
Definition KisPart.cpp:318
QMap< QUrl, QUrl > pendingAddRecentUrlMap
Definition KisPart.cpp:116
void addDocument(KisDocument *document, bool notify=true)
Definition KisPart.cpp:209
void queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl url, QUrl oldUrl=QUrl())
Definition KisPart.cpp:604
int documentCount() const
Definition KisPart.cpp:241
int viewCount(KisDocument *doc) const
Definition KisPart.cpp:354
void playbackEngineChanged(KisPlaybackEngine *newPlaybackEngine)
static bool exists()
Definition KisPart.cpp:375
KisAnimationCachePopulator * cachePopulator() const
Definition KisPart.cpp:494
KisMainWindow * currentMainwindow() const
Definition KisPart.cpp:457
void prioritizeFrameForCache(KisImageSP image, int frame)
Definition KisPart.cpp:504
KisIdleWatcher idleWatcher
Definition KisPart.cpp:108
void startBlankSession()
Definition KisPart.cpp:635
~KisPart() override
Definition KisPart.cpp:174
KisMainWindow * windowById(QUuid id) const
Definition KisPart.cpp:478
void removeDocument(KisDocument *document, bool deleteDocument=true)
Definition KisPart.cpp:246
KisDocument * createTemporaryDocument() const
Definition KisPart.cpp:234
void sigViewRemoved(KisView *view)
QWidget * currentMainwindowAsQWidget() const
Definition KisPart.cpp:473
void startCustomDocument(KisDocument *doc)
startCustomDocument adds the given document to the document list and deletes the sender()
Definition KisPart.cpp:609
KisDocument * createDocument() const
Definition KisPart.cpp:228
bool closingSession
Definition KisPart.cpp:113
QList< QPointer< KisView > > views
Definition KisPart.cpp:105
void setCurrentSession(KisSessionResourceSP session)
Definition KisPart.cpp:661
void documentOpened(const QString &ref)
void updateIdleWatcherConnections()
Definition KisPart.cpp:191
void openExistingFile(const QString &path)
Definition KisPart.cpp:511
void sigDocumentRemoved(const QString &filename)
int mainwindowCount() const
Definition KisPart.cpp:451
void unloadPlaybackEngine()
Definition KisPart.cpp:685
void showSessionManager()
Definition KisPart.cpp:624
void sigMainWindowIsBeingCreated(KisMainWindow *window)
void notifyMainWindowIsBeingCreated(KisMainWindow *mainWindow)
notifyMainWindowIsBeingCreated emits the sigMainWindowCreated signal
Definition KisPart.cpp:269
KisPart * part
Definition KisPart.cpp:103
KisMainWindow * createMainWindow(QUuid id=QUuid())
Definition KisPart.cpp:258
The KisPlaybackEngineMLT class is an implementation of KisPlaybackEngine that uses MLT (Media Lovin' ...
The KisPlaybackEngineQT class is an implementation of KisPlaybackEngine that drives animation playbac...
Krita's base animation playback engine for producing image frame changes and associated audio.
static KisRecentFileIconCache * instance()
void reloadFileIcon(const QUrl &url)
void remove(const QUrl &url)
static KisRecentFilesManager * instance()
The KisResourceModel class provides the main access to resources. It is possible to filter the resour...
bool updateResource(KoResourceSP resource) override
updateResource creates a new version of the resource in the storage and in the database....
static KisResourceServerProvider * instance()
KoResourceServer< KisSessionResource > * sessionServer()
bool contains(int time) const
KisTranslateLayerNamesVisitor::KisTranslateLayerNamesVisitor translates layer names from templates.
static void log(const QString &message)
Logs with date/time.
KisInputManager * inputManager() const
Filters events and sends them to canvas actions.
KisMainWindow * mainWindow() const
Definition KisView.cpp:1089
QPointer< KisDocument > document
Definition KisView.cpp:121
static QStringList findDirs(const QString &type)
QSharedPointer< T > resource(const QString &md5, const QString &fileName, const QString &name)
resource retrieves a resource. If the md5sum is not empty, the resource will only be retrieved if a r...
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define dbgUI
Definition kis_debug.h:55
const QString Sessions
static KisMemoryStatisticsServer * instance()
static KoResourceServerProvider * instance()
KisCanvas2 * canvas