/*************************************************************************** * Copyright (C) 2015 Alexander Golubev (Fat-Zer) * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ # include # include # include # include # include # include #endif #include #include #include #include #include #include #include #include #include "WorkflowManager.h" using namespace PartDesignGui; WorkflowManager * WorkflowManager::_instance = nullptr; WorkflowManager::WorkflowManager() { // Fill the map with already opened documents for ( auto doc : App::GetApplication().getDocuments() ) { slotFinishRestoreDocument ( *doc ); } connectNewDocument = App::GetApplication().signalNewDocument.connect( boost::bind( &WorkflowManager::slotNewDocument, this, _1 ) ); connectFinishRestoreDocument = App::GetApplication().signalFinishRestoreDocument.connect( boost::bind( &WorkflowManager::slotFinishRestoreDocument, this, _1 ) ); connectDeleteDocument = App::GetApplication().signalDeleteDocument.connect( boost::bind( &WorkflowManager::slotDeleteDocument, this, _1 ) ); } WorkflowManager::~WorkflowManager() { // they won't be automatically disconnected on destruction! connectNewDocument.disconnect (); connectFinishRestoreDocument.disconnect (); connectDeleteDocument.disconnect (); } // Those destruction/construction is not really needed and could be done in the instance() // but to make things a bit more clear, better to keep them around. void WorkflowManager::init() { if (!_instance) { _instance = new WorkflowManager(); } else { //throw Base::RuntimeError( "Trying to init the workflow manager second time." ); } } WorkflowManager *WorkflowManager::instance() { if (!_instance) { WorkflowManager::init(); } return _instance; } void WorkflowManager::destruct() { if (_instance) { delete _instance; _instance = nullptr; } } void WorkflowManager::slotNewDocument( const App::Document &doc ) { // new document always uses new workflow dwMap[&doc] = Workflow::Modern; } void WorkflowManager::slotFinishRestoreDocument( const App::Document &doc ) { Workflow wf = guessWorkflow (&doc); // Mark document as undetermined if the guessed workflow is not new if( wf != Workflow::Modern ) { wf = Workflow::Undetermined; } dwMap[&doc] = wf; } void WorkflowManager::slotDeleteDocument( const App::Document &doc ) { dwMap.erase(&doc); } Workflow WorkflowManager::getWorkflowForDocument( App::Document *doc) { assert (doc); auto it = dwMap.find(doc); if ( it!=dwMap.end() ) { return it->second; } else { // We haven't yet checked the file workflow // May happen if e.g. file not completely loaded yet return Workflow::Undetermined; } } Workflow WorkflowManager::determineWorkflow(App::Document *doc) { Workflow rv = getWorkflowForDocument (doc); if (rv != Workflow::Undetermined) { // Return if workflow is known return rv; } // Guess the workflow again rv = guessWorkflow (doc); if (rv != Workflow::Modern) { QMessageBox msgBox(Gui::getMainWindow()); if ( rv == Workflow::Legacy ) { // legacy messages msgBox.setText( QObject::tr( "The document \"%1\" you are editing was designed with an old version of " "PartDesign workbench." ).arg( QString::fromStdString ( doc->getName()) ) ); msgBox.setInformativeText ( QObject::tr( "Do you want to migrate in order to use modern PartDesign features?" ) ); } else { // The document is already in the middle of migration msgBox.setText( QObject::tr( "The document \"%1\" seems to be either in the middle of" " the migration process from legacy PartDesign or have a slightly broken structure." ).arg( QString::fromStdString ( doc->getName()) ) ); msgBox.setInformativeText ( QObject::tr( "Do you want to make the migration automatically?" ) ); } msgBox.setDetailedText( QObject::tr( "Note: If you choose to migrate you won't be able to edit" " the file with an older FreeCAD version.\n" "If you refuse to migrate you won't be able to use new PartDesign features" " like Bodies and Parts. As a result you also won't be able to use your parts" " in the assembly workbench.\n" "Although you will be able to migrate any moment later with 'Part Design -> Migrate'." ) ); msgBox.setIcon( QMessageBox::Question ); QPushButton * yesBtn = msgBox.addButton ( QMessageBox::Yes ); QPushButton * manuallyBtn = msgBox.addButton ( QObject::tr ( "Migrate manually" ), QMessageBox::YesRole ); // If it is already a document in the middle of the migration the user shouldn't refuse to migrate if ( rv != Workflow::Undetermined ) { msgBox.addButton ( QMessageBox::No ); } msgBox.setDefaultButton ( yesBtn ); // TODO: Add some description of manual migration mode (2015-08-09, Fat-Zer) msgBox.exec(); if ( msgBox.clickedButton() == yesBtn ) { Gui::Application::Instance->commandManager().runCommandByName("PartDesign_Migrate"); rv = Workflow::Modern; } else if ( msgBox.clickedButton() == manuallyBtn ) { rv = Workflow::Undetermined; } else { rv = Workflow::Legacy; } } // Actually set the result in our map dwMap[ doc ] = rv; return rv; } void WorkflowManager::forceWorkflow(const App::Document *doc, Workflow wf) { dwMap[ doc ] = wf; } Workflow WorkflowManager::guessWorkflow(const App::Document *doc) { // Retrieve bodies of the document auto features = doc->getObjectsOfType( PartDesign::Feature::getClassTypeId() ); if( features.empty() ) { // a new file should be done in the new workflow return Workflow::Modern; } else { auto bodies = doc->getObjectsOfType( PartDesign::Body::getClassTypeId() ); if (bodies.empty()) { // If there are no bodies workflow is legacy return Workflow::Legacy; } else { bool features_without_bodies = false; for( auto feat: features ) { if( !PartDesign::Body::findBodyOf( feat ) ) { features_without_bodies = true; break; } } // if there are features not belonging to any body it means that migration was incomplete, otherwise it's Modern return features_without_bodies ? Workflow::Undetermined : Workflow::Modern; } } }