File indexing completed on 2024-05-19 04:29:17
0001 /* This file is part of the KDE project 0002 * SPDX-FileCopyrightText: 1998-1999 Torben Weis <weis@kde.org> 0003 * SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org> 0004 * SPDX-FileCopyrightText: 2007-2008 Thorsten Zachmann <zachmann@kde.org> 0005 * SPDX-FileCopyrightText: 2010-2012 Boudewijn Rempt <boud@valdyas.org> 0006 * SPDX-FileCopyrightText: 2011 Inge Wallin <ingwa@kogmbh.com> 0007 * SPDX-FileCopyrightText: 2015 Michael Abrahams <miabraha@gmail.com> 0008 * 0009 * SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 0012 #include "KisPart.h" 0013 0014 #include <config-mlt.h> 0015 0016 #include "KoProgressProxy.h" 0017 #include <KoCanvasController.h> 0018 #include <KoCanvasControllerWidget.h> 0019 #include <KoColorSpaceEngine.h> 0020 #include <KoCanvasBase.h> 0021 #include <KoToolManager.h> 0022 #include <KoShapeControllerBase.h> 0023 #include <KoResourceServerProvider.h> 0024 #include <kis_icon.h> 0025 0026 #include "KisApplication.h" 0027 #include "KisMainWindow.h" 0028 #include "KisDocument.h" 0029 #include "KisView.h" 0030 #include "KisViewManager.h" 0031 #include "KisImportExportManager.h" 0032 #include "KoDocumentInfo.h" 0033 0034 #include <kis_debug.h> 0035 #include <KoResourcePaths.h> 0036 #include <KoDialog.h> 0037 #include <QMessageBox> 0038 #include <QMenu> 0039 #include <QScopedPointer> 0040 #include <QMap> 0041 0042 #include <QMenuBar> 0043 #include <klocalizedstring.h> 0044 #include <kactioncollection.h> 0045 #include <kconfig.h> 0046 #include <kconfiggroup.h> 0047 #include <QKeySequence> 0048 0049 #include <QDialog> 0050 #include <QApplication> 0051 #include <QDomDocument> 0052 #include <QDomElement> 0053 #include <QGlobalStatic> 0054 #include <KisMimeDatabase.h> 0055 #include <dialogs/KisSessionManagerDialog.h> 0056 0057 #include <kis_group_layer.h> 0058 #include "kis_config.h" 0059 #include "kis_shape_controller.h" 0060 #include "KisResourceServerProvider.h" 0061 #include "kis_animation_cache_populator.h" 0062 #include "kis_image_animation_interface.h" 0063 #include "kis_time_span.h" 0064 #include "kis_idle_watcher.h" 0065 #include "kis_image.h" 0066 #include "KisTranslateLayerNamesVisitor.h" 0067 #include "kis_color_manager.h" 0068 0069 #include <KisCursorOverrideLock.h> 0070 #include "kis_action.h" 0071 #include "kis_action_registry.h" 0072 #include "KisSessionResource.h" 0073 #include "KisBusyWaitBroker.h" 0074 #include "dialogs/kis_delayed_save_dialog.h" 0075 #include "kis_memory_statistics_server.h" 0076 #include "KisRecentFilesManager.h" 0077 #include "KisRecentFileIconCache.h" 0078 #include "KisPlaybackEngine.h" 0079 #include "KisPlaybackEngineQT.h" 0080 0081 #ifdef HAVE_MLT 0082 #include "KisPlaybackEngineMLT.h" 0083 #endif 0084 0085 Q_GLOBAL_STATIC(KisPart, s_instance) 0086 0087 0088 class Q_DECL_HIDDEN KisPart::Private 0089 { 0090 public: 0091 Private(KisPart *_part) 0092 : part(_part) 0093 , idleWatcher(2500) 0094 , animationCachePopulator(_part) 0095 , playbackEngine(nullptr) 0096 { 0097 } 0098 0099 ~Private() 0100 { 0101 } 0102 0103 KisPart *part; 0104 0105 QList<QPointer<KisView> > views; 0106 QList<QPointer<KisMainWindow> > mainWindows; 0107 QList<QPointer<KisDocument> > documents; 0108 KisIdleWatcher idleWatcher; 0109 KisAnimationCachePopulator animationCachePopulator; 0110 QScopedPointer<KisPlaybackEngine> playbackEngine; 0111 0112 KisSessionResourceSP currentSession; 0113 bool closingSession{false}; 0114 QScopedPointer<KisSessionManagerDialog> sessionManager; 0115 0116 QMap<QUrl, QUrl> pendingAddRecentUrlMap; 0117 0118 bool queryCloseDocument(KisDocument *document) { 0119 Q_FOREACH(auto view, views) { 0120 if (view && view->isVisible() && view->document() == document) { 0121 return view->queryClose(); 0122 } 0123 } 0124 0125 return true; 0126 } 0127 }; 0128 0129 0130 KisPart* KisPart::instance() 0131 { 0132 return s_instance; 0133 } 0134 0135 namespace { 0136 void busyWaitWithFeedback(KisImageSP image) 0137 { 0138 const int busyWaitDelay = 1000; 0139 if (KisPart::instance()->currentMainwindow()) { 0140 KisDelayedSaveDialog dialog(image, KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, KisPart::instance()->currentMainwindow()); 0141 dialog.blockIfImageIsBusy(); 0142 } 0143 } 0144 } 0145 0146 KisPart::KisPart() 0147 : d(new Private(this)) 0148 { 0149 // Preload all the resources in the background 0150 Q_UNUSED(KoResourceServerProvider::instance()); 0151 Q_UNUSED(KisResourceServerProvider::instance()); 0152 Q_UNUSED(KisColorManager::instance()); 0153 0154 connect(this, SIGNAL(documentOpened(QString)), 0155 this, SLOT(updateIdleWatcherConnections())); 0156 0157 connect(this, SIGNAL(documentClosed(QString)), 0158 this, SLOT(updateIdleWatcherConnections())); 0159 0160 connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()), 0161 this, SLOT(updateShortcuts())); 0162 connect(&d->idleWatcher, SIGNAL(startedIdleMode()), 0163 &d->animationCachePopulator, SLOT(slotRequestRegeneration())); 0164 connect(&d->idleWatcher, SIGNAL(startedIdleMode()), 0165 KisMemoryStatisticsServer::instance(), SLOT(tryForceUpdateMemoryStatisticsWhileIdle())); 0166 0167 // We start by loading the simple QTimer-based anim playback engine first. 0168 // To save RAM, the MLT-based engine will be loaded later, once the KisImage in question becomes animated. 0169 setPlaybackEngine(new KisPlaybackEngineQT(this)); 0170 0171 d->animationCachePopulator.slotRequestRegeneration(); 0172 KisBusyWaitBroker::instance()->setFeedbackCallback(&busyWaitWithFeedback); 0173 } 0174 0175 KisPart::~KisPart() 0176 { 0177 while (!d->documents.isEmpty()) { 0178 delete d->documents.takeFirst(); 0179 } 0180 0181 while (!d->views.isEmpty()) { 0182 delete d->views.takeFirst(); 0183 } 0184 0185 while (!d->mainWindows.isEmpty()) { 0186 delete d->mainWindows.takeFirst(); 0187 } 0188 0189 delete d; 0190 } 0191 0192 void KisPart::updateIdleWatcherConnections() 0193 { 0194 QVector<KisImageSP> images; 0195 0196 Q_FOREACH (QPointer<KisDocument> document, documents()) { 0197 if (document->image()) { 0198 images << document->image(); 0199 } 0200 } 0201 0202 d->idleWatcher.setTrackedImages(images); 0203 0204 /** 0205 * Update memory stats on changing the amount of images open in Krita 0206 */ 0207 d->idleWatcher.forceImageModified(); 0208 } 0209 0210 void KisPart::addDocument(KisDocument *document, bool notify) 0211 { 0212 //dbgUI << "Adding document to part list" << document; 0213 Q_ASSERT(document); 0214 if (!d->documents.contains(document)) { 0215 d->documents.append(document); 0216 if (notify){ 0217 emit documentOpened('/'+ objectName()); 0218 emit sigDocumentAdded(document); 0219 } 0220 connect(document, SIGNAL(sigSavingFinished(QString)), SLOT(slotDocumentSaved(QString))); 0221 } 0222 } 0223 0224 QList<QPointer<KisDocument> > KisPart::documents() const 0225 { 0226 return d->documents; 0227 } 0228 0229 KisDocument *KisPart::createDocument() const 0230 { 0231 KisDocument *doc = new KisDocument(); 0232 return doc; 0233 } 0234 0235 KisDocument *KisPart::createTemporaryDocument() const 0236 { 0237 KisDocument *doc = new KisDocument(false); 0238 return doc; 0239 } 0240 0241 0242 int KisPart::documentCount() const 0243 { 0244 return d->documents.size(); 0245 } 0246 0247 void KisPart::removeDocument(KisDocument *document, bool deleteDocument) 0248 { 0249 if (document) { 0250 d->documents.removeAll(document); 0251 emit documentClosed('/' + objectName()); 0252 emit sigDocumentRemoved(document->path()); 0253 if (deleteDocument) { 0254 document->deleteLater(); 0255 } 0256 } 0257 } 0258 0259 KisMainWindow *KisPart::createMainWindow(QUuid id) 0260 { 0261 KisMainWindow *mw = new KisMainWindow(id); 0262 dbgUI <<"mainWindow" << (void*)mw << "added to view" << this; 0263 d->mainWindows.append(mw); 0264 0265 // Add all actions with a menu property to the main window 0266 Q_FOREACH(QAction *action, mw->actionCollection()->actions()) { 0267 QString menuLocation = action->property("menulocation").toString(); 0268 if (!menuLocation.isEmpty()) { 0269 QAction *found = 0; 0270 QList<QAction *> candidates = mw->menuBar()->actions(); 0271 Q_FOREACH(const QString &name, menuLocation.split("/")) { 0272 Q_FOREACH(QAction *candidate, candidates) { 0273 if (candidate->objectName().toLower() == name.toLower()) { 0274 found = candidate; 0275 candidates = candidate->menu()->actions(); 0276 break; 0277 } 0278 } 0279 if (candidates.isEmpty()) { 0280 break; 0281 } 0282 } 0283 0284 if (found && found->menu()) { 0285 found->menu()->addAction(action); 0286 } 0287 } 0288 } 0289 0290 0291 return mw; 0292 } 0293 0294 void KisPart::notifyMainWindowIsBeingCreated(KisMainWindow *mainWindow) 0295 { 0296 emit sigMainWindowIsBeingCreated(mainWindow); 0297 } 0298 0299 0300 KisView *KisPart::createView(KisDocument *document, 0301 KisViewManager *viewManager, 0302 QWidget *parent) 0303 { 0304 // If creating the canvas fails, record this and disable OpenGL next time 0305 KisConfig cfg(false); 0306 KConfigGroup grp( KSharedConfig::openConfig(), "crashprevention"); 0307 if (grp.readEntry("CreatingCanvas", false)) { 0308 cfg.disableOpenGL(); 0309 } 0310 if (cfg.canvasState() == "OPENGL_FAILED") { 0311 cfg.disableOpenGL(); 0312 } 0313 grp.writeEntry("CreatingCanvas", true); 0314 grp.sync(); 0315 0316 KisView *view = nullptr; 0317 { 0318 KisCursorOverrideLock cursorLock(Qt::WaitCursor); 0319 view = new KisView(document, viewManager, parent); 0320 } 0321 0322 // Record successful canvas creation 0323 grp.writeEntry("CreatingCanvas", false); 0324 grp.sync(); 0325 0326 addView(view); 0327 0328 return view; 0329 } 0330 0331 void KisPart::addView(KisView *view) 0332 { 0333 if (!view) 0334 return; 0335 0336 if (!d->views.contains(view)) { 0337 d->views.append(view); 0338 } 0339 0340 emit sigViewAdded(view); 0341 } 0342 0343 void KisPart::removeView(KisView *view) 0344 { 0345 if (!view) return; 0346 0347 /** 0348 * HACK ALERT: we check here explicitly if the document (or main 0349 * window), is saving the stuff. If we close the 0350 * document *before* the saving is completed, a crash 0351 * will happen. 0352 */ 0353 KIS_ASSERT_RECOVER_RETURN(!view->mainWindow()->hackIsSaving()); 0354 0355 emit sigViewRemoved(view); 0356 0357 QPointer<KisDocument> doc = view->document(); 0358 d->views.removeAll(view); 0359 0360 if (doc) { 0361 bool found = false; 0362 Q_FOREACH (QPointer<KisView> view, d->views) { 0363 if (view && view->document() == doc) { 0364 found = true; 0365 break; 0366 } 0367 } 0368 if (!found) { 0369 removeDocument(doc); 0370 } 0371 } 0372 } 0373 0374 QList<QPointer<KisView> > KisPart::views() const 0375 { 0376 return d->views; 0377 } 0378 0379 int KisPart::viewCount(KisDocument *doc) const 0380 { 0381 if (!doc) { 0382 return d->views.count(); 0383 } 0384 else { 0385 int count = 0; 0386 Q_FOREACH (QPointer<KisView> view, d->views) { 0387 if (view && view->isVisible() && view->document() == doc) { 0388 count++; 0389 } 0390 } 0391 return count; 0392 } 0393 } 0394 0395 bool KisPart::closingSession() const 0396 { 0397 return d->closingSession; 0398 } 0399 0400 bool KisPart::exists() 0401 { 0402 return s_instance.exists(); 0403 } 0404 0405 bool KisPart::closeSession(bool keepWindows) 0406 { 0407 d->closingSession = true; 0408 0409 Q_FOREACH(auto document, d->documents) { 0410 if (!d->queryCloseDocument(document.data())) { 0411 d->closingSession = false; 0412 return false; 0413 } 0414 } 0415 0416 if (d->currentSession) { 0417 KisConfig kisCfg(false); 0418 if (kisCfg.saveSessionOnQuit(false)) { 0419 0420 d->currentSession->storeCurrentWindows(); 0421 KisResourceModel model(ResourceType::Sessions); 0422 bool result = model.updateResource(d->currentSession); 0423 Q_UNUSED(result); 0424 0425 KConfigGroup cfg = KSharedConfig::openConfig()->group("session"); 0426 cfg.writeEntry("previousSession", d->currentSession->name()); 0427 } 0428 0429 d->currentSession = nullptr; 0430 } 0431 0432 if (!keepWindows) { 0433 Q_FOREACH (auto window, d->mainWindows) { 0434 window->close(); 0435 } 0436 0437 if (d->sessionManager) { 0438 d->sessionManager->close(); 0439 } 0440 } 0441 0442 d->closingSession = false; 0443 return true; 0444 } 0445 0446 void KisPart::slotDocumentSaved(const QString &filePath) 0447 { 0448 // We used to use doc->path(), but it does not contain the correct output 0449 // file path when doing an export, therefore we now pass it directly from 0450 // the sigSavingFinished signal. 0451 // KisDocument *doc = qobject_cast<KisDocument*>(sender()); 0452 emit sigDocumentSaved(filePath); 0453 0454 QUrl url = QUrl::fromLocalFile(filePath); 0455 KisRecentFileIconCache::instance()->reloadFileIcon(url); 0456 if (!d->pendingAddRecentUrlMap.contains(url)) { 0457 return; 0458 } 0459 QUrl oldUrl = d->pendingAddRecentUrlMap.take(url); 0460 addRecentURLToAllMainWindows(url, oldUrl); 0461 } 0462 0463 void KisPart::removeMainWindow(KisMainWindow *mainWindow) 0464 { 0465 dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this; 0466 if (mainWindow) { 0467 d->mainWindows.removeAll(mainWindow); 0468 } 0469 } 0470 0471 const QList<QPointer<KisMainWindow> > &KisPart::mainWindows() const 0472 { 0473 return d->mainWindows; 0474 } 0475 0476 int KisPart::mainwindowCount() const 0477 { 0478 return d->mainWindows.count(); 0479 } 0480 0481 0482 KisMainWindow *KisPart::currentMainwindow() const 0483 { 0484 QWidget *widget = qApp->activeWindow(); 0485 KisMainWindow *mainWindow = qobject_cast<KisMainWindow*>(widget); 0486 while (!mainWindow && widget) { 0487 widget = widget->parentWidget(); 0488 mainWindow = qobject_cast<KisMainWindow*>(widget); 0489 } 0490 0491 if (!mainWindow && mainWindows().size() > 0) { 0492 mainWindow = mainWindows().first(); 0493 } 0494 return mainWindow; 0495 0496 } 0497 0498 QWidget *KisPart::currentMainwindowAsQWidget() const 0499 { 0500 return currentMainwindow(); 0501 } 0502 0503 KisMainWindow * KisPart::windowById(QUuid id) const 0504 { 0505 Q_FOREACH(QPointer<KisMainWindow> mainWindow, d->mainWindows) { 0506 if (mainWindow->id() == id) { 0507 return mainWindow; 0508 } 0509 } 0510 0511 return nullptr; 0512 } 0513 0514 KisIdleWatcher* KisPart::idleWatcher() const 0515 { 0516 return &d->idleWatcher; 0517 } 0518 0519 KisAnimationCachePopulator* KisPart::cachePopulator() const 0520 { 0521 return &d->animationCachePopulator; 0522 } 0523 0524 KisPlaybackEngine *KisPart::playbackEngine() const 0525 { 0526 return d->playbackEngine.data(); 0527 } 0528 0529 void KisPart::prioritizeFrameForCache(KisImageSP image, int frame) { 0530 KisImageAnimationInterface* animInterface = image->animationInterface(); 0531 if ( animInterface && animInterface->documentPlaybackRange().contains(frame)) { 0532 d->animationCachePopulator.requestRegenerationWithPriorityFrame(image, frame); 0533 } 0534 } 0535 0536 void KisPart::openExistingFile(const QString &path) 0537 { 0538 // TODO: refactor out this method! 0539 0540 KisMainWindow *mw = currentMainwindow(); 0541 KIS_SAFE_ASSERT_RECOVER_RETURN(mw); 0542 0543 mw->openDocument(path, KisMainWindow::None); 0544 } 0545 0546 void KisPart::updateShortcuts() 0547 { 0548 // Update the UI actions. KisActionRegistry also takes care of updating 0549 // shortcut hints in tooltips. 0550 Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) { 0551 KisKActionCollection *ac = mainWindow->actionCollection(); 0552 0553 ac->updateShortcuts(); 0554 } 0555 } 0556 0557 void KisPart::openTemplate(const QUrl &url) 0558 { 0559 KisCursorOverrideLock cursorLock(Qt::BusyCursor); 0560 0561 KisDocument *document = createDocument(); 0562 0563 bool ok = document->loadNativeFormat(url.toLocalFile()); 0564 document->setModified(false); 0565 document->undoStack()->clear(); 0566 document->documentInfo()->resetMetaData(); 0567 0568 if (ok) { 0569 QString mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); 0570 // in case this is a open document template remove the -template from the end 0571 mimeType.remove( QRegExp( "-template$" ) ); 0572 document->setMimeTypeAfterLoading(mimeType); 0573 document->resetPath(); 0574 document->setReadWrite(true); 0575 } 0576 else { 0577 if (document->errorMessage().isEmpty()) { 0578 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1", document->localFilePath())); 0579 } 0580 else { 0581 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1\nReason: %2", document->localFilePath(), document->errorMessage())); 0582 } 0583 delete document; 0584 return; 0585 } 0586 QMap<QString, QString> dictionary; 0587 // XXX: fill the dictionary from the desktop file 0588 KisTranslateLayerNamesVisitor v(dictionary); 0589 document->image()->rootLayer()->accept(v); 0590 0591 addDocument(document); 0592 0593 KisMainWindow *mw = currentMainwindow(); 0594 mw->addViewAndNotifyLoadingCompleted(document); 0595 } 0596 0597 void KisPart::addRecentURLToAllMainWindows(QUrl url, QUrl oldUrl) 0598 { 0599 // Add entry to recent documents list 0600 // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) 0601 if (!url.isEmpty()) { 0602 bool ok = true; 0603 if (url.isLocalFile()) { 0604 QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0605 const QStringList tmpDirs = QStandardPaths::locateAll(QStandardPaths::TempLocation, "", QStandardPaths::LocateDirectory); 0606 for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) { 0607 if (path.contains(*it)) { 0608 ok = false; // it's in the tmp resource 0609 } 0610 } 0611 0612 const QStringList templateDirs = KoResourcePaths::findDirs("templates"); 0613 for (QStringList::ConstIterator it = templateDirs.begin() ; ok && it != templateDirs.end() ; ++it) { 0614 if (path.contains(*it)) { 0615 ok = false; // it's in the templates directory. 0616 break; 0617 } 0618 } 0619 } 0620 if (ok) { 0621 if (!oldUrl.isEmpty()) { 0622 KisRecentFilesManager::instance()->remove(oldUrl); 0623 } 0624 KisRecentFilesManager::instance()->add(url); 0625 } 0626 } 0627 } 0628 0629 void KisPart::queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl url, QUrl oldUrl) 0630 { 0631 d->pendingAddRecentUrlMap.insert(url, oldUrl); 0632 } 0633 0634 void KisPart::startCustomDocument(KisDocument* doc) 0635 { 0636 addDocument(doc); 0637 KisMainWindow *mw = currentMainwindow(); 0638 mw->addViewAndNotifyLoadingCompleted(doc); 0639 0640 } 0641 0642 KisInputManager* KisPart::currentInputManager() 0643 { 0644 KisMainWindow *mw = currentMainwindow(); 0645 KisViewManager *manager = mw ? mw->viewManager() : 0; 0646 return manager ? manager->inputManager() : 0; 0647 } 0648 0649 void KisPart::showSessionManager() 0650 { 0651 if (d->sessionManager.isNull()) { 0652 d->sessionManager.reset(new KisSessionManagerDialog()); 0653 } 0654 0655 d->sessionManager->show(); 0656 d->sessionManager->activateWindow(); 0657 } 0658 0659 void KisPart::startBlankSession() 0660 { 0661 KisMainWindow *window = createMainWindow(); 0662 window->initializeGeometry(); 0663 window->show(); 0664 0665 } 0666 0667 bool KisPart::restoreSession(const QString &sessionName) 0668 { 0669 if (sessionName.isNull()) return false; 0670 0671 KoResourceServer<KisSessionResource> *rserver = KisResourceServerProvider::instance()->sessionServer(); 0672 KisSessionResourceSP session = rserver->resource("", "", sessionName); 0673 if (!session || !session->valid()) return false; 0674 0675 return restoreSession(session); 0676 } 0677 0678 bool KisPart::restoreSession(KisSessionResourceSP session) 0679 { 0680 session->restore(); 0681 d->currentSession = session; 0682 return true; 0683 } 0684 0685 void KisPart::setCurrentSession(KisSessionResourceSP session) 0686 { 0687 d->currentSession = session; 0688 } 0689 0690 void KisPart::upgradeToPlaybackEngineMLT(KoCanvasBase* canvas) 0691 { 0692 #ifdef HAVE_MLT 0693 0694 // TODO: This is a slightly hacky workaround to loading the MLT engine over itself, 0695 // as the QT-based engine no longer supports audio. Is there a better way? 0696 if (d->playbackEngine->supportsAudio()) { 0697 return; 0698 } 0699 0700 setPlaybackEngine(new KisPlaybackEngineMLT(this)); 0701 0702 if (canvas) { 0703 d->playbackEngine->setObservedCanvas(canvas); 0704 } 0705 0706 #endif //HAVE_MLT 0707 } 0708 0709 void KisPart::setPlaybackEngine(KisPlaybackEngine *p_playbackEngine) 0710 { 0711 // make sure that the old engine is still alive until the end 0712 // of the emitted signal 0713 QScopedPointer backup(p_playbackEngine); 0714 d->playbackEngine.swap(backup); 0715 0716 emit playbackEngineChanged(p_playbackEngine); 0717 } 0718 0719 #include "moc_KisPart.cpp"