bool GuiAppInstance::findAndTryLoadUntitledAutoSave() { if ( !appPTR->getCurrentSettings()->isAutoSaveEnabledForUnsavedProjects() ) { return false; } QDir savesDir( Project::autoSavesDir() ); QStringList entries = savesDir.entryList(QDir::Files | QDir::NoDotAndDotDot); QStringList foundAutosaves; for (int i = 0; i < entries.size(); ++i) { const QString & entry = entries.at(i); QString searchStr( QLatin1Char('.') ); searchStr.append( QString::fromUtf8(NATRON_PROJECT_FILE_EXT) ); searchStr.append( QString::fromUtf8(".autosave") ); int suffixPos = entry.indexOf(searchStr); if ( (suffixPos == -1) || entry.contains( QString::fromUtf8("RENDER_SAVE") ) ) { continue; } foundAutosaves << entry; } if ( foundAutosaves.empty() ) { return false; } QString text = tr("An auto-saved project was found with no associated project file.\n" "Would you like to restore it? Clicking No will remove this auto-save."); appPTR->hideSplashScreen(); StandardButtonEnum ret = Dialogs::questionDialog(tr("Auto-save").toStdString(), text.toStdString(), false, StandardButtons(eStandardButtonYes | eStandardButtonNo), eStandardButtonYes); if ( (ret == eStandardButtonNo) || (ret == eStandardButtonEscape) ) { Project::clearAutoSavesDir(); return false; } for (int i = 0; i < foundAutosaves.size(); ++i) { const QString& autoSaveFileName = foundAutosaves[i]; if (i == 0) { //Load the first one into the current instance of Natron, then open-up new instances if ( !getProject()->loadProject(savesDir.path() + QLatin1Char('/'), autoSaveFileName, true) ) { return false; } } else { CLArgs cl; AppInstance* newApp = appPTR->newAppInstance(cl, false); if ( !newApp->getProject()->loadProject(savesDir.path() + QLatin1Char('/'), autoSaveFileName, true) ) { return false; } } } return true; } // findAndTryLoadAutoSave
int Gui::saveWarning() { if ( !getApp()->getProject()->isSaveUpToDate() ) { StandardButtonEnum ret = Dialogs::questionDialog(NATRON_APPLICATION_NAME, tr("Save changes to %1?").arg( getApp()->getProject()->getProjectFilename() ).toStdString(), false, StandardButtons(eStandardButtonSave | eStandardButtonDiscard | eStandardButtonCancel), eStandardButtonSave); if ( (ret == eStandardButtonEscape) || (ret == eStandardButtonCancel) ) { return 2; } else if (ret == eStandardButtonDiscard) { return 1; } else { return 0; } } return -1; }
void GuiAppInstance::load(const CLArgs& cl, bool makeEmptyInstance) { if (getAppID() == 0) { appPTR->setLoadingStatus( QObject::tr("Creating user interface...") ); } try { declareCurrentAppVariable_Python(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } _imp->_gui = new Gui(this); _imp->_gui->createGui(); printAutoDeclaredVariable(_imp->declareAppAndParamsString); ///if the app is interactive, build the plugins toolbuttons from the groups we extracted off the plugins. const std::list<boost::shared_ptr<PluginGroupNode> > & _toolButtons = appPTR->getTopLevelPluginsToolButtons(); for (std::list<boost::shared_ptr<PluginGroupNode> >::const_iterator it = _toolButtons.begin(); it != _toolButtons.end(); ++it) { _imp->findOrCreateToolButtonRecursive(*it); } _imp->_gui->sortAllPluginsToolButtons(); Q_EMIT pluginsPopulated(); ///show the gui _imp->_gui->show(); boost::shared_ptr<Settings> nSettings = appPTR->getCurrentSettings(); QObject::connect( getProject().get(), SIGNAL(formatChanged(Format)), this, SLOT(projectFormatChanged(Format)) ); { QSettings settings( QString::fromUtf8(NATRON_ORGANIZATION_NAME), QString::fromUtf8(NATRON_APPLICATION_NAME) ); if ( !settings.contains( QString::fromUtf8("checkForUpdates") ) ) { StandardButtonEnum reply = Dialogs::questionDialog(tr("Updates").toStdString(), tr("Do you want " NATRON_APPLICATION_NAME " to check for updates " "on launch of the application ?").toStdString(), false); bool checkForUpdates = reply == eStandardButtonYes; nSettings->setCheckUpdatesEnabled(checkForUpdates); } if ( nSettings->isCheckForUpdatesEnabled() ) { appPTR->setLoadingStatus( tr("Checking if updates are available...") ); checkForNewVersion(); } } if ( nSettings->isDefaultAppearanceOutdated() ) { StandardButtonEnum reply = Dialogs::questionDialog(tr("Appearance").toStdString(), tr(NATRON_APPLICATION_NAME " default appearance changed since last version.\n" "Would you like to set the new default appearance?").toStdString(), false); if (reply == eStandardButtonYes) { nSettings->restoreDefaultAppearance(); } } /// Create auto-save dir if it does not exists QDir dir = Project::autoSavesDir(); dir.mkpath( QString::fromUtf8(".") ); if (getAppID() == 0) { appPTR->getCurrentSettings()->doOCIOStartupCheckIfNeeded(); if ( !appPTR->isShorcutVersionUpToDate() ) { StandardButtonEnum reply = questionDialog(tr("Shortcuts").toStdString(), tr("Default shortcuts for " NATRON_APPLICATION_NAME " have changed, " "would you like to set them to their defaults ? " "Clicking no will keep the old shortcuts hence if a new shortcut has been " "set to something else than an empty shortcut you won't benefit of it.").toStdString(), false, StandardButtons(eStandardButtonYes | eStandardButtonNo), eStandardButtonNo); if (reply == eStandardButtonYes) { appPTR->restoreDefaultShortcuts(); } } } if (makeEmptyInstance) { return; } /// If this is the first instance of the software, try to load an autosave if ( (getAppID() == 0) && cl.getScriptFilename().isEmpty() ) { if ( findAndTryLoadUntitledAutoSave() ) { ///if we successfully loaded an autosave ignore the specified project in the launch args. return; } } QFileInfo info( cl.getScriptFilename() ); if ( cl.getScriptFilename().isEmpty() || !info.exists() ) { getProject()->createViewer(); execOnProjectCreatedCallback(); const QString& imageFile = cl.getImageFilename(); if ( !imageFile.isEmpty() ) { handleFileOpenEvent( imageFile.toStdString() ); } } else { if ( info.suffix() == QString::fromUtf8("py") ) { appPTR->setLoadingStatus( tr("Loading script: ") + cl.getScriptFilename() ); ///If this is a Python script, execute it loadPythonScript(info); execOnProjectCreatedCallback(); } else if ( info.suffix() == QString::fromUtf8(NATRON_PROJECT_FILE_EXT) ) { ///Otherwise just load the project specified. QString name = info.fileName(); QString path = info.path(); Global::ensureLastPathSeparator(path); appPTR->setLoadingStatus(tr("Loading project: ") + path + name); getProject()->loadProject(path, name); ///remove any file open event that might have occured appPTR->setFileToOpen( QString() ); } else { Dialogs::errorDialog( tr("Invalid file").toStdString(), tr(NATRON_APPLICATION_NAME " only accepts python scripts or .ntp project files").toStdString() ); execOnProjectCreatedCallback(); } } const QString& extraOnProjectCreatedScript = cl.getDefaultOnProjectLoadedScript(); if ( !extraOnProjectCreatedScript.isEmpty() ) { QFileInfo cbInfo(extraOnProjectCreatedScript); if ( cbInfo.exists() ) { loadPythonScript(cbInfo); } } } // load
void OutputEffectInstance::renderFullSequence(bool isBlocking, bool enableRenderStats, BlockingBackgroundRender* renderController, int first, int last, int frameStep) { int viewsCount = getApp()->getProject()->getProjectViewsCount(); const ViewIdx mainView(0); std::vector<ViewIdx> viewsToRender(viewsCount); for (int i = 0; i < viewsCount; ++i) { viewsToRender[i] = ViewIdx(i); } ///The effect is sequential (e.g: WriteFFMPEG), and thus cannot render multiple views, we have to choose one ///We pick the user defined main view in the project settings SequentialPreferenceEnum sequentiallity = getSequentialPreference(); bool canOnlyHandleOneView = sequentiallity == eSequentialPreferenceOnlySequential || sequentiallity == eSequentialPreferencePreferSequential; if (canOnlyHandleOneView) { viewsToRender.clear(); viewsToRender.push_back(mainView); } if ( isViewAware() ) { //If the Writer is view aware, check if it wants to render all views at once or not KnobPtr outputFileNameKnob = getKnobByName(kOfxImageEffectFileParamName); if (outputFileNameKnob) { KnobOutputFile* outputFileName = dynamic_cast<KnobOutputFile*>( outputFileNameKnob.get() ); assert(outputFileName); if (outputFileName) { std::string pattern = outputFileName->getValue(); std::size_t foundViewPattern = pattern.find_first_of("%v"); if (foundViewPattern == std::string::npos) { foundViewPattern = pattern.find_first_of("%V"); } if (foundViewPattern == std::string::npos) { ///No view pattern ///all views will be overwritten to the same file ///If this is WriteOIIO, check the parameter "viewsSelector" to determine if the user wants to encode all ///views to a single file or not KnobPtr viewsKnob = getKnobByName(kWriteOIIOParamViewsSelector); bool hasViewChoice = false; if ( viewsKnob && !viewsKnob->getIsSecret() ) { KnobChoice* viewsChoice = dynamic_cast<KnobChoice*>( viewsKnob.get() ); if (viewsChoice) { hasViewChoice = true; int viewChoice_i = viewsChoice->getValue(); if (viewChoice_i == 0) { // the "All" choice viewsToRender.clear(); // note: if the plugin renders all views to a single file, then rendering view 0 will do the job. viewsToRender.push_back( ViewIdx(0) ); } else { //The user has specified a view viewsToRender.clear(); assert(viewChoice_i >= 1); viewsToRender.push_back( ViewIdx(viewChoice_i - 1) ); } } } if (!hasViewChoice) { if (viewsToRender.size() > 1) { std::string mainViewName; const std::vector<std::string>& viewNames = getApp()->getProject()->getProjectViewNames(); if ( mainView < (int)viewNames.size() ) { mainViewName = viewNames[mainView]; } QString message = tr("%1 does not support multi-view, only the view %2 will be rendered.") .arg( QString::fromUtf8( getNode()->getLabel_mt_safe().c_str() ) ) .arg( QString::fromUtf8( mainViewName.c_str() ) ); if (!renderController) { message.append( QChar::fromLatin1('\n') ); message.append( QString::fromUtf8("You can use the %v or %V indicator in the filename to render to separate files.\n" "Would you like to continue?") ); StandardButtonEnum rep = Dialogs::questionDialog(tr("Multi-view support").toStdString(), message.toStdString(), false, StandardButtons(eStandardButtonOk | eStandardButtonCancel), eStandardButtonOk); if (rep != eStandardButtonOk) { return; } } else { Dialogs::warningDialog( tr("Multi-view support").toStdString(), message.toStdString() ); } } //Render the main-view only... viewsToRender.clear(); viewsToRender.push_back(mainView); } } else { ///The user wants to write each view into a separate file ///This will disregard the content of kWriteOIIOParamViewsSelector and the Writer ///should write one view per-file. } } } } else { // !isViewAware if (viewsToRender.size() > 1) { std::string mainViewName; const std::vector<std::string>& viewNames = getApp()->getProject()->getProjectViewNames(); if ( mainView < (int)viewNames.size() ) { mainViewName = viewNames[mainView]; } QString message = tr("%1 does not support multi-view, only the view %2 will be rendered.") .arg( QString::fromUtf8( getNode()->getLabel_mt_safe().c_str() ) ) .arg( QString::fromUtf8( mainViewName.c_str() ) ); if (!renderController) { message.append( QChar::fromLatin1('\n') ); message.append( QString::fromUtf8("You can use the %v or %V indicator in the filename to render to separate files.\n" "Would you like to continue?") ); StandardButtonEnum rep = Dialogs::questionDialog(tr("Multi-view support").toStdString(), message.toStdString(), false, StandardButtons(eStandardButtonOk | eStandardButtonCancel), eStandardButtonOk); if (rep != eStandardButtonOk) { return; } } else { Dialogs::warningDialog( tr("Multi-view support").toStdString(), message.toStdString() ); } } } RenderSequenceArgs args; { QMutexLocker k(&_outputEffectDataLock); args.firstFrame = first; args.lastFrame = last; args.frameStep = frameStep; args.renderController = renderController; args.useStats = enableRenderStats; args.blocking = isBlocking; args.viewsToRender = viewsToRender; _renderSequenceRequests.push_back(args); if (_renderSequenceRequests.size() > 1) { //The node is already rendering a sequence, queue it and dequeue it in notifyRenderFinished() return; } } launchRenderSequence(args); } // OutputEffectInstance::renderFullSequence