222 lines
8.4 KiB
C++
222 lines
8.4 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2015 Alexander Golubev (Fat-Zer) <fatzer2@gmail.com> *
|
|
* *
|
|
* 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 <vector>
|
|
# include <list>
|
|
# include <set>
|
|
# include <boost/bind.hpp>
|
|
# include <QMessageBox>
|
|
# include <QPushButton>
|
|
#endif
|
|
|
|
#include <Base/Exception.h>
|
|
#include <App/Application.h>
|
|
#include <App/Document.h>
|
|
#include <Gui/MainWindow.h>
|
|
#include <Gui/Command.h>
|
|
#include <Gui/Application.h>
|
|
#include <Mod/PartDesign/App/Body.h>
|
|
#include <Mod/PartDesign/App/Feature.h>
|
|
#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;
|
|
}
|
|
}
|
|
}
|