Gui: Application/Document/MainWindow changes following App namespace
Application: * signalNewDocument, check the extra argument, isMainDoc, the decide whether to create view of the new document. This is so that external linked document can be opened in background without crowding the tab list. * slotDeleteDocument, calls Document::beforeDelete() * slotActiveDocument, creates view if none, because external document is now opened without view. * onLastWindowClosed(), switch to next active document, and creates view if none. * send(Has)MsgToFocusView(), new API to send message to the active view in focus. This is to solve the ambiguity of things like pressing delete key, copy, paste handling when the active new is not in focus. For example, when spread sheet view is active, delete/copy/paste handling should be different when the focus on the spread sheet view or focus on tree view. * tryClose(), delegate to MainWindow for close confirmation * reopen(), new API to reload a partial document in full Document/DocumentP: * _CoinMap, new internal map for quick access view provider from its root node. * slotNewObject, modified to support view provider override from App::FeaturePython, through new API DocumentObject::getViewProviderNameOverride(). * slotDeletedObject/slotTransactionRemove, improve handling of geo group children rebuild * slotSkipRecompute, add special handling of document with skip recompute. Some command cannot work when skip recompute is active. For example, sketcher command will check if recompute is successful on many commands, and will undo if not. New 'PartialCompute' flag is added to allow recompute only the editing object and all its dependencies if 'SkipRecompute' is active. * slotTouchedObject, new signal handling of manually touched object. * setModified(), do nothing if already modified. This is a critical performance improvement, because marking tab window as modified turns out to be a relatively expensive operation, and will cause massive slow down if calling it on every property change. * getViewProviderByPathFromHead/getViewProvidersByPath(), new APIs to obtain view provider(s) from coin SoPath. * save/saveAll/saveCopy, modified to support external document saving. * Save/RestoreDocFile(), save and restore tree item recursive expansion status. * slotFinishRestoreObject(), handle new signal signalFinishRestoreObject(), unifies postprocessing in restore and import operation. * createView/setActiveView(), add support of delayed view creations * canClose(), delegate to MainWindows to ask for confirmation * undo/redo(), support grouped transaction undo/redo. Transactions may be grouped by the same transaction ID if they are triggered by a single operation but involves objects from multiple documents. * toggleInSceneGraph(), new API to add or remove root node from or to scenegraph without deleting the view object. MainWindow: * Update command status using a single shot timer instead of periodical one. * Improve message display is status bar. Give error and warning message higher priority (using QStatusBar::showMessage()) than normal status message (using actionLabel), reversed from original implementation. * confirmSave(), new API to check for modification, and ask user to save the document before closing. The confirmation dialog allows user to apply the answer to all document for convenience. * saveAll(), new API to save all document with correct ordering in case of external linking. * createMimeDataFromSelection/insertFromMimeData(), support copy paste object with external linking. A new dialog DlgObjectSelection is used to let user select exactly which object to export. CommandDoc/CommandWindow: * Related changes to object delete, document import, export, and save.
This commit is contained in:
@@ -288,11 +288,12 @@ Application::Application(bool GUIenabled)
|
||||
{
|
||||
//App::GetApplication().Attach(this);
|
||||
if (GUIenabled) {
|
||||
App::GetApplication().signalNewDocument.connect(boost::bind(&Gui::Application::slotNewDocument, this, _1));
|
||||
App::GetApplication().signalNewDocument.connect(boost::bind(&Gui::Application::slotNewDocument, this, _1, _2));
|
||||
App::GetApplication().signalDeleteDocument.connect(boost::bind(&Gui::Application::slotDeleteDocument, this, _1));
|
||||
App::GetApplication().signalRenameDocument.connect(boost::bind(&Gui::Application::slotRenameDocument, this, _1));
|
||||
App::GetApplication().signalActiveDocument.connect(boost::bind(&Gui::Application::slotActiveDocument, this, _1));
|
||||
App::GetApplication().signalRelabelDocument.connect(boost::bind(&Gui::Application::slotRelabelDocument, this, _1));
|
||||
App::GetApplication().signalShowHidden.connect(boost::bind(&Gui::Application::slotShowHidden, this, _1));
|
||||
|
||||
|
||||
// install the last active language
|
||||
@@ -669,7 +670,7 @@ void Application::createStandardOperations()
|
||||
Gui::CreateTestCommands();
|
||||
}
|
||||
|
||||
void Application::slotNewDocument(const App::Document& Doc)
|
||||
void Application::slotNewDocument(const App::Document& Doc, bool isMainDoc)
|
||||
{
|
||||
#ifdef FC_DEBUG
|
||||
std::map<const App::Document*, Gui::Document*>::const_iterator it = d->documents.find(&Doc);
|
||||
@@ -687,12 +688,13 @@ void Application::slotNewDocument(const App::Document& Doc)
|
||||
pDoc->signalInEdit.connect(boost::bind(&Gui::Application::slotInEdit, this, _1));
|
||||
pDoc->signalResetEdit.connect(boost::bind(&Gui::Application::slotResetEdit, this, _1));
|
||||
|
||||
signalNewDocument(*pDoc);
|
||||
pDoc->createView(View3DInventor::getClassTypeId());
|
||||
signalNewDocument(*pDoc, isMainDoc);
|
||||
if(isMainDoc)
|
||||
pDoc->createView(View3DInventor::getClassTypeId());
|
||||
// FIXME: Do we really need this further? Calling processEvents() mixes up order of execution in an
|
||||
// unpredicatable way. At least it seems that with Qt5 we don't need this any more.
|
||||
#if QT_VERSION < 0x050000
|
||||
qApp->processEvents(); // make sure to show the window stuff on the right place
|
||||
// qApp->processEvents(); // make sure to show the window stuff on the right place
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -704,11 +706,15 @@ void Application::slotDeleteDocument(const App::Document& Doc)
|
||||
return;
|
||||
}
|
||||
|
||||
// We must clear the selection here to notify all observers
|
||||
Gui::Selection().clearSelection(doc->second->getDocument()->getName());
|
||||
// We must clear the selection here to notify all observers.
|
||||
// And because of possible cross document link, better clear all selection
|
||||
// to be safe
|
||||
Gui::Selection().clearCompleteSelection();
|
||||
doc->second->signalDeleteDocument(*doc->second);
|
||||
signalDeleteDocument(*doc->second);
|
||||
|
||||
doc->second->beforeDelete();
|
||||
|
||||
// If the active document gets destructed we must set it to 0. If there are further existing documents then the
|
||||
// view that becomes active sets the active document again. So, we needn't worry about this.
|
||||
if (d->activeDocument == doc->second)
|
||||
@@ -740,6 +746,16 @@ void Application::slotRenameDocument(const App::Document& Doc)
|
||||
signalRenameDocument(*doc->second);
|
||||
}
|
||||
|
||||
void Application::slotShowHidden(const App::Document& Doc)
|
||||
{
|
||||
std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
|
||||
#ifdef FC_DEBUG
|
||||
assert(doc!=d->documents.end());
|
||||
#endif
|
||||
|
||||
signalShowHidden(*doc->second);
|
||||
}
|
||||
|
||||
void Application::slotActiveDocument(const App::Document& Doc)
|
||||
{
|
||||
std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
|
||||
@@ -753,6 +769,12 @@ void Application::slotActiveDocument(const App::Document& Doc)
|
||||
Base::PyGILStateLocker lock;
|
||||
Py::Object active(d->activeDocument->getPyObject(), true);
|
||||
Py::Module("FreeCADGui").setAttr(std::string("ActiveDocument"),active);
|
||||
|
||||
auto view = getMainWindow()->activeWindow();
|
||||
if(!view || view->getAppDocument()!=&Doc) {
|
||||
Gui::MDIView* view = d->activeDocument->getActiveView();
|
||||
getMainWindow()->setActiveWindow(view);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Base::PyGILStateLocker lock;
|
||||
@@ -760,6 +782,7 @@ void Application::slotActiveDocument(const App::Document& Doc)
|
||||
}
|
||||
}
|
||||
signalActiveDocument(*doc->second);
|
||||
getMainWindow()->updateActions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -776,6 +799,7 @@ void Application::slotDeletedObject(const ViewProvider& vp)
|
||||
void Application::slotChangedObject(const ViewProvider& vp, const App::Property& prop)
|
||||
{
|
||||
this->signalChangedObject(vp,prop);
|
||||
getMainWindow()->updateActions(true);
|
||||
}
|
||||
|
||||
void Application::slotRelabelObject(const ViewProvider& vp)
|
||||
@@ -786,6 +810,7 @@ void Application::slotRelabelObject(const ViewProvider& vp)
|
||||
void Application::slotActivatedObject(const ViewProvider& vp)
|
||||
{
|
||||
this->signalActivatedObject(vp);
|
||||
getMainWindow()->updateActions();
|
||||
}
|
||||
|
||||
void Application::slotInEdit(const Gui::ViewProviderDocumentObject& vp)
|
||||
@@ -804,6 +829,19 @@ void Application::onLastWindowClosed(Gui::Document* pcDoc)
|
||||
try {
|
||||
// Call the closing mechanism from Python. This also checks whether pcDoc is the last open document.
|
||||
Command::doCommand(Command::Doc, "App.closeDocument(\"%s\")", pcDoc->getDocument()->getName());
|
||||
if (!d->activeDocument && d->documents.size()) {
|
||||
for(auto &v : d->documents) {
|
||||
Gui::MDIView* view = v.second->getActiveView();
|
||||
if(view) {
|
||||
setActiveDocument(v.second);
|
||||
getMainWindow()->setActiveWindow(view);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto gdoc = d->documents.begin()->second;
|
||||
setActiveDocument(gdoc);
|
||||
activateView(View3DInventor::getClassTypeId(),true);
|
||||
}
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
e.ReportException();
|
||||
@@ -828,6 +866,31 @@ bool Application::sendHasMsgToActiveView(const char* pMsg)
|
||||
return pView ? pView->onHasMsg(pMsg) : false;
|
||||
}
|
||||
|
||||
/// send Messages to the active view
|
||||
bool Application::sendMsgToFocusView(const char* pMsg, const char** ppReturn)
|
||||
{
|
||||
MDIView* pView = getMainWindow()->activeWindow();
|
||||
if(!pView)
|
||||
return false;
|
||||
for(auto focus=qApp->focusWidget();focus;focus=focus->parentWidget()) {
|
||||
if(focus == pView)
|
||||
return pView->onMsg(pMsg,ppReturn);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Application::sendHasMsgToFocusView(const char* pMsg)
|
||||
{
|
||||
MDIView* pView = getMainWindow()->activeWindow();
|
||||
if(!pView)
|
||||
return false;
|
||||
for(auto focus=qApp->focusWidget();focus;focus=focus->parentWidget()) {
|
||||
if(focus == pView)
|
||||
return pView->onHasMsg(pMsg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Gui::MDIView* Application::activeView(void) const
|
||||
{
|
||||
if (activeDocument())
|
||||
@@ -1040,24 +1103,9 @@ void Application::updateActive(void)
|
||||
|
||||
void Application::tryClose(QCloseEvent * e)
|
||||
{
|
||||
if (d->documents.size() == 0) {
|
||||
e->accept();
|
||||
}
|
||||
else {
|
||||
// ask all documents if closable
|
||||
std::map<const App::Document*, Gui::Document*>::iterator It;
|
||||
for (It = d->documents.begin();It!=d->documents.end();++It) {
|
||||
// a document may have several views attached, so ask it directly
|
||||
#if 0
|
||||
MDIView* active = It->second->getActiveView();
|
||||
e->setAccepted(active->canClose());
|
||||
#else
|
||||
e->setAccepted(It->second->canClose());
|
||||
#endif
|
||||
if (!e->isAccepted())
|
||||
return;
|
||||
}
|
||||
}
|
||||
e->setAccepted(getMainWindow()->closeAllDocuments(false));
|
||||
if(!e->isAccepted())
|
||||
return;
|
||||
|
||||
// ask all passive views if closable
|
||||
for (std::list<Gui::BaseView*>::iterator It = d->passive.begin();It!=d->passive.end();++It) {
|
||||
@@ -1079,14 +1127,7 @@ void Application::tryClose(QCloseEvent * e)
|
||||
itp = d->passive.begin();
|
||||
}
|
||||
|
||||
// remove all documents
|
||||
size_t cnt = d->documents.size();
|
||||
while (d->documents.size() > 0 && cnt > 0) {
|
||||
// destroys also the Gui document
|
||||
It = d->documents.begin();
|
||||
App::GetApplication().closeDocument(It->second->getDocument()->getName());
|
||||
--cnt; // avoid infinite loop
|
||||
}
|
||||
App::GetApplication().closeAllDocuments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1680,6 +1721,14 @@ void Application::runApplication(void)
|
||||
|
||||
// A new QApplication
|
||||
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
|
||||
|
||||
#if defined(QTWEBENGINE) && defined(Q_OS_LINUX)
|
||||
// Avoid warning of 'Qt WebEngine seems to be initialized from a plugin...'
|
||||
// QTWEBENGINE is defined in src/Gui/CMakeLists.txt, currently only enabled
|
||||
// when build with Conda.
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
#endif
|
||||
|
||||
// if application not yet created by the splasher
|
||||
int argc = App::Application::GetARGC();
|
||||
GUISingleApplication mainApp(argc, App::Application::GetARGV());
|
||||
@@ -2144,3 +2193,61 @@ void Application::checkForPreviousCrashes()
|
||||
dlg.exec();
|
||||
}
|
||||
}
|
||||
|
||||
App::Document *Application::reopen(App::Document *doc) {
|
||||
if(!doc) return 0;
|
||||
std::string name = doc->FileName.getValue();
|
||||
std::set<const Gui::Document*> untouchedDocs;
|
||||
for(auto &v : d->documents) {
|
||||
if(!v.second->isModified() && !v.second->getDocument()->isTouched())
|
||||
untouchedDocs.insert(v.second);
|
||||
}
|
||||
|
||||
WaitCursor wc;
|
||||
wc.setIgnoreEvents(WaitCursor::NoEvents);
|
||||
|
||||
if(doc->testStatus(App::Document::PartialDoc)
|
||||
|| doc->testStatus(App::Document::PartialRestore))
|
||||
{
|
||||
App::GetApplication().openDocument(name.c_str());
|
||||
} else {
|
||||
std::vector<std::string> docs;
|
||||
for(auto d : doc->getDependentDocuments(true)) {
|
||||
if(d->testStatus(App::Document::PartialDoc)
|
||||
|| d->testStatus(App::Document::PartialRestore) )
|
||||
docs.push_back(d->FileName.getValue());
|
||||
}
|
||||
for(auto &file : docs)
|
||||
App::GetApplication().openDocument(file.c_str(),false);
|
||||
}
|
||||
|
||||
doc = 0;
|
||||
for(auto &v : d->documents) {
|
||||
if(name == v.first->FileName.getValue())
|
||||
doc = const_cast<App::Document*>(v.first);
|
||||
if(untouchedDocs.count(v.second)) {
|
||||
if(!v.second->isModified()) continue;
|
||||
bool reset = true;
|
||||
for(auto obj : v.second->getDocument()->getObjects()) {
|
||||
if(!obj->isTouched())
|
||||
continue;
|
||||
std::vector<App::Property*> props;
|
||||
obj->getPropertyList(props);
|
||||
for(auto prop : props){
|
||||
auto link = dynamic_cast<App::PropertyLinkBase*>(prop);
|
||||
if(link && link->checkRestore()) {
|
||||
reset = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!reset)
|
||||
break;
|
||||
}
|
||||
if(reset) {
|
||||
v.second->getDocument()->purgeTouched();
|
||||
v.second->setModified(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <App/Application.h>
|
||||
|
||||
class QCloseEvent;
|
||||
class SoNode;
|
||||
|
||||
namespace Gui{
|
||||
class BaseView;
|
||||
@@ -65,6 +66,8 @@ public:
|
||||
void importFrom(const char* FileName, const char* DocName, const char* Module);
|
||||
/// Export objects from the document DocName to a single file
|
||||
void exportTo(const char* FileName, const char* DocName, const char* Module);
|
||||
/// Reload a partial opened document
|
||||
App::Document *reopen(App::Document *doc);
|
||||
//@}
|
||||
|
||||
|
||||
@@ -74,6 +77,10 @@ public:
|
||||
bool sendMsgToActiveView(const char* pMsg, const char** ppReturn=0);
|
||||
/// send Messages test to the active view
|
||||
bool sendHasMsgToActiveView(const char* pMsg);
|
||||
/// send Messages to the focused view
|
||||
bool sendMsgToFocusView(const char* pMsg, const char** ppReturn=0);
|
||||
/// send Messages test to the focused view
|
||||
bool sendHasMsgToFocusView(const char* pMsg);
|
||||
/// Attach a view (get called by the FCView constructor)
|
||||
void attachView(Gui::BaseView* pcView);
|
||||
/// Detach a view (get called by the FCView destructor)
|
||||
@@ -89,7 +96,7 @@ public:
|
||||
/** @name Signals of the Application */
|
||||
//@{
|
||||
/// signal on new Document
|
||||
boost::signals2::signal<void (const Gui::Document&)> signalNewDocument;
|
||||
boost::signals2::signal<void (const Gui::Document&, bool)> signalNewDocument;
|
||||
/// signal on deleted Document
|
||||
boost::signals2::signal<void (const Gui::Document&)> signalDeleteDocument;
|
||||
/// signal on relabeling Document
|
||||
@@ -114,6 +121,8 @@ public:
|
||||
boost::signals2::signal<void (const char*)> signalAddWorkbench;
|
||||
/// signal on removed workbench
|
||||
boost::signals2::signal<void (const char*)> signalRemoveWorkbench;
|
||||
/// signal on show hidden items
|
||||
boost::signals2::signal<void (const Gui::Document&)> signalShowHidden;
|
||||
/// signal on activating view
|
||||
boost::signals2::signal<void (const Gui::MDIView*)> signalActivateView;
|
||||
/// signal on entering in edit mode
|
||||
@@ -126,11 +135,12 @@ public:
|
||||
//@{
|
||||
protected:
|
||||
/// Observer message from the Application
|
||||
void slotNewDocument(const App::Document&);
|
||||
void slotNewDocument(const App::Document&,bool);
|
||||
void slotDeleteDocument(const App::Document&);
|
||||
void slotRelabelDocument(const App::Document&);
|
||||
void slotRenameDocument(const App::Document&);
|
||||
void slotActiveDocument(const App::Document&);
|
||||
void slotShowHidden(const App::Document&);
|
||||
void slotNewObject(const ViewProvider&);
|
||||
void slotDeletedObject(const ViewProvider&);
|
||||
void slotChangedObject(const ViewProvider&, const App::Property& Prop);
|
||||
@@ -223,8 +233,10 @@ public:
|
||||
static PyObject* sAddLangPath (PyObject *self,PyObject *args); // adds a path to a qm file
|
||||
static PyObject* sAddIconPath (PyObject *self,PyObject *args); // adds a path to an icon file
|
||||
static PyObject* sAddIcon (PyObject *self,PyObject *args); // adds an icon to the cache
|
||||
static PyObject* sGetIcon (PyObject *self,PyObject *args); // get an icon from the cache
|
||||
|
||||
static PyObject* sSendActiveView (PyObject *self,PyObject *args);
|
||||
static PyObject* sSendFocusView (PyObject *self,PyObject *args);
|
||||
|
||||
static PyObject* sGetMainWindow (PyObject *self,PyObject *args);
|
||||
static PyObject* sUpdateGui (PyObject *self,PyObject *args);
|
||||
@@ -238,6 +250,7 @@ public:
|
||||
static PyObject* sRunCommand (PyObject *self,PyObject *args);
|
||||
static PyObject* sAddCommand (PyObject *self,PyObject *args);
|
||||
static PyObject* sListCommands (PyObject *self,PyObject *args);
|
||||
static PyObject* sIsCommandActive (PyObject *self,PyObject *args);
|
||||
|
||||
static PyObject* sHide (PyObject *self,PyObject *args); // deprecated
|
||||
static PyObject* sShow (PyObject *self,PyObject *args); // deprecated
|
||||
@@ -247,6 +260,7 @@ public:
|
||||
static PyObject* sOpen (PyObject *self,PyObject *args); // open Python scripts
|
||||
static PyObject* sInsert (PyObject *self,PyObject *args); // open Python scripts
|
||||
static PyObject* sExport (PyObject *self,PyObject *args);
|
||||
static PyObject* sReload (PyObject *self,PyObject *args);
|
||||
|
||||
static PyObject* sCoinRemoveAllChildren (PyObject *self,PyObject *args);
|
||||
|
||||
|
||||
@@ -101,6 +101,9 @@ PyMethodDef Application::Methods[] = {
|
||||
{"addIcon", (PyCFunction) Application::sAddIcon, METH_VARARGS,
|
||||
"addIcon(string, string or list) -> None\n\n"
|
||||
"Add an icon as file name or in XPM format to the system"},
|
||||
{"getIcon", (PyCFunction) Application::sGetIcon, METH_VARARGS,
|
||||
"getIcon(string -> QIcon\n\n"
|
||||
"Get an icon in the system"},
|
||||
{"getMainWindow", (PyCFunction) Application::sGetMainWindow, METH_VARARGS,
|
||||
"getMainWindow() -> QMainWindow\n\n"
|
||||
"Return the main window instance"},
|
||||
@@ -132,11 +135,16 @@ PyMethodDef Application::Methods[] = {
|
||||
{"runCommand", (PyCFunction) Application::sRunCommand, METH_VARARGS,
|
||||
"runCommand(string) -> None\n\n"
|
||||
"Run command with name"},
|
||||
{"isCommandActive", (PyCFunction) Application::sIsCommandActive, METH_VARARGS,
|
||||
"isCommandActive(string) -> Bool\n\n"
|
||||
"Test if a command is active"},
|
||||
{"listCommands", (PyCFunction) Application::sListCommands, METH_VARARGS,
|
||||
"listCommands() -> list of strings\n\n"
|
||||
"Returns a list of all commands known to FreeCAD."},
|
||||
{"SendMsgToActiveView", (PyCFunction) Application::sSendActiveView, METH_VARARGS,
|
||||
"deprecated -- use class View"},
|
||||
{"sendMsgToFocusView", (PyCFunction) Application::sSendFocusView, METH_VARARGS,
|
||||
"send message to the focused view"},
|
||||
{"hide", (PyCFunction) Application::sHide, METH_VARARGS,
|
||||
"deprecated"},
|
||||
{"show", (PyCFunction) Application::sShow, METH_VARARGS,
|
||||
@@ -160,8 +168,8 @@ PyMethodDef Application::Methods[] = {
|
||||
"setActiveDocument(string or App.Document) -> None\n\n"
|
||||
"Activate the specified document"},
|
||||
{"activeView", (PyCFunction)Application::sActiveView, METH_VARARGS,
|
||||
"activeView() -> object or None\n\n"
|
||||
"Return the active view of the active document or None if no one exists"},
|
||||
"activeView(typename=None) -> object or None\n\n"
|
||||
"Return the active view of the active document or None if no one exists" },
|
||||
{"activateView", (PyCFunction)Application::sActivateView, METH_VARARGS,
|
||||
"activateView(type)\n\n"
|
||||
"Activate a view of the given type of the active document"},
|
||||
@@ -200,6 +208,10 @@ PyMethodDef Application::Methods[] = {
|
||||
"removeDocumentObserver() -> None\n\n"
|
||||
"Remove an added document observer."},
|
||||
|
||||
{"reload", (PyCFunction) Application::sReload, METH_VARARGS,
|
||||
"reload(name) -> doc\n\n"
|
||||
"Reload a partial opened document"},
|
||||
|
||||
{"coinRemoveAllChildren", (PyCFunction) Application::sCoinRemoveAllChildren, METH_VARARGS,
|
||||
"Remove all children from a group node"},
|
||||
|
||||
@@ -236,16 +248,37 @@ PyObject* Gui::Application::sActiveDocument(PyObject * /*self*/, PyObject *args)
|
||||
|
||||
PyObject* Gui::Application::sActiveView(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
const char *typeName=0;
|
||||
if (!PyArg_ParseTuple(args, "|s", &typeName))
|
||||
return NULL;
|
||||
|
||||
Gui::MDIView* mdiView = Instance->activeView();
|
||||
if (mdiView) {
|
||||
// already incremented in getPyObject().
|
||||
return mdiView->getPyObject();
|
||||
}
|
||||
PY_TRY {
|
||||
Base::Type type;
|
||||
if(typeName) {
|
||||
type = Base::Type::fromName(typeName);
|
||||
if(type.isBad()) {
|
||||
PyErr_Format(PyExc_TypeError, "Invalid type '%s'", typeName);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Py_Return;
|
||||
Gui::MDIView* mdiView = Instance->activeView();
|
||||
if (mdiView && (type.isBad() || mdiView->isDerivedFrom(type))) {
|
||||
auto res = Py::asObject(mdiView->getPyObject());
|
||||
if(!res.isNone() || !type.isBad())
|
||||
return Py::new_reference_to(res);
|
||||
}
|
||||
|
||||
if(type.isBad())
|
||||
type = Gui::View3DInventor::getClassTypeId();
|
||||
Instance->activateView(type, true);
|
||||
mdiView = Instance->activeView();
|
||||
if (mdiView)
|
||||
return mdiView->getPyObject();
|
||||
|
||||
Py_Return;
|
||||
|
||||
} PY_CATCH
|
||||
}
|
||||
|
||||
PyObject* Gui::Application::sActivateView(PyObject * /*self*/, PyObject *args)
|
||||
@@ -624,6 +657,28 @@ PyObject* Application::sSendActiveView(PyObject * /*self*/, PyObject *args)
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* Application::sSendFocusView(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char *psCommandStr;
|
||||
PyObject *suppress=Py_False;
|
||||
if (!PyArg_ParseTuple(args, "s|O!",&psCommandStr,&PyBool_Type,&suppress))
|
||||
return NULL;
|
||||
|
||||
const char* ppReturn=0;
|
||||
if (!Instance->sendMsgToFocusView(psCommandStr,&ppReturn)) {
|
||||
if (!PyObject_IsTrue(suppress))
|
||||
Base::Console().Warning("Unknown view command: %s\n",psCommandStr);
|
||||
}
|
||||
|
||||
// Print the return value to the output
|
||||
if (ppReturn) {
|
||||
return Py_BuildValue("s",ppReturn);
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* Application::sGetMainWindow(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
@@ -1042,6 +1097,21 @@ PyObject* Application::sAddIcon(PyObject * /*self*/, PyObject *args)
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* Application::sGetIcon(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char *iconName;
|
||||
if (!PyArg_ParseTuple(args, "s", &iconName))
|
||||
return NULL;
|
||||
|
||||
PythonWrapper wrap;
|
||||
wrap.loadGuiModule();
|
||||
wrap.loadWidgetsModule();
|
||||
auto pixmap = BitmapFactory().pixmap(iconName);
|
||||
if(!pixmap.isNull())
|
||||
return Py::new_reference_to(wrap.fromQIcon(new QIcon(pixmap)));
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* Application::sAddCommand(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char* pName;
|
||||
@@ -1079,7 +1149,11 @@ PyObject* Application::sAddCommand(PyObject * /*self*/, PyObject *args)
|
||||
group = what[1];
|
||||
}
|
||||
else {
|
||||
group = module;
|
||||
boost::regex rx("/Ext/freecad/(\\w+)/");
|
||||
if (boost::regex_search(file, what, rx))
|
||||
group = what[1];
|
||||
else
|
||||
group = module;
|
||||
}
|
||||
}
|
||||
catch (Py::Exception& e) {
|
||||
@@ -1131,6 +1205,9 @@ PyObject* Application::sRunCommand(PyObject * /*self*/, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "s|i", &pName, &item))
|
||||
return NULL;
|
||||
|
||||
Gui::Command::LogDisabler d1;
|
||||
Gui::SelectionLogDisabler d2;
|
||||
|
||||
Command* cmd = Application::Instance->commandManager().getCommandByName(pName);
|
||||
if (cmd) {
|
||||
cmd->invoke(item);
|
||||
@@ -1143,6 +1220,23 @@ PyObject* Application::sRunCommand(PyObject * /*self*/, PyObject *args)
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* Application::sIsCommandActive(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char* pName;
|
||||
if (!PyArg_ParseTuple(args, "s", &pName))
|
||||
return NULL;
|
||||
|
||||
Command* cmd = Application::Instance->commandManager().getCommandByName(pName);
|
||||
if (!cmd) {
|
||||
PyErr_Format(Base::BaseExceptionFreeCADError, "No such command '%s'", pName);
|
||||
return 0;
|
||||
}
|
||||
PY_TRY {
|
||||
return Py::new_reference_to(Py::Boolean(cmd->isActive()));
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
|
||||
PyObject* Application::sListCommands(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
@@ -1168,6 +1262,10 @@ PyObject* Application::sDoCommand(PyObject * /*self*/, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "s", &sCmd))
|
||||
return NULL;
|
||||
|
||||
Gui::Command::LogDisabler d1;
|
||||
Gui::SelectionLogDisabler d2;
|
||||
|
||||
Gui::Command::printPyCaller();
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::App, sCmd);
|
||||
|
||||
PyObject *module, *dict;
|
||||
@@ -1189,6 +1287,10 @@ PyObject* Application::sDoCommandGui(PyObject * /*self*/, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "s", &sCmd))
|
||||
return NULL;
|
||||
|
||||
Gui::Command::LogDisabler d1;
|
||||
Gui::SelectionLogDisabler d2;
|
||||
|
||||
Gui::Command::printPyCaller();
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::Gui, sCmd);
|
||||
|
||||
PyObject *module, *dict;
|
||||
@@ -1325,6 +1427,19 @@ PyObject* Application::sGetMarkerIndex(PyObject * /*self*/, PyObject *args)
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject* Application::sReload(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
if (!PyArg_ParseTuple(args, "s", &name))
|
||||
return NULL;
|
||||
|
||||
PY_TRY {
|
||||
auto doc = Application::Instance->reopen(App::GetApplication().getDocument(name));
|
||||
if(doc)
|
||||
return doc->getPyObject();
|
||||
}PY_CATCH;
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* Application::sAddDocObserver(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
|
||||
@@ -77,6 +77,11 @@ else(MSVC)
|
||||
endif(MSVC)
|
||||
|
||||
if (BUILD_QT5)
|
||||
|
||||
if (Qt5WebEngineWidgets_FOUND AND BUILD_WITH_CONDA)
|
||||
add_definitions(-DQTWEBENGINE)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${Qt5Widgets_INCLUDE_DIRS}
|
||||
@@ -356,6 +361,7 @@ set(Gui_MOC_HDRS
|
||||
TaskView/TaskView.h
|
||||
DAGView/DAGView.h
|
||||
DAGView/DAGModel.h
|
||||
DlgObjectSelection.h
|
||||
${FreeCADGui_SDK_MOC_HDRS}
|
||||
)
|
||||
|
||||
@@ -428,6 +434,7 @@ SET(Gui_UIC_SRCS
|
||||
TextureMapping.ui
|
||||
TaskView/TaskAppearance.ui
|
||||
TaskView/TaskSelectLinkProperty.ui
|
||||
DlgObjectSelection.ui
|
||||
)
|
||||
|
||||
SET(Gui_RES_SRCS
|
||||
@@ -500,6 +507,7 @@ SET(Dialog_CPP_SRCS
|
||||
DownloadItem.cpp
|
||||
DownloadManager.cpp
|
||||
DocumentRecovery.cpp
|
||||
DlgObjectSelection.cpp
|
||||
)
|
||||
|
||||
SET(Dialog_HPP_SRCS
|
||||
@@ -533,6 +541,7 @@ SET(Dialog_HPP_SRCS
|
||||
DownloadItem.h
|
||||
DownloadManager.h
|
||||
DocumentRecovery.h
|
||||
DlgObjectSelection.h
|
||||
)
|
||||
|
||||
SET(Dialog_SRCS
|
||||
@@ -568,6 +577,7 @@ SET(Dialog_SRCS
|
||||
Placement.ui
|
||||
SceneInspector.ui
|
||||
TextureMapping.ui
|
||||
DlgObjectSelection.ui
|
||||
)
|
||||
SOURCE_GROUP("Dialog" FILES ${Dialog_SRCS})
|
||||
|
||||
|
||||
@@ -37,11 +37,15 @@
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/FileInfo.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Sequencer.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Console.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObjectGroup.h>
|
||||
#include <App/DocumentObject.h>
|
||||
@@ -69,6 +73,9 @@
|
||||
#include "MergeDocuments.h"
|
||||
#include "NavigationStyle.h"
|
||||
#include "GraphvizView.h"
|
||||
#include "DlgObjectSelection.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("Command", false);
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
@@ -90,6 +97,7 @@ StdCmdOpen::StdCmdOpen()
|
||||
sStatusTip = QT_TR_NOOP("Open a document or import files");
|
||||
sPixmap = "document-open";
|
||||
sAccel = keySequenceToAccel(QKeySequence::Open);
|
||||
eType = NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdOpen::activated(int iMsg)
|
||||
@@ -538,6 +546,33 @@ bool StdCmdSaveCopy::isActive(void)
|
||||
return ( getActiveGuiDocument() ? true : false );
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Std_SaveAll
|
||||
//===========================================================================
|
||||
DEF_STD_CMD_A(StdCmdSaveAll);
|
||||
|
||||
StdCmdSaveAll::StdCmdSaveAll()
|
||||
:Command("Std_SaveAll")
|
||||
{
|
||||
sGroup = QT_TR_NOOP("File");
|
||||
sMenuText = QT_TR_NOOP("Save All");
|
||||
sToolTipText = QT_TR_NOOP("Save all opened document");
|
||||
sWhatsThis = "Std_SaveAll";
|
||||
sStatusTip = QT_TR_NOOP("Save all opened document");
|
||||
}
|
||||
|
||||
void StdCmdSaveAll::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
Gui::Document::saveAll();
|
||||
}
|
||||
|
||||
bool StdCmdSaveAll::isActive(void)
|
||||
{
|
||||
return ( getActiveGuiDocument() ? true : false );
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
// Std_Revert
|
||||
//===========================================================================
|
||||
@@ -552,6 +587,7 @@ StdCmdRevert::StdCmdRevert()
|
||||
sWhatsThis = "Std_Revert";
|
||||
sStatusTip = QT_TR_NOOP("Reverts to the saved version of this file");
|
||||
//sPixmap = "document-revert";
|
||||
eType = NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdRevert::activated(int iMsg)
|
||||
@@ -742,6 +778,7 @@ StdCmdQuit::StdCmdQuit()
|
||||
sPixmap = "application-exit";
|
||||
#endif
|
||||
sAccel = "Alt+F4";
|
||||
eType = NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdQuit::activated(int iMsg)
|
||||
@@ -767,7 +804,7 @@ StdCmdUndo::StdCmdUndo()
|
||||
sStatusTip = QT_TR_NOOP("Undo exactly one action");
|
||||
sPixmap = "edit-undo";
|
||||
sAccel = keySequenceToAccel(QKeySequence::Undo);
|
||||
eType = ForEdit;
|
||||
eType = ForEdit|NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdUndo::activated(int iMsg)
|
||||
@@ -811,7 +848,7 @@ StdCmdRedo::StdCmdRedo()
|
||||
sStatusTip = QT_TR_NOOP("Redoes a previously undone action");
|
||||
sPixmap = "edit-redo";
|
||||
sAccel = keySequenceToAccel(QKeySequence::Redo);
|
||||
eType = ForEdit;
|
||||
eType = ForEdit|NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdRedo::activated(int iMsg)
|
||||
@@ -887,7 +924,7 @@ StdCmdCopy::StdCmdCopy()
|
||||
void StdCmdCopy::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
bool done = getGuiApplication()->sendMsgToActiveView("Copy");
|
||||
bool done = getGuiApplication()->sendMsgToFocusView("Copy");
|
||||
if (!done) {
|
||||
QMimeData * mimeData = getMainWindow()->createMimeDataFromSelection();
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
@@ -897,7 +934,7 @@ void StdCmdCopy::activated(int iMsg)
|
||||
|
||||
bool StdCmdCopy::isActive(void)
|
||||
{
|
||||
if (getGuiApplication()->sendHasMsgToActiveView("Copy"))
|
||||
if (getGuiApplication()->sendHasMsgToFocusView("Copy"))
|
||||
return true;
|
||||
return Selection().hasSelection();
|
||||
}
|
||||
@@ -922,7 +959,7 @@ StdCmdPaste::StdCmdPaste()
|
||||
void StdCmdPaste::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
bool done = getGuiApplication()->sendMsgToActiveView("Paste");
|
||||
bool done = getGuiApplication()->sendMsgToFocusView("Paste");
|
||||
if (!done) {
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
const QMimeData* mimeData = cb->mimeData();
|
||||
@@ -935,7 +972,7 @@ void StdCmdPaste::activated(int iMsg)
|
||||
|
||||
bool StdCmdPaste::isActive(void)
|
||||
{
|
||||
if (getGuiApplication()->sendHasMsgToActiveView("Paste"))
|
||||
if (getGuiApplication()->sendHasMsgToFocusView("Paste"))
|
||||
return true;
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
const QMimeData* mime = cb->mimeData();
|
||||
@@ -959,38 +996,34 @@ StdCmdDuplicateSelection::StdCmdDuplicateSelection()
|
||||
void StdCmdDuplicateSelection::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
std::vector<SelectionSingleton::SelObj> sel = Selection().getCompleteSelection();
|
||||
std::set<App::DocumentObject*> unique_objs;
|
||||
std::map< App::Document*, std::vector<App::DocumentObject*> > objs;
|
||||
for (std::vector<SelectionSingleton::SelObj>::iterator it = sel.begin(); it != sel.end(); ++it) {
|
||||
if (it->pObject && it->pObject->getDocument()) {
|
||||
if (unique_objs.insert(it->pObject).second)
|
||||
objs[it->pObject->getDocument()].push_back(it->pObject);
|
||||
}
|
||||
std::vector<App::DocumentObject*> sel;
|
||||
std::set<App::DocumentObject*> objSet;
|
||||
for(auto &s : Selection().getCompleteSelection()) {
|
||||
if(s.pObject && s.pObject->getNameInDocument() && objSet.insert(s.pObject).second)
|
||||
sel.push_back(s.pObject);
|
||||
}
|
||||
|
||||
if (objs.empty())
|
||||
if(sel.empty())
|
||||
return;
|
||||
|
||||
bool hasXLink = false;
|
||||
Base::FileInfo fi(App::Application::getTempFileName());
|
||||
{
|
||||
std::vector<App::DocumentObject*> sel; // selected
|
||||
std::vector<App::DocumentObject*> all; // object sub-graph
|
||||
for (std::map< App::Document*, std::vector<App::DocumentObject*> >::iterator it = objs.begin(); it != objs.end(); ++it) {
|
||||
std::vector<App::DocumentObject*> dep = it->first->getDependencyList(it->second);
|
||||
sel.insert(sel.end(), it->second.begin(), it->second.end());
|
||||
all.insert(all.end(), dep.begin(), dep.end());
|
||||
}
|
||||
|
||||
auto all = App::Document::getDependencyList(sel);
|
||||
if (all.size() > sel.size()) {
|
||||
int ret = QMessageBox::question(getMainWindow(),
|
||||
qApp->translate("Std_DuplicateSelection","Object dependencies"),
|
||||
qApp->translate("Std_DuplicateSelection","The selected objects have a dependency to unselected objects.\n"
|
||||
"Do you want to duplicate them, too?"),
|
||||
QMessageBox::Yes,QMessageBox::No);
|
||||
if (ret == QMessageBox::Yes) {
|
||||
sel = all;
|
||||
}
|
||||
DlgObjectSelection dlg(sel,getMainWindow());
|
||||
if(dlg.exec()!=QDialog::Accepted)
|
||||
return;
|
||||
sel = dlg.getSelections();
|
||||
if(sel.empty())
|
||||
return;
|
||||
}
|
||||
std::vector<App::Document*> unsaved;
|
||||
hasXLink = App::PropertyXLink::hasXLink(sel,&unsaved);
|
||||
if(unsaved.size()) {
|
||||
QMessageBox::critical(getMainWindow(), QObject::tr("Unsaved document"),
|
||||
QObject::tr("The exported object contains external link. Please save the document"
|
||||
"at least once before exporting."));
|
||||
return;
|
||||
}
|
||||
|
||||
// save stuff to file
|
||||
@@ -1002,13 +1035,26 @@ void StdCmdDuplicateSelection::activated(int iMsg)
|
||||
}
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if (doc) {
|
||||
doc->openTransaction("Duplicate");
|
||||
// restore objects from file and add to active document
|
||||
Base::ifstream str(fi, std::ios::in | std::ios::binary);
|
||||
MergeDocuments mimeView(doc);
|
||||
mimeView.importObjects(str);
|
||||
str.close();
|
||||
doc->commitTransaction();
|
||||
bool proceed = true;
|
||||
if(hasXLink && !doc->isSaved()) {
|
||||
int ret = QMessageBox::question(getMainWindow(),
|
||||
qApp->translate("Std_DuplicateSelection","Object dependencies"),
|
||||
qApp->translate("Std_DuplicateSelection",
|
||||
"To link to external objects, the document must be saved at least once.\n"
|
||||
"Do you want to save the document now?"),
|
||||
QMessageBox::Yes,QMessageBox::No);
|
||||
if(ret == QMessageBox::Yes)
|
||||
proceed = Application::Instance->getDocument(doc)->saveAs();
|
||||
}
|
||||
if(proceed) {
|
||||
doc->openTransaction("Duplicate");
|
||||
// restore objects from file and add to active document
|
||||
Base::ifstream str(fi, std::ios::in | std::ios::binary);
|
||||
MergeDocuments mimeView(doc);
|
||||
mimeView.importObjects(str);
|
||||
str.close();
|
||||
doc->commitTransaction();
|
||||
}
|
||||
}
|
||||
fi.deleteFile();
|
||||
}
|
||||
@@ -1076,137 +1122,147 @@ void StdCmdDelete::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
// go through all documents
|
||||
const SelectionSingleton& rSel = Selection();
|
||||
const std::vector<App::Document*> docs = App::GetApplication().getDocuments();
|
||||
for (std::vector<App::Document*>::const_iterator it = docs.begin(); it != docs.end(); ++it) {
|
||||
Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(*it);
|
||||
std::vector<Gui::SelectionObject> sel = rSel.getSelectionEx((*it)->getName());
|
||||
if (!sel.empty()) {
|
||||
bool autoDeletion = true;
|
||||
|
||||
// if an object is in edit mode handle only this object even if unselected (#0001838)
|
||||
Gui::ViewProvider* vpedit = pGuiDoc->getInEdit();
|
||||
if (vpedit) {
|
||||
// check if the edited view provider is selected
|
||||
for (std::vector<Gui::SelectionObject>::iterator ft = sel.begin(); ft != sel.end(); ++ft) {
|
||||
Gui::ViewProvider* vp = pGuiDoc->getViewProvider(ft->getObject());
|
||||
if (vp == vpedit) {
|
||||
if (!ft->getSubNames().empty()) {
|
||||
// handle the view provider
|
||||
Gui::getMainWindow()->setUpdatesEnabled(false);
|
||||
|
||||
try {
|
||||
(*it)->openTransaction("Delete");
|
||||
vpedit->onDelete(ft->getSubNames());
|
||||
(*it)->commitTransaction();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
(*it)->abortTransaction();
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
Gui::getMainWindow()->setUpdatesEnabled(true);
|
||||
Gui::getMainWindow()->update();
|
||||
}
|
||||
break;
|
||||
std::set<App::Document*> docs;
|
||||
try {
|
||||
openCommand("Delete");
|
||||
if (getGuiApplication()->sendHasMsgToFocusView(getName())) {
|
||||
commitCommand();
|
||||
return;
|
||||
}
|
||||
Gui::getMainWindow()->setUpdatesEnabled(false);
|
||||
auto editDoc = Application::Instance->editDocument();
|
||||
ViewProviderDocumentObject *vpedit = 0;
|
||||
if(editDoc)
|
||||
vpedit = dynamic_cast<ViewProviderDocumentObject*>(editDoc->getInEdit());
|
||||
if(vpedit) {
|
||||
for(auto &sel : Selection().getSelectionEx(editDoc->getDocument()->getName())) {
|
||||
if(sel.getObject() == vpedit->getObject()) {
|
||||
if (!sel.getSubNames().empty()) {
|
||||
vpedit->onDelete(sel.getSubNames());
|
||||
docs.insert(editDoc->getDocument());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// check if we can delete the object - linked objects
|
||||
std::set<QString> affectedLabels;
|
||||
for (std::vector<Gui::SelectionObject>::iterator ft = sel.begin(); ft != sel.end(); ++ft) {
|
||||
App::DocumentObject* obj = ft->getObject();
|
||||
std::vector<App::DocumentObject*> links = obj->getInList();
|
||||
if (!links.empty()) {
|
||||
// check if the referenced objects are groups or are selected too
|
||||
for (std::vector<App::DocumentObject*>::iterator lt = links.begin(); lt != links.end(); ++lt) {
|
||||
if (!rSel.isSelected(*lt)) {
|
||||
ViewProvider* vp = pGuiDoc->getViewProvider(*lt);
|
||||
if (!vp->canDelete(obj)) {
|
||||
autoDeletion = false;
|
||||
affectedLabels.insert(QString::fromUtf8((*lt)->Label.getValue()));
|
||||
}
|
||||
} else {
|
||||
std::set<QString> affectedLabels;
|
||||
bool more = false;
|
||||
auto sels = Selection().getSelectionEx();
|
||||
bool autoDeletion = true;
|
||||
for(auto &sel : sels) {
|
||||
auto obj = sel.getObject();
|
||||
for(auto parent : obj->getInList()) {
|
||||
if(!Selection().isSelected(parent)) {
|
||||
ViewProvider* vp = Application::Instance->getViewProvider(parent);
|
||||
if (vp && !vp->canDelete(obj)) {
|
||||
autoDeletion = false;
|
||||
QString label;
|
||||
if(parent->getDocument() != obj->getDocument())
|
||||
label = QLatin1String(parent->getFullName().c_str());
|
||||
else
|
||||
label = QLatin1String(parent->getNameInDocument());
|
||||
if(parent->Label.getStrValue() != parent->getNameInDocument())
|
||||
label += QString::fromLatin1(" (%1)").arg(
|
||||
QString::fromUtf8(parent->Label.getValue()));
|
||||
affectedLabels.insert(label);
|
||||
if(affectedLabels.size()>=10) {
|
||||
more = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(more)
|
||||
break;
|
||||
}
|
||||
|
||||
//check for inactive objects in selection (Mantis #3477)
|
||||
std::set<QString> inactiveLabels;
|
||||
App::Application& app = App::GetApplication();
|
||||
App::Document* actDoc = app.getActiveDocument();
|
||||
for (std::vector<Gui::SelectionObject>::iterator ft = sel.begin(); ft != sel.end(); ++ft) {
|
||||
App::DocumentObject* obj = ft->getObject();
|
||||
App::Document* objDoc = obj->getDocument();
|
||||
if (actDoc != objDoc) {
|
||||
inactiveLabels.insert(QString::fromUtf8(obj->Label.getValue()));
|
||||
autoDeletion = false;
|
||||
}
|
||||
// The check below is not needed because we now only get selection
|
||||
// from the active document
|
||||
#if 0
|
||||
//check for inactive objects in selection Mantis #3477
|
||||
std::set<QString> inactiveLabels;
|
||||
App::Application& app = App::GetApplication();
|
||||
App::Document* actDoc = app.getActiveDocument();
|
||||
for (std::vector<Gui::SelectionObject>::iterator ft = sels.begin(); ft != sels.end(); ++ft) {
|
||||
App::DocumentObject* obj = ft->getObject();
|
||||
App::Document* objDoc = obj->getDocument();
|
||||
if (actDoc != objDoc) {
|
||||
inactiveLabels.insert(QString::fromUtf8(obj->Label.getValue()));
|
||||
autoDeletion = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!autoDeletion) { //can't just delete, need to ask
|
||||
QString bodyMessage;
|
||||
QTextStream bodyMessageStream(&bodyMessage);
|
||||
|
||||
//message for linked items
|
||||
if (!autoDeletion) {
|
||||
QString bodyMessage;
|
||||
QTextStream bodyMessageStream(&bodyMessage);
|
||||
bodyMessageStream << qApp->translate("Std_Delete",
|
||||
"The following referencing objects might break.\n\n"
|
||||
"Are you sure you want to continue?\n");
|
||||
for (const auto ¤tLabel : affectedLabels)
|
||||
bodyMessageStream << '\n' << currentLabel;
|
||||
if(more)
|
||||
bodyMessageStream << "\n...";
|
||||
#if 0
|
||||
//message for inactive items
|
||||
if (!inactiveLabels.empty()) {
|
||||
if (!affectedLabels.empty()) {
|
||||
bodyMessageStream << qApp->translate("Std_Delete",
|
||||
"These items are linked to items selected for deletion and might break.") << "\n\n";
|
||||
for (const auto ¤tLabel : affectedLabels)
|
||||
bodyMessageStream << currentLabel << '\n';
|
||||
bodyMessageStream << "\n";
|
||||
}
|
||||
|
||||
//message for inactive items
|
||||
if (!inactiveLabels.empty()) {
|
||||
if (!affectedLabels.empty()) {
|
||||
bodyMessageStream << "\n";
|
||||
}
|
||||
std::string thisDoc = pGuiDoc->getDocument()->getName();
|
||||
bodyMessageStream << qApp->translate("Std_Delete",
|
||||
"These items are selected for deletion, but are not in the active document.") << "\n\n";
|
||||
for (const auto ¤tLabel : inactiveLabels)
|
||||
bodyMessageStream << currentLabel << " / " << Base::Tools::fromStdString(thisDoc) << '\n';
|
||||
}
|
||||
bodyMessageStream << "\n\n" << qApp->translate("Std_Delete",
|
||||
"Are you sure you want to continue?");
|
||||
|
||||
int ret = QMessageBox::question(Gui::getMainWindow(),
|
||||
qApp->translate("Std_Delete", "Delete Selection Issues"), bodyMessage,
|
||||
QMessageBox::Yes, QMessageBox::No);
|
||||
if (ret == QMessageBox::Yes)
|
||||
autoDeletion = true;
|
||||
std::string thisDoc = pGuiDoc->getDocument()->getName();
|
||||
bodyMessageStream << qApp->translate("Std_Delete",
|
||||
"These items are selected for deletion, but are not in the active document. \n\n");
|
||||
for (const auto ¤tLabel : inactiveLabels)
|
||||
bodyMessageStream << currentLabel << " / " << Base::Tools::fromStdString(thisDoc) << '\n';
|
||||
}
|
||||
#endif
|
||||
|
||||
if (autoDeletion) {
|
||||
Gui::getMainWindow()->setUpdatesEnabled(false);
|
||||
try {
|
||||
(*it)->openTransaction("Delete");
|
||||
for (std::vector<Gui::SelectionObject>::iterator ft = sel.begin(); ft != sel.end(); ++ft) {
|
||||
Gui::ViewProvider* vp = pGuiDoc->getViewProvider(ft->getObject());
|
||||
if (vp) {
|
||||
// ask the ViewProvider if it wants to do some clean up
|
||||
if (vp->onDelete(ft->getSubNames())) {
|
||||
doCommand(Doc,"App.getDocument(\"%s\").removeObject(\"%s\")"
|
||||
,(*it)->getName(), ft->getFeatName());
|
||||
}
|
||||
}
|
||||
int ret = QMessageBox::warning(Gui::getMainWindow(),
|
||||
qApp->translate("Std_Delete", "Object dependencies"), bodyMessage,
|
||||
QMessageBox::Yes, QMessageBox::No);
|
||||
if (ret == QMessageBox::Yes)
|
||||
autoDeletion = true;
|
||||
}
|
||||
if (autoDeletion) {
|
||||
for(auto &sel : sels) {
|
||||
auto obj = sel.getObject();
|
||||
Gui::ViewProvider* vp = Application::Instance->getViewProvider(obj);
|
||||
if (vp) {
|
||||
// ask the ViewProvider if it wants to do some clean up
|
||||
if (vp->onDelete(sel.getSubNames())) {
|
||||
FCMD_OBJ_DOC_CMD(obj,"removeObject('" << obj->getNameInDocument() << "')");
|
||||
docs.insert(obj->getDocument());
|
||||
}
|
||||
(*it)->commitTransaction();
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
(*it)->abortTransaction();
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
Gui::getMainWindow()->setUpdatesEnabled(true);
|
||||
Gui::getMainWindow()->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
doCommand(Doc,"App.getDocument(\"%s\").recompute()", (*it)->getName());
|
||||
if(docs.size()) {
|
||||
const auto &outList = App::PropertyXLink::getDocumentOutList();
|
||||
for(auto it=docs.begin();it!=docs.end();++it) {
|
||||
auto itd = outList.find(*it);
|
||||
if(itd!=outList.end()) {
|
||||
for(auto doc : itd->second) {
|
||||
if(doc != *it)
|
||||
docs.erase(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto doc : docs) {
|
||||
FCMD_DOC_CMD(doc,"recompute()");
|
||||
}
|
||||
}
|
||||
} catch (const Base::Exception& e) {
|
||||
QMessageBox::critical(getMainWindow(), QObject::tr("Delete failed"),
|
||||
QString::fromLatin1(e.what()));
|
||||
e.ReportException();
|
||||
} catch (...) {
|
||||
QMessageBox::critical(getMainWindow(), QObject::tr("Delete failed"),
|
||||
QString::fromLatin1("Unknown error"));
|
||||
}
|
||||
commitCommand();
|
||||
Gui::getMainWindow()->setUpdatesEnabled(true);
|
||||
Gui::getMainWindow()->update();
|
||||
}
|
||||
|
||||
bool StdCmdDelete::isActive(void)
|
||||
@@ -1230,24 +1286,32 @@ StdCmdRefresh::StdCmdRefresh()
|
||||
sPixmap = "view-refresh";
|
||||
sAccel = keySequenceToAccel(QKeySequence::Refresh);
|
||||
eType = AlterDoc | Alter3DView | AlterSelection | ForEdit;
|
||||
bCanLog = false;
|
||||
}
|
||||
|
||||
void StdCmdRefresh::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
if (getActiveGuiDocument()) {
|
||||
//Note: Don't add the recompute to undo/redo because it complicates
|
||||
//testing the changes of properties.
|
||||
//openCommand("Refresh active document");
|
||||
this->getDocument()->setStatus(App::Document::SkipRecompute, false);
|
||||
doCommand(Doc,"App.activeDocument().recompute()");
|
||||
//commitCommand();
|
||||
App::AutoTransaction trans("Recompute");
|
||||
try {
|
||||
doCommand(Doc,"App.activeDocument().recompute(None,True,True)");
|
||||
} catch(Base::Exception &e) {
|
||||
int ret = QMessageBox::warning(getMainWindow(), QObject::tr("Dependency error"),
|
||||
QObject::tr("The document contains dependency cycles.\n"
|
||||
"Please check the Report View for more details.\n\n"
|
||||
"Do you still want to proceed?"),
|
||||
QMessageBox::Yes, QMessageBox::No);
|
||||
if(ret == QMessageBox::No)
|
||||
return;
|
||||
doCommand(Doc,"App.activeDocument().recompute(None,True)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StdCmdRefresh::isActive(void)
|
||||
{
|
||||
return this->getDocument() && this->getDocument()->isTouched();
|
||||
return this->getDocument() && this->getDocument()->mustExecute();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
@@ -1453,6 +1517,220 @@ bool StdCmdEdit::isActive(void)
|
||||
return (Selection().getCompleteSelection().size() > 0) || (Gui::Control().activeDialog() != 0);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// StdCmdExpression
|
||||
//===========================================================================
|
||||
class StdCmdExpression : public Gui::Command
|
||||
{
|
||||
public:
|
||||
StdCmdExpression() : Command("Std_Expressions")
|
||||
{
|
||||
sGroup = QT_TR_NOOP("Edit");
|
||||
sMenuText = QT_TR_NOOP("Expression actions");
|
||||
sToolTipText = QT_TR_NOOP("Expression actions");
|
||||
sWhatsThis = "Std_Expressions";
|
||||
sStatusTip = QT_TR_NOOP("Expression actions");
|
||||
eType = ForEdit;
|
||||
}
|
||||
|
||||
virtual const char* className() const {return "StdCmdExpression";}
|
||||
protected:
|
||||
|
||||
virtual void activated(int iMsg) {
|
||||
std::map<App::Document*, std::set<App::DocumentObject*> > objs;
|
||||
switch(iMsg) {
|
||||
case 0:
|
||||
for(auto &sel : Selection().getCompleteSelection())
|
||||
objs[sel.pObject->getDocument()].insert(sel.pObject);
|
||||
break;
|
||||
case 1:
|
||||
if(App::GetApplication().getActiveDocument()) {
|
||||
auto doc = App::GetApplication().getActiveDocument();
|
||||
auto array = doc->getObjects();
|
||||
auto &set = objs[doc];
|
||||
set.insert(array.begin(),array.end());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for(auto doc : App::GetApplication().getDocuments()) {
|
||||
auto &set = objs[doc];
|
||||
auto array = doc->getObjects();
|
||||
set.insert(array.begin(),array.end());
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
pasteExpressions();
|
||||
break;
|
||||
}
|
||||
copyExpressions(objs);
|
||||
}
|
||||
|
||||
virtual Gui::Action * createAction(void) {
|
||||
ActionGroup* pcAction = new ActionGroup(this, getMainWindow());
|
||||
pcAction->setDropDownMenu(true);
|
||||
applyCommandData(this->className(), pcAction);
|
||||
|
||||
pcActionCopySel = pcAction->addAction(QObject::tr("Copy selected"));
|
||||
pcActionCopyActive = pcAction->addAction(QObject::tr("Copy active document"));
|
||||
pcActionCopyAll = pcAction->addAction(QObject::tr("Copy all documents"));
|
||||
pcActionPaste = pcAction->addAction(QObject::tr("Paste"));
|
||||
|
||||
return pcAction;
|
||||
}
|
||||
|
||||
void copyExpressions(const std::map<App::Document*, std::set<App::DocumentObject*> > &objs) {
|
||||
std::ostringstream ss;
|
||||
std::vector<App::Property*> props;
|
||||
for(auto &v : objs) {
|
||||
for(auto obj : v.second) {
|
||||
props.clear();
|
||||
obj->getPropertyList(props);
|
||||
for(auto prop : props) {
|
||||
auto p = dynamic_cast<App::PropertyExpressionContainer*>(prop);
|
||||
if(!p) continue;
|
||||
for(auto &v : p->getExpressions()) {
|
||||
ss << "##@@ " << v.first.toString() << ' '
|
||||
<< obj->getFullName() << '.' << p->getName()
|
||||
<< " (" << obj->Label.getValue() << ')' << std::endl;
|
||||
ss << "##@@";
|
||||
if(v.second->comment.size()) {
|
||||
if(v.second->comment[0] == '&'
|
||||
|| v.second->comment.find('\n') != std::string::npos
|
||||
|| v.second->comment.find('\r') != std::string::npos)
|
||||
{
|
||||
std::string comment = v.second->comment;
|
||||
boost::replace_all(comment,"&","&");
|
||||
boost::replace_all(comment,"\n"," ");
|
||||
boost::replace_all(comment,"\r"," ");
|
||||
ss << '&' << comment;
|
||||
}else
|
||||
ss << v.second->comment;
|
||||
}
|
||||
ss << std::endl << v.second->toString(true) << std::endl << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QApplication::clipboard()->setText(QString::fromUtf8(ss.str().c_str()));
|
||||
}
|
||||
|
||||
void pasteExpressions() {
|
||||
std::map<App::Document*, std::map<App::PropertyExpressionContainer*,
|
||||
std::map<App::ObjectIdentifier, App::ExpressionPtr> > > exprs;
|
||||
|
||||
bool failed = false;
|
||||
std::string txt = QApplication::clipboard()->text().toUtf8().constData();
|
||||
const char *tstart = txt.c_str();
|
||||
const char *tend = tstart + txt.size();
|
||||
|
||||
static boost::regex rule("^##@@ ([^ ]+) (\\w+)#(\\w+)\\.(\\w+) [^\n]+\n##@@([^\n]*)\n");
|
||||
boost::cmatch m;
|
||||
if(!boost::regex_search(tstart,m,rule)) {
|
||||
FC_WARN("No expression header found");
|
||||
return;
|
||||
}
|
||||
boost::cmatch m2;
|
||||
bool found = true;
|
||||
for(;found;m=m2) {
|
||||
found = boost::regex_search(m[0].second,tend,m2,rule);
|
||||
|
||||
auto pathName = m.str(1);
|
||||
auto docName = m.str(2);
|
||||
auto objName = m.str(3);
|
||||
auto propName = m.str(4);
|
||||
auto comment = m.str(5);
|
||||
|
||||
App::Document *doc = App::GetApplication().getDocument(docName.c_str());
|
||||
if(!doc) {
|
||||
FC_WARN("Cannot find document '" << docName << "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto obj = doc->getObject(objName.c_str());
|
||||
if(!obj) {
|
||||
FC_WARN("Cannot find object '" << docName << '#' << objName << "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto prop = dynamic_cast<App::PropertyExpressionContainer*>(
|
||||
obj->getPropertyByName(propName.c_str()));
|
||||
if(!prop) {
|
||||
FC_WARN("Invalid property '" << docName << '#' << objName << '.' << propName << "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t len = (found?m2[0].first:tend) - m[0].second;
|
||||
try {
|
||||
App::ExpressionPtr expr(App::Expression::parse(obj,std::string(m[0].second,len)));
|
||||
if(expr && comment.size()) {
|
||||
if(comment[0] == '&') {
|
||||
expr->comment = comment.c_str()+1;
|
||||
boost::replace_all(expr->comment,"&","&");
|
||||
boost::replace_all(expr->comment," ","\n");
|
||||
boost::replace_all(expr->comment," ","\r");
|
||||
} else
|
||||
expr->comment = comment;
|
||||
}
|
||||
exprs[doc][prop][App::ObjectIdentifier::parse(obj,pathName)] = std::move(expr);
|
||||
} catch(Base::Exception &e) {
|
||||
FC_ERR(e.what() << std::endl << m[0].str());
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
if(failed) {
|
||||
QMessageBox::critical(getMainWindow(), QObject::tr("Expression error"),
|
||||
QObject::tr("Failed to parse some of the expressions.\n"
|
||||
"Please check the Report View for more details."));
|
||||
return;
|
||||
}
|
||||
|
||||
openCommand("Paste expressions");
|
||||
try {
|
||||
for(auto &v : exprs) {
|
||||
for(auto &v2 : v.second) {
|
||||
auto &expressions = v2.second;
|
||||
auto old = v2.first->getExpressions();
|
||||
for(auto it=expressions.begin(),itNext=it;it!=expressions.end();it=itNext) {
|
||||
++itNext;
|
||||
auto iter = old.find(it->first);
|
||||
if(iter != old.end() && it->second->isSame(*iter->second))
|
||||
expressions.erase(it);
|
||||
}
|
||||
if(expressions.size())
|
||||
v2.first->setExpressions(std::move(expressions));
|
||||
}
|
||||
}
|
||||
commitCommand();
|
||||
} catch (const Base::Exception& e) {
|
||||
abortCommand();
|
||||
QMessageBox::critical(getMainWindow(), QObject::tr("Failed to paste expressions"),
|
||||
QString::fromLatin1(e.what()));
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
bool isActive() {
|
||||
if(!App::GetApplication().getActiveDocument()) {
|
||||
pcActionCopyAll->setEnabled(false);
|
||||
pcActionCopySel->setEnabled(false);
|
||||
pcActionCopyActive->setEnabled(false);
|
||||
pcActionPaste->setEnabled(false);
|
||||
return true;
|
||||
}
|
||||
pcActionCopyActive->setEnabled(true);
|
||||
pcActionCopyAll->setEnabled(true);
|
||||
pcActionCopySel->setEnabled(Selection().hasSelection());
|
||||
|
||||
pcActionPaste->setEnabled(
|
||||
QApplication::clipboard()->text().startsWith(QLatin1String("##@@ ")));
|
||||
return true;
|
||||
}
|
||||
|
||||
QAction *pcActionCopyAll;
|
||||
QAction *pcActionCopySel;
|
||||
QAction *pcActionCopyActive;
|
||||
QAction *pcActionPaste;
|
||||
};
|
||||
|
||||
namespace Gui {
|
||||
|
||||
@@ -1470,6 +1748,7 @@ void CreateDocCommands(void)
|
||||
rcCmdMgr.addCommand(new StdCmdSave());
|
||||
rcCmdMgr.addCommand(new StdCmdSaveAs());
|
||||
rcCmdMgr.addCommand(new StdCmdSaveCopy());
|
||||
rcCmdMgr.addCommand(new StdCmdSaveAll());
|
||||
rcCmdMgr.addCommand(new StdCmdRevert());
|
||||
rcCmdMgr.addCommand(new StdCmdProjectInfo());
|
||||
rcCmdMgr.addCommand(new StdCmdProjectUtil());
|
||||
@@ -1491,6 +1770,7 @@ void CreateDocCommands(void)
|
||||
rcCmdMgr.addCommand(new StdCmdTransformManip());
|
||||
rcCmdMgr.addCommand(new StdCmdAlignment());
|
||||
rcCmdMgr.addCommand(new StdCmdEdit());
|
||||
rcCmdMgr.addCommand(new StdCmdExpression());
|
||||
}
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
@@ -169,18 +169,18 @@ StdCmdCloseAllWindows::StdCmdCloseAllWindows()
|
||||
sToolTipText = QT_TR_NOOP("Close all windows");
|
||||
sWhatsThis = "Std_CloseAllWindows";
|
||||
sStatusTip = QT_TR_NOOP("Close all windows");
|
||||
eType = 0;
|
||||
eType = NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdCloseAllWindows::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
getMainWindow()->closeAllWindows();
|
||||
getMainWindow()->closeAllDocuments();
|
||||
}
|
||||
|
||||
bool StdCmdCloseAllWindows::isActive(void)
|
||||
{
|
||||
return !(getMainWindow()->windows().isEmpty());
|
||||
return !(getMainWindow()->windows().isEmpty()) || App::GetApplication().getDocuments().size();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
375
src/Gui/DlgObjectSelection.cpp
Normal file
375
src/Gui/DlgObjectSelection.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018 Zheng, Lei (realthunder) <realthunder.dev@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 <QTreeWidget>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include "DlgObjectSelection.h"
|
||||
#include "Application.h"
|
||||
#include "ViewProviderDocumentObject.h"
|
||||
#include "ui_DlgObjectSelection.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("Gui",true,true);
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
/* TRANSLATOR Gui::DlgObjectSelection */
|
||||
|
||||
DlgObjectSelection::DlgObjectSelection(
|
||||
const std::vector<App::DocumentObject*> &objs, QWidget* parent, Qt::WindowFlags fl)
|
||||
: QDialog(parent, fl), ui(new Ui_DlgObjectSelection)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// make sure to show a horizontal scrollbar if needed
|
||||
#if QT_VERSION >= 0x050000
|
||||
ui->depList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
ui->depList->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||
ui->depList->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
|
||||
ui->depList->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
|
||||
ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
#else
|
||||
ui->depList->header()->setResizeMode(0, QHeaderView::ResizeToContents);
|
||||
ui->depList->header()->setResizeMode(1, QHeaderView::ResizeToContents);
|
||||
ui->depList->header()->setResizeMode(2, QHeaderView::ResizeToContents);
|
||||
ui->depList->header()->setResizeMode(3, QHeaderView::ResizeToContents);
|
||||
ui->treeWidget->header()->setResizeMode(0, QHeaderView::ResizeToContents);
|
||||
#endif
|
||||
ui->depList->header()->setStretchLastSection(false);
|
||||
ui->depList->headerItem()->setText(0, tr("Dependency"));
|
||||
ui->depList->headerItem()->setText(1, tr("Document"));
|
||||
ui->depList->headerItem()->setText(2, tr("Name"));
|
||||
ui->depList->headerItem()->setText(3, tr("State"));
|
||||
|
||||
ui->treeWidget->headerItem()->setText(0, tr("Hierarchy"));
|
||||
ui->treeWidget->header()->setStretchLastSection(false);
|
||||
|
||||
for(auto obj : App::Document::getDependencyList(objs)) {
|
||||
auto &info = objMap[obj];
|
||||
info.depItem = new QTreeWidgetItem(ui->depList);
|
||||
auto vp = Gui::Application::Instance->getViewProvider(obj);
|
||||
if(vp) info.depItem->setIcon(0, vp->getIcon());
|
||||
info.depItem->setIcon(0, vp->getIcon());
|
||||
info.depItem->setText(0, QString::fromUtf8((obj)->Label.getValue()));
|
||||
info.depItem->setText(1, QString::fromUtf8(obj->getDocument()->getName()));
|
||||
info.depItem->setText(2, QString::fromLatin1(obj->getNameInDocument()));
|
||||
info.depItem->setText(3, tr("Selected"));
|
||||
info.depItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
|
||||
info.depItem->setCheckState(0,Qt::Checked);
|
||||
}
|
||||
for(auto obj : objs) {
|
||||
auto &info = objMap[obj];
|
||||
info.items.push_back(createItem(obj,0));
|
||||
info.items.back()->setCheckState(0,Qt::Checked);
|
||||
}
|
||||
|
||||
for(auto &v : objMap) {
|
||||
for(auto obj : v.first->getOutListRecursive()) {
|
||||
if(obj == v.first)
|
||||
continue;
|
||||
auto it = objMap.find(obj);
|
||||
if(it == objMap.end())
|
||||
continue;
|
||||
v.second.outList[obj] = &it->second;
|
||||
}
|
||||
for(auto obj : v.first->getInListRecursive()) {
|
||||
if(obj == v.first)
|
||||
continue;
|
||||
auto it = objMap.find(obj);
|
||||
if(it == objMap.end())
|
||||
continue;
|
||||
v.second.inList[obj] = &it->second;
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)),
|
||||
this, SLOT(onItemExpanded(QTreeWidgetItem*)));
|
||||
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this, SLOT(onItemChanged(QTreeWidgetItem*,int)));
|
||||
connect(ui->depList, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
this, SLOT(onItemChanged(QTreeWidgetItem*,int)));
|
||||
connect(ui->treeWidget, SIGNAL(itemSelectionChanged()),
|
||||
this, SLOT(onItemSelectionChanged()));
|
||||
connect(ui->depList, SIGNAL(itemSelectionChanged()),
|
||||
this, SLOT(onDepSelectionChanged()));
|
||||
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the object and frees any allocated resources
|
||||
*/
|
||||
DlgObjectSelection::~DlgObjectSelection()
|
||||
{
|
||||
// no need to delete child widgets, Qt does it all for us
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QTreeWidgetItem *DlgObjectSelection::createItem(App::DocumentObject *obj, QTreeWidgetItem *parent) {
|
||||
QTreeWidgetItem* item;
|
||||
if(parent)
|
||||
item = new QTreeWidgetItem(parent);
|
||||
else
|
||||
item = new QTreeWidgetItem(ui->treeWidget);
|
||||
auto vp = Gui::Application::Instance->getViewProvider(obj);
|
||||
if(vp) item->setIcon(0, vp->getIcon());
|
||||
item->setText(0, QString::fromUtf8((obj)->Label.getValue()));
|
||||
item->setData(0, Qt::UserRole, QByteArray(obj->getDocument()->getName()));
|
||||
item->setData(0, Qt::UserRole+1, QByteArray(obj->getNameInDocument()));
|
||||
item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
|
||||
std::set<App::DocumentObject *> outSet;
|
||||
for(auto o : obj->getOutList()) {
|
||||
if(objMap.count(o))
|
||||
outSet.insert(o);
|
||||
}
|
||||
if(outSet.empty())
|
||||
return item;
|
||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
if(!parent) {
|
||||
bool populate = false;
|
||||
for(auto o : outSet) {
|
||||
if(objMap[o].items.empty()) {
|
||||
populate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!populate)
|
||||
return item;
|
||||
for(auto o : outSet) {
|
||||
auto &info = objMap[o];
|
||||
info.items.push_back(createItem(o,item));
|
||||
info.items.back()->setCheckState(0,info.checkState);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
class SignalBlocker {
|
||||
public:
|
||||
SignalBlocker(QTreeWidget *treeWidget)
|
||||
:treeWidget(treeWidget)
|
||||
{
|
||||
treeWidget->blockSignals(true);
|
||||
}
|
||||
~SignalBlocker() {
|
||||
treeWidget->blockSignals(false);
|
||||
}
|
||||
QTreeWidget *treeWidget;
|
||||
};
|
||||
|
||||
App::DocumentObject *DlgObjectSelection::objFromItem(QTreeWidgetItem *item) {
|
||||
std::string name;
|
||||
std::string docName;
|
||||
if(item->treeWidget() == ui->treeWidget) {
|
||||
docName = item->data(0,Qt::UserRole).toByteArray().constData();
|
||||
name = item->data(0,Qt::UserRole+1).toByteArray().constData();
|
||||
}else{
|
||||
docName = qPrintable(item->text(1));
|
||||
name = qPrintable(item->text(2));
|
||||
}
|
||||
auto doc = App::GetApplication().getDocument(docName.c_str());
|
||||
if(!doc) return 0;
|
||||
return doc->getObject(name.c_str());
|
||||
}
|
||||
|
||||
void DlgObjectSelection::onItemExpanded(QTreeWidgetItem * item) {
|
||||
if(item->childCount())
|
||||
return;
|
||||
auto obj = objFromItem(item);
|
||||
if(!obj)
|
||||
return;
|
||||
SignalBlocker blocker(ui->treeWidget);
|
||||
std::set<App::DocumentObject *> outSet;
|
||||
for(auto o : obj->getOutList()) {
|
||||
if(!objMap.count(obj) || !outSet.insert(o).second)
|
||||
continue;
|
||||
auto &info = objMap[o];
|
||||
info.items.push_back(createItem(o,item));
|
||||
info.items.back()->setCheckState(0,info.checkState);
|
||||
}
|
||||
}
|
||||
|
||||
void DlgObjectSelection::onItemChanged(QTreeWidgetItem * item, int column) {
|
||||
if(column) return;
|
||||
auto obj = objFromItem(item);
|
||||
if(!obj) return;
|
||||
auto state = item->checkState(0);
|
||||
auto it = objMap.find(obj);
|
||||
if(it == objMap.end() || state == it->second.checkState)
|
||||
return;
|
||||
SignalBlocker blocker(ui->treeWidget);
|
||||
SignalBlocker blocker2(ui->depList);
|
||||
auto &info = it->second;
|
||||
info.checkState = state;
|
||||
|
||||
if(item == info.depItem) {
|
||||
for(auto item : info.items)
|
||||
item->setCheckState(0,state);
|
||||
}else{
|
||||
info.depItem->setCheckState(0,state);
|
||||
info.depItem->setText(3,state==Qt::Checked?tr("Selected"):QString());
|
||||
}
|
||||
|
||||
if(state == Qt::Unchecked) {
|
||||
for(auto &v : info.outList) {
|
||||
if(info.inList.count(v.first)) {
|
||||
// This indicates a dependency loop. The check here is so that
|
||||
// object selection still works despite of the loop
|
||||
continue;
|
||||
}
|
||||
if(v.second->checkState == Qt::Unchecked)
|
||||
continue;
|
||||
v.second->checkState = Qt::Unchecked;
|
||||
v.second->depItem->setText(3,QString());
|
||||
v.second->depItem->setCheckState(0,Qt::Unchecked);
|
||||
for(auto item : v.second->items)
|
||||
item->setCheckState(0,Qt::Unchecked);
|
||||
}
|
||||
for(auto &v : info.inList) {
|
||||
if(v.second->checkState != Qt::Checked)
|
||||
continue;
|
||||
v.second->checkState = Qt::PartiallyChecked;
|
||||
v.second->depItem->setText(3,tr("Partial"));
|
||||
v.second->depItem->setCheckState(0,Qt::PartiallyChecked);
|
||||
for(auto item : v.second->items)
|
||||
item->setCheckState(0,Qt::PartiallyChecked);
|
||||
}
|
||||
return;
|
||||
} else if(state == Qt::Checked) {
|
||||
for(auto &v : info.outList) {
|
||||
if(info.inList.count(v.first)) {
|
||||
// This indicates a dependency loop. The check here is so that
|
||||
// object selection still works despite of the loop
|
||||
continue;
|
||||
}
|
||||
if(v.second->checkState == Qt::Checked)
|
||||
continue;
|
||||
v.second->checkState = Qt::Checked;
|
||||
v.second->depItem->setText(3,tr("Selected"));
|
||||
v.second->depItem->setCheckState(0,Qt::Checked);
|
||||
for(auto item : v.second->items)
|
||||
item->setCheckState(0,Qt::Checked);
|
||||
}
|
||||
bool touched;
|
||||
do {
|
||||
touched = false;
|
||||
for(auto &v : info.inList) {
|
||||
if(v.second->checkState != Qt::PartiallyChecked)
|
||||
continue;
|
||||
bool partial = false;
|
||||
for(auto &vv : v.second->outList) {
|
||||
if(vv.second->checkState != Qt::Checked) {
|
||||
partial = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(partial)
|
||||
continue;
|
||||
touched = true;
|
||||
v.second->checkState = Qt::Checked;
|
||||
v.second->depItem->setText(3,tr("Selected"));
|
||||
v.second->depItem->setCheckState(0,Qt::Checked);
|
||||
for(auto item : v.second->items)
|
||||
item->setCheckState(0,Qt::Checked);
|
||||
}
|
||||
}while(touched);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> DlgObjectSelection::getSelections() const {
|
||||
std::vector<App::DocumentObject*> res;
|
||||
for(auto &v : objMap) {
|
||||
if(v.second.checkState != Qt::Unchecked)
|
||||
res.push_back(v.first);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void DlgObjectSelection::onItemSelectionChanged() {
|
||||
SignalBlocker block2(ui->treeWidget);
|
||||
SignalBlocker block(ui->depList);
|
||||
QTreeWidgetItem *scroll=0;
|
||||
for(auto &v : objMap) {
|
||||
auto &info = v.second;
|
||||
auto it = sels.find(v.first);
|
||||
auto selected = it==sels.end();
|
||||
for(auto item : info.items) {
|
||||
if(selected == item->isSelected()) {
|
||||
for(auto item : info.items)
|
||||
item->setSelected(selected);
|
||||
scroll = info.depItem;
|
||||
info.depItem->setSelected(selected);
|
||||
scroll = info.depItem;
|
||||
if(!selected)
|
||||
sels.erase(it);
|
||||
else
|
||||
sels.insert(v.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(scroll)
|
||||
ui->depList->scrollToItem(scroll);
|
||||
}
|
||||
|
||||
void DlgObjectSelection::onDepSelectionChanged() {
|
||||
SignalBlocker block2(ui->treeWidget);
|
||||
SignalBlocker block(ui->depList);
|
||||
QTreeWidgetItem *scroll=0;
|
||||
for(auto &v : objMap) {
|
||||
auto &info = v.second;
|
||||
auto it = sels.find(v.first);
|
||||
auto selected = it==sels.end();
|
||||
if(info.depItem->isSelected()==selected) {
|
||||
for(auto item : info.items) {
|
||||
scroll = item;
|
||||
item->setSelected(selected);
|
||||
}
|
||||
if(!selected)
|
||||
sels.erase(it);
|
||||
else {
|
||||
sels.insert(v.first);
|
||||
for(auto item : info.items) {
|
||||
for(auto parent=item->parent();parent;parent=parent->parent())
|
||||
parent->setExpanded(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(scroll)
|
||||
ui->treeWidget->scrollToItem(scroll);
|
||||
}
|
||||
|
||||
void DlgObjectSelection::accept() {
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void DlgObjectSelection::reject() {
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
#include "moc_DlgObjectSelection.cpp"
|
||||
70
src/Gui/DlgObjectSelection.h
Normal file
70
src/Gui/DlgObjectSelection.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018 Zheng, Lei (realthunder) <realthunder.dev@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 *
|
||||
* *
|
||||
****************************************************************************/
|
||||
#ifndef GUI_DLGOBJECTSELECTION_H
|
||||
#define GUI_DLGOBJECTSELECTION_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Gui {
|
||||
|
||||
class Ui_DlgObjectSelection;
|
||||
class GuiExport DlgObjectSelection : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DlgObjectSelection(const std::vector<App::DocumentObject*> &objs,
|
||||
QWidget* parent = 0, Qt::WindowFlags fl = 0);
|
||||
~DlgObjectSelection();
|
||||
|
||||
std::vector<App::DocumentObject*> getSelections() const;
|
||||
void accept();
|
||||
void reject();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onItemExpanded(QTreeWidgetItem * item);
|
||||
void onItemChanged(QTreeWidgetItem * item, int);
|
||||
void onItemSelectionChanged();
|
||||
void onDepSelectionChanged();
|
||||
|
||||
private:
|
||||
QTreeWidgetItem *createItem(App::DocumentObject *obj, QTreeWidgetItem *parent);
|
||||
App::DocumentObject *objFromItem(QTreeWidgetItem *item);
|
||||
|
||||
private:
|
||||
struct Info {
|
||||
std::map<App::DocumentObject *, Info*> inList;
|
||||
std::map<App::DocumentObject *, Info*> outList;
|
||||
std::vector<QTreeWidgetItem*> items;
|
||||
QTreeWidgetItem *depItem = 0;
|
||||
Qt::CheckState checkState = Qt::Checked;
|
||||
};
|
||||
std::map<App::DocumentObject *,Info> objMap;
|
||||
Ui_DlgObjectSelection* ui;
|
||||
std::set<App::DocumentObject*> sels;
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
|
||||
#endif // GUI_DLGOBJECTSELECTION_H
|
||||
|
||||
156
src/Gui/DlgObjectSelection.ui
Normal file
156
src/Gui/DlgObjectSelection.ui
Normal file
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Gui::DlgObjectSelection</class>
|
||||
<widget class="QDialog" name="Gui::DlgObjectSelection">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>621</width>
|
||||
<height>383</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Object selection</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>The selected objects contain other dependencies. Please select which objects to export. All dependencies are auto selected by default.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="headerCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="headerShowSortIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QTreeWidget" name="depList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<attribute name="headerShowSortIndicator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">3</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -407,6 +407,19 @@ automatically run a file recovery when it is started.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="Gui::PrefCheckBox" name="prefPartialLoading">
|
||||
<property name="text">
|
||||
<string>Disable partial loading of external linked objects</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>NoPartialLoading</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Document</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -73,6 +73,7 @@ void DlgSettingsDocumentImp::saveSettings()
|
||||
prefSaveBackupFiles->onSave();
|
||||
prefCountBackupFiles->onSave();
|
||||
prefDuplicateLabel->onSave();
|
||||
prefPartialLoading->onSave();
|
||||
prefLicenseType->onSave();
|
||||
prefLicenseUrl->onSave();
|
||||
prefAuthor->onSave();
|
||||
@@ -102,6 +103,7 @@ void DlgSettingsDocumentImp::loadSettings()
|
||||
prefSaveBackupFiles->onRestore();
|
||||
prefCountBackupFiles->onRestore();
|
||||
prefDuplicateLabel->onRestore();
|
||||
prefPartialLoading->onRestore();
|
||||
prefLicenseType->onRestore();
|
||||
prefLicenseUrl->onRestore();
|
||||
prefAuthor->onRestore();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -84,6 +84,13 @@ protected:
|
||||
void slotFinishRestoreDocument(const App::Document&);
|
||||
void slotUndoDocument(const App::Document&);
|
||||
void slotRedoDocument(const App::Document&);
|
||||
void slotShowHidden(const App::Document&);
|
||||
void slotFinishImportObjects(const std::vector<App::DocumentObject*> &);
|
||||
void slotFinishRestoreObject(const App::DocumentObject &obj);
|
||||
void slotRecomputed(const App::Document&);
|
||||
void slotSkipRecompute(const App::Document &doc, const std::vector<App::DocumentObject*> &objs);
|
||||
void slotTouchedObject(const App::DocumentObject &);
|
||||
void slotChangePropertyEditor(const App::Document&, const App::Property &);
|
||||
//@}
|
||||
|
||||
void addViewProvider(Gui::ViewProviderDocumentObject*);
|
||||
@@ -108,12 +115,18 @@ public:
|
||||
/// signal on leaving edit mode
|
||||
mutable boost::signals2::signal<void (const Gui::ViewProviderDocumentObject&)> signalResetEdit;
|
||||
/// signal on changed Object, the 2nd argument is the highlite mode to use
|
||||
mutable boost::signals2::signal<void (const Gui::ViewProviderDocumentObject&,
|
||||
const Gui::HighlightMode&,
|
||||
bool)> signalHighlightObject;
|
||||
mutable boost::signals2::signal<void (const Gui::ViewProviderDocumentObject&,
|
||||
const Gui::HighlightMode&,
|
||||
bool,
|
||||
App::DocumentObject *parent,
|
||||
const char *subname)> signalHighlightObject;
|
||||
/// signal on changed Object, the 2nd argument is the highlite mode to use
|
||||
mutable boost::signals2::signal<void (const Gui::ViewProviderDocumentObject&,
|
||||
const Gui::TreeItemMode&)> signalExpandObject;
|
||||
const Gui::TreeItemMode&,
|
||||
App::DocumentObject *parent,
|
||||
const char *subname)> signalExpandObject;
|
||||
/// signal on changed ShowInTree property in view provider
|
||||
mutable boost::signals2::signal<void (const Gui::ViewProviderDocumentObject&)> signalShowItem;
|
||||
/// signal on scrolling to an object
|
||||
mutable boost::signals2::signal<void (const Gui::ViewProviderDocumentObject&)> signalScrollToObject;
|
||||
/// signal on undo Document
|
||||
@@ -133,6 +146,8 @@ public:
|
||||
bool saveAs(void);
|
||||
/// Save a copy of the document under a new file name
|
||||
bool saveCopy(void);
|
||||
/// Save all open document
|
||||
static void saveAll();
|
||||
/// This method is used to save properties or very small amounts of data to an XML document.
|
||||
virtual void Save (Base::Writer &writer) const;
|
||||
/// This method is used to restore properties from an XML document.
|
||||
@@ -164,7 +179,7 @@ public:
|
||||
Gui::MDIView* getViewOfViewProvider(Gui::ViewProvider*) const;
|
||||
Gui::MDIView* getViewOfNode(SoNode*) const;
|
||||
/// Create a new view
|
||||
void createView(const Base::Type& typeId);
|
||||
MDIView *createView(const Base::Type& typeId);
|
||||
/// Create a clone of the given view
|
||||
Gui::MDIView* cloneView(Gui::MDIView*);
|
||||
/** send messages to the active view
|
||||
@@ -184,7 +199,11 @@ public:
|
||||
/// Detach a view (get called by the MDIView destructor)
|
||||
void detachView(Gui::BaseView* pcView, bool bPassiv=false);
|
||||
/// helper for selection
|
||||
ViewProvider* getViewProviderByPathFromTail(SoPath * path) const;
|
||||
ViewProviderDocumentObject* getViewProviderByPathFromTail(SoPath * path) const;
|
||||
/// helper for selection
|
||||
ViewProviderDocumentObject* getViewProviderByPathFromHead(SoPath * path) const;
|
||||
/// Get all view providers along the path and the corresponding node index in the path
|
||||
std::vector<std::pair<ViewProviderDocumentObject*,int> > getViewProvidersByPath(SoPath * path) const;
|
||||
/// call update on all attached views
|
||||
void onUpdate(void);
|
||||
/// call relabel to all attached views
|
||||
@@ -195,10 +214,13 @@ public:
|
||||
std::list<MDIView*> getMDIViewsOfType(const Base::Type& typeId) const;
|
||||
//@}
|
||||
|
||||
MDIView *setActiveView(ViewProviderDocumentObject *vp=0, Base::Type typeId = Base::Type());
|
||||
|
||||
/** @name View provider handling */
|
||||
//@{
|
||||
/// Get the view provider for that object
|
||||
ViewProvider* getViewProvider(const App::DocumentObject *) const;
|
||||
ViewProviderDocumentObject *getViewProvider(SoNode *node) const;
|
||||
/// set an annotation view provider
|
||||
void setAnnotationViewProvider(const char* name, ViewProvider *pcProvider);
|
||||
/// get an annotation view provider
|
||||
@@ -228,6 +250,12 @@ public:
|
||||
std::string *subname=0, int *mode=0, std::string *subElement=0) const;
|
||||
/// set the in edit ViewProvider subname reference
|
||||
void setInEdit(ViewProviderDocumentObject *parentVp, const char *subname);
|
||||
/** Add or remove view provider from scene graphs of all views
|
||||
*
|
||||
* It calls ViewProvider::canAddToSceneGraph() to decide whether to add the
|
||||
* view provider or remove it
|
||||
*/
|
||||
void toggleInSceneGraph(ViewProvider *vp);
|
||||
//@}
|
||||
|
||||
/** @name methods for the UNDO REDO handling */
|
||||
@@ -248,10 +276,16 @@ public:
|
||||
void undo(int iSteps);
|
||||
/// Will REDO one or more steps
|
||||
void redo(int iSteps) ;
|
||||
/** Check if the document is performing undo/redo transaction
|
||||
*
|
||||
* Unlike App::Document::isPerformingTransaction(), Gui::Document will
|
||||
* report transacting when triggering grouped undo/redo in other documents
|
||||
*/
|
||||
bool isPerformingTransaction() const;
|
||||
//@}
|
||||
|
||||
/// handles the application close event
|
||||
bool canClose();
|
||||
bool canClose(bool checkModify=true, bool checkLink=false);
|
||||
bool isLastView(void);
|
||||
|
||||
/// called by Application before being deleted
|
||||
@@ -259,18 +293,25 @@ public:
|
||||
|
||||
virtual PyObject *getPyObject(void);
|
||||
|
||||
const std::string &getCameraSettings() const;
|
||||
void saveCameraSettings(const char *);
|
||||
|
||||
protected:
|
||||
// pointer to the python class
|
||||
Gui::DocumentPy *_pcDocPy;
|
||||
|
||||
private:
|
||||
//handles the scene graph nodes to correctly group child and parents
|
||||
void handleChildren3D(ViewProvider* viewProvider);
|
||||
void rebuildRootNodes();
|
||||
void handleChildren3D(ViewProvider* viewProvider, bool deleting=false);
|
||||
|
||||
/// Check other documents for the same transaction ID
|
||||
bool checkTransactionID(bool undo, int iSteps);
|
||||
|
||||
struct DocumentP* d;
|
||||
static int _iDocCount;
|
||||
|
||||
std::string cameraSettings;
|
||||
|
||||
/** @name attributes for the UNDO REDO facility
|
||||
*/
|
||||
//@{
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<Methode Name="setEdit">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
setEdit([String:Name|ViewProvider|DocumentObject]|,mod)
|
||||
setEdit([String:Name|ViewProvider|DocumentObject]|,mod,subname=None)
|
||||
Set the given object in edit mode.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
@@ -101,6 +101,15 @@
|
||||
<UserDocu>scrollToTreeItem(ViewObject) - scroll the tree view to the item of a view object</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="toggleInSceneGraph">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
toggleInSceneGraph(ViewObject)
|
||||
|
||||
Add or remove view object from scene graph of all views depending on its canAddToSceneGraph()
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Attribute Name="ActiveObject" ReadOnly="false">
|
||||
<Documentation>
|
||||
<UserDocu>The active object of the document</UserDocu>
|
||||
@@ -113,11 +122,35 @@
|
||||
</Documentation>
|
||||
<Parameter Name="ActiveView" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="EditingTransform">
|
||||
<Documentation>
|
||||
<UserDocu>The editing transformation matrix</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="EditingTransform" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="InEditInfo">
|
||||
<Documentation>
|
||||
<UserDocu>A tuple(obj,subname,subElement,editMode) of editing object reference, or None if no object is in edit</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="InEditInfo" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="EditMode" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Current edit mode. Only meaningful when there is a current object in edit</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="EditMode" Type="Int" />
|
||||
</Attribute>
|
||||
<Attribute Name="Document" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>The related App document to this Gui document</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Document" Type="Object" />
|
||||
</Attribute>
|
||||
<Attribute Name="Transacting" ReadOnly="true">
|
||||
<Documentation>
|
||||
<UserDocu>Indicate whether the document is undoing/redoing</UserDocu>
|
||||
</Documentation>
|
||||
<Parameter Name="Transacting" Type="Boolean" />
|
||||
</Attribute>
|
||||
<Attribute Name="Modified" ReadOnly="true">
|
||||
<Documentation>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <App/Document.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Document.h"
|
||||
#include "MergeDocuments.h"
|
||||
#include "ViewProviderExtern.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include "Tree.h"
|
||||
#include "ViewProviderDocumentObject.h"
|
||||
#include "ViewProviderDocumentObjectPy.h"
|
||||
#include "ViewProviderPy.h"
|
||||
#include "ViewProviderDocumentObjectPy.h"
|
||||
|
||||
@@ -103,38 +105,42 @@ PyObject* DocumentPy::setEdit(PyObject *args)
|
||||
{
|
||||
char *psFeatStr;
|
||||
int mod = 0;
|
||||
char *subname = 0;
|
||||
ViewProvider *vp = 0;
|
||||
App::DocumentObject *obj = 0;
|
||||
|
||||
// by name
|
||||
if (PyArg_ParseTuple(args, "s|i;Name of the object to edit has to be given!", &psFeatStr,&mod)) {
|
||||
App::DocumentObject * obj = getDocumentPtr()->getDocument()->getObject(psFeatStr);
|
||||
if (PyArg_ParseTuple(args, "s|is;Name of the object to edit has to be given!", &psFeatStr,&mod,&subname)) {
|
||||
obj = getDocumentPtr()->getDocument()->getObject(psFeatStr);
|
||||
if (!obj) {
|
||||
PyErr_Format(Base::BaseExceptionFreeCADError, "No such object found in document: '%s'", psFeatStr);
|
||||
return 0;
|
||||
}
|
||||
}else{
|
||||
PyErr_Clear();
|
||||
PyObject *pyObj;
|
||||
if(!PyArg_ParseTuple(args, "O|is", &pyObj,&mod,&subname))
|
||||
return 0;
|
||||
|
||||
bool ok = getDocumentPtr()->setEdit(getDocumentPtr()->getViewProvider(obj),mod);
|
||||
return PyBool_FromLong(ok ? 1 : 0);
|
||||
if(PyObject_TypeCheck(pyObj,&App::DocumentObjectPy::Type))
|
||||
obj = static_cast<App::DocumentObjectPy*>(pyObj)->getDocumentObjectPtr();
|
||||
else if(PyObject_TypeCheck(pyObj,&ViewProviderPy::Type))
|
||||
vp = static_cast<ViewProviderPy*>(pyObj)->getViewProviderPtr();
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError,"Expect the first argument to be string|DocObject|ViewObject");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// by document object
|
||||
PyErr_Clear();
|
||||
PyObject *docObj;
|
||||
if (PyArg_ParseTuple(args, "O!|i", &(App::DocumentObjectPy::Type), &docObj,&mod)) {
|
||||
App::DocumentObject * obj = static_cast<App::DocumentObjectPy*>(docObj)->getDocumentObjectPtr();
|
||||
bool ok = getDocumentPtr()->setEdit(getDocumentPtr()->getViewProvider(obj),mod);
|
||||
return PyBool_FromLong(ok ? 1 : 0);
|
||||
if(!vp) {
|
||||
if(!obj || !obj->getNameInDocument() || !(vp=Application::Instance->getViewProvider(obj))) {
|
||||
PyErr_SetString(PyExc_ValueError,"Invalid document object");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// by view provider
|
||||
PyErr_Clear();
|
||||
if (PyArg_ParseTuple(args, "O!|i", &(Gui::ViewProviderPy::Type), &docObj,&mod)) {
|
||||
Gui::ViewProvider * view = static_cast<Gui::ViewProviderPy*>(docObj)->getViewProviderPtr();
|
||||
bool ok = getDocumentPtr()->setEdit(view,mod);
|
||||
return PyBool_FromLong(ok ? 1 : 0);
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_TypeError, "Either string, document object or view provider expected.");
|
||||
return 0;
|
||||
bool ok = getDocumentPtr()->setEdit(vp,mod,subname);
|
||||
return PyBool_FromLong(ok ? 1 : 0);
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::getInEdit(PyObject *args)
|
||||
@@ -289,22 +295,32 @@ PyObject* DocumentPy::mergeProject(PyObject *args)
|
||||
PyObject* DocumentPy::toggleTreeItem(PyObject *args)
|
||||
{
|
||||
PyObject *object=0;
|
||||
const char *subname=0;
|
||||
int mod = 0;
|
||||
if (PyArg_ParseTuple(args,"O!|i",&(App::DocumentObjectPy::Type), &object,&mod)) {
|
||||
if (PyArg_ParseTuple(args,"O!|is",&(App::DocumentObjectPy::Type), &object,&mod,&subname)) {
|
||||
App::DocumentObject* Object = static_cast<App::DocumentObjectPy*>(object)->getDocumentObjectPtr();
|
||||
// Should be set!
|
||||
assert(Object);
|
||||
|
||||
App::DocumentObject *parent = 0;
|
||||
if(subname) {
|
||||
auto sobj = Object->getSubObject(subname);
|
||||
if(!sobj)
|
||||
throw Py::RuntimeError("Sub-object not found");
|
||||
parent = Object;
|
||||
Object = sobj;
|
||||
}
|
||||
|
||||
// get the gui document of the Assembly Item
|
||||
//ActiveAppDoc = Item->getDocument();
|
||||
//ActiveGuiDoc = Gui::Application::Instance->getDocument(getDocumentPtr());
|
||||
Gui::ViewProviderDocumentObject* ActiveVp = dynamic_cast<Gui::ViewProviderDocumentObject*> (getDocumentPtr()->getViewProvider(Object));
|
||||
assert(ActiveVp);
|
||||
switch(mod) {
|
||||
case 0: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::ToggleItem); break;
|
||||
case 1: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::CollapseItem); break;
|
||||
case 2: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::ExpandItem); break;
|
||||
case 3: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::ExpandPath); break;
|
||||
case 0: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::ToggleItem,parent,subname); break;
|
||||
case 1: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::CollapseItem,parent,subname); break;
|
||||
case 2: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::ExpandItem,parent,subname); break;
|
||||
case 3: getDocumentPtr()->signalExpandObject(*ActiveVp,Gui::ExpandPath,parent,subname); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -324,6 +340,16 @@ PyObject* DocumentPy::scrollToTreeItem(PyObject *args)
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::toggleInSceneGraph(PyObject *args) {
|
||||
PyObject *view;
|
||||
if (!PyArg_ParseTuple(args,"O!",&(Gui::ViewProviderPy::Type), &view))
|
||||
return 0;
|
||||
|
||||
Gui::ViewProvider* vp = static_cast<ViewProviderPy*>(view)->getViewProviderPtr();
|
||||
getDocumentPtr()->toggleInSceneGraph(vp);
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
Py::Object DocumentPy::getActiveObject(void) const
|
||||
{
|
||||
App::DocumentObject *object = getDocumentPtr()->getDocument()->getActiveObject();
|
||||
@@ -367,6 +393,49 @@ Py::Object DocumentPy::getDocument(void) const
|
||||
}
|
||||
}
|
||||
|
||||
Py::Object DocumentPy::getEditingTransform(void) const {
|
||||
return Py::Object(new Base::MatrixPy(new Base::Matrix4D(
|
||||
getDocumentPtr()->getEditingTransform())));
|
||||
}
|
||||
|
||||
void DocumentPy::setEditingTransform(Py::Object arg) {
|
||||
if(!PyObject_TypeCheck(arg.ptr(),&Base::MatrixPy::Type))
|
||||
throw Py::TypeError("Expecting type of matrix");
|
||||
getDocumentPtr()->setEditingTransform(
|
||||
*static_cast<Base::MatrixPy*>(arg.ptr())->getMatrixPtr());
|
||||
}
|
||||
|
||||
Py::Object DocumentPy::getInEditInfo(void) const {
|
||||
ViewProviderDocumentObject *vp = 0;
|
||||
std::string subname,subelement;
|
||||
int mode = 0;
|
||||
getDocumentPtr()->getInEdit(&vp,&subname,&mode,&subelement);
|
||||
if(!vp || !vp->getObject() || !vp->getObject()->getNameInDocument())
|
||||
return Py::None();
|
||||
return Py::TupleN(Py::Object(vp->getObject()->getPyObject(),true),
|
||||
Py::String(subname),Py::String(subelement),Py::Int(mode));
|
||||
}
|
||||
|
||||
void DocumentPy::setInEditInfo(Py::Object arg) {
|
||||
PyObject *pyobj = 0;
|
||||
const char *subname = 0;
|
||||
if (!PyArg_ParseTuple(arg.ptr(), "O!s",
|
||||
&Gui::ViewProviderDocumentObjectPy::Type, &pyobj,&subname))
|
||||
throw Py::Exception();
|
||||
getDocumentPtr()->setInEdit(static_cast<ViewProviderDocumentObjectPy*>(
|
||||
pyobj)->getViewProviderDocumentObjectPtr(),subname);
|
||||
}
|
||||
|
||||
Py::Int DocumentPy::getEditMode(void) const {
|
||||
int mode = -1;
|
||||
getDocumentPtr()->getInEdit(0,0,&mode);
|
||||
return Py::Int(mode);
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getTransacting() const {
|
||||
return Py::Boolean(getDocumentPtr()->isPerformingTransaction());
|
||||
}
|
||||
|
||||
Py::Boolean DocumentPy::getModified(void) const
|
||||
{
|
||||
return Py::Boolean(getDocumentPtr()->isModified());
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#include <Inventor/draggers/SoDragger.h>
|
||||
#include <Inventor/draggers/SoTransformerDragger.h>
|
||||
#include <Inventor/draggers/SoTrackballDragger.h>
|
||||
#include <Inventor/draggers/SoCenterballDragger.h>
|
||||
#include <Inventor/elements/SoCoordinateElement.h>
|
||||
#include <Inventor/elements/SoCreaseAngleElement.h>
|
||||
#include <Inventor/elements/SoGLCacheContextElement.h>
|
||||
@@ -76,6 +77,7 @@
|
||||
#include <Inventor/elements/SoViewportRegionElement.h>
|
||||
#include <Inventor/elements/SoViewVolumeElement.h>
|
||||
#include <Inventor/elements/SoModelMatrixElement.h>
|
||||
#include <Inventor/elements/SoTextureEnabledElement.h>
|
||||
#include <Inventor/engines/SoComposeVec3f.h>
|
||||
#include <Inventor/engines/SoComposeRotationFromTo.h>
|
||||
#include <Inventor/engines/SoComposeRotation.h>
|
||||
@@ -114,6 +116,7 @@
|
||||
#include <Inventor/nodes/SoImage.h>
|
||||
#include <Inventor/nodes/SoIndexedFaceSet.h>
|
||||
#include <Inventor/nodes/SoIndexedLineSet.h>
|
||||
#include <Inventor/nodes/SoIndexedPointSet.h>
|
||||
#include <Inventor/nodes/SoIndexedTriangleStripSet.h>
|
||||
#include <Inventor/nodes/SoLightModel.h>
|
||||
#include <Inventor/nodes/SoLineSet.h>
|
||||
|
||||
@@ -49,6 +49,7 @@ TYPESYSTEM_SOURCE_ABSTRACT(Gui::MDIView,Gui::BaseView);
|
||||
|
||||
MDIView::MDIView(Gui::Document* pcDocument,QWidget* parent, Qt::WindowFlags wflags)
|
||||
: QMainWindow(parent, wflags), BaseView(pcDocument),currentMode(Child), wstate(Qt::WindowNoState)
|
||||
, ActiveObjects(pcDocument)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
@@ -167,7 +168,7 @@ bool MDIView::canClose(void)
|
||||
{
|
||||
if (!bIsPassive && getGuiDocument() && getGuiDocument()->isLastView()) {
|
||||
this->setFocus(); // raises the view to front
|
||||
return (getGuiDocument()->canClose());
|
||||
return (getGuiDocument()->canClose(true,true));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -284,7 +285,7 @@ void MDIView::setCurrentViewMode(ViewMode mode)
|
||||
{
|
||||
if (this->currentMode == Child) {
|
||||
if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
|
||||
getMainWindow()->removeWindow(this);
|
||||
getMainWindow()->removeWindow(this,false);
|
||||
setWindowFlags(windowFlags() | Qt::Window);
|
||||
setParent(0, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
|
||||
Qt::WindowMinMaxButtonsHint);
|
||||
@@ -314,7 +315,7 @@ void MDIView::setCurrentViewMode(ViewMode mode)
|
||||
{
|
||||
if (this->currentMode == Child) {
|
||||
if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
|
||||
getMainWindow()->removeWindow(this);
|
||||
getMainWindow()->removeWindow(this,false);
|
||||
setWindowFlags(windowFlags() | Qt::Window);
|
||||
setParent(0, Qt::Window);
|
||||
showFullScreen();
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <algorithm>
|
||||
# include <QApplication>
|
||||
# include <QThread>
|
||||
# include <QBuffer>
|
||||
# include <QByteArray>
|
||||
# include <QClipboard>
|
||||
@@ -114,6 +115,9 @@
|
||||
#include "SpaceballEvent.h"
|
||||
#include "View3DInventor.h"
|
||||
#include "View3DInventorViewer.h"
|
||||
#include "DlgObjectSelection.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("MainWindow",false,true,true);
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
#define slots
|
||||
@@ -130,12 +134,43 @@ MainWindow* MainWindow::instance = 0L;
|
||||
|
||||
namespace Gui {
|
||||
|
||||
/**
|
||||
* The CustomMessageEvent class is used to send messages as events in the methods
|
||||
* Error(), Warning() and Message() of the StatusBarObserver class to the main window
|
||||
* to display them on the status bar instead of printing them directly to the status bar.
|
||||
*
|
||||
* This makes the usage of StatusBarObserver thread-safe.
|
||||
* @author Werner Mayer
|
||||
*/
|
||||
class CustomMessageEvent : public QEvent
|
||||
{
|
||||
public:
|
||||
enum Type {None, Err, Wrn, Pane, Msg, Log, Tmp};
|
||||
CustomMessageEvent(Type t, const QString& s, int timeout=0)
|
||||
: QEvent(QEvent::User), _type(t), msg(s), _timeout(timeout)
|
||||
{ }
|
||||
~CustomMessageEvent()
|
||||
{ }
|
||||
Type type() const
|
||||
{ return _type; }
|
||||
const QString& message() const
|
||||
{ return msg; }
|
||||
int timeout() const
|
||||
{ return _timeout; }
|
||||
private:
|
||||
Type _type;
|
||||
QString msg;
|
||||
int _timeout;
|
||||
};
|
||||
|
||||
// -------------------------------------
|
||||
// Pimpl class
|
||||
struct MainWindowP
|
||||
{
|
||||
QLabel* sizeLabel;
|
||||
QLabel* actionLabel;
|
||||
QTimer* actionTimer;
|
||||
QTimer* statusTimer;
|
||||
QTimer* activityTimer;
|
||||
QTimer* visibleTimer;
|
||||
QMdiArea* mdiArea;
|
||||
@@ -146,6 +181,8 @@ struct MainWindowP
|
||||
bool whatsthis;
|
||||
QString whatstext;
|
||||
Assistant* assistant;
|
||||
int currentStatusType = 100;
|
||||
int actionUpdateDelay = 0;
|
||||
QMap<QString, QPointer<UrlHandler> > urlHandler;
|
||||
};
|
||||
|
||||
@@ -278,6 +315,9 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
||||
d->mdiArea->setBackground(QBrush(QColor(160,160,160)));
|
||||
setCentralWidget(d->mdiArea);
|
||||
|
||||
statusBar()->setObjectName(QString::fromLatin1("statusBar"));
|
||||
connect(statusBar(), SIGNAL(messageChanged(const QString &)), this, SLOT(statusMessageChanged()));
|
||||
|
||||
// labels and progressbar
|
||||
d->status = new StatusBarObserver();
|
||||
d->actionLabel = new QLabel(statusBar());
|
||||
@@ -294,12 +334,17 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
||||
d->actionTimer->setObjectName(QString::fromLatin1("actionTimer"));
|
||||
connect(d->actionTimer, SIGNAL(timeout()), d->actionLabel, SLOT(clear()));
|
||||
|
||||
// clear status type
|
||||
d->statusTimer = new QTimer( this );
|
||||
d->statusTimer->setObjectName(QString::fromLatin1("statusTimer"));
|
||||
connect(d->statusTimer, SIGNAL(timeout()), this, SLOT(clearStatus()));
|
||||
|
||||
// update gui timer
|
||||
d->activityTimer = new QTimer(this);
|
||||
d->activityTimer->setObjectName(QString::fromLatin1("activityTimer"));
|
||||
connect(d->activityTimer, SIGNAL(timeout()),this, SLOT(updateActions()));
|
||||
connect(d->activityTimer, SIGNAL(timeout()),this, SLOT(_updateActions()));
|
||||
d->activityTimer->setSingleShot(false);
|
||||
d->activityTimer->start(300);
|
||||
d->activityTimer->start(150);
|
||||
|
||||
// show main window timer
|
||||
d->visibleTimer = new QTimer(this);
|
||||
@@ -335,12 +380,15 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Tree view
|
||||
if (hiddenDockWindows.find("Std_TreeView") == std::string::npos) {
|
||||
//work through parameter.
|
||||
ParameterGrp::handle group = App::GetApplication().GetUserParameter().
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("TreeView");
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("TreeView");
|
||||
bool enabled = group->GetBool("Enabled", true);
|
||||
if(enabled != group->GetBool("Enabled", false)) {
|
||||
enabled = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
||||
->GetGroup("MainWindow")->GetGroup("DockWindows")->GetBool("Std_TreeView",false);
|
||||
}
|
||||
group->SetBool("Enabled", enabled); //ensure entry exists.
|
||||
if (enabled) {
|
||||
TreeDockWidget* tree = new TreeDockWidget(0, this);
|
||||
@@ -355,8 +403,12 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
||||
if (hiddenDockWindows.find("Std_PropertyView") == std::string::npos) {
|
||||
//work through parameter.
|
||||
ParameterGrp::handle group = App::GetApplication().GetUserParameter().
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("PropertyView");
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("PropertyView");
|
||||
bool enabled = group->GetBool("Enabled", true);
|
||||
if(enabled != group->GetBool("Enabled", false)) {
|
||||
enabled = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
|
||||
->GetGroup("MainWindow")->GetGroup("DockWindows")->GetBool("Std_PropertyView",false);
|
||||
}
|
||||
group->SetBool("Enabled", enabled); //ensure entry exists.
|
||||
if (enabled) {
|
||||
PropertyDockView* pcPropView = new PropertyDockView(0, this);
|
||||
@@ -422,6 +474,8 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
||||
pDockMgr->registerDockWindow("Std_PythonView", pcPython);
|
||||
}
|
||||
|
||||
//TODO: Add external object support for DAGView
|
||||
#if 0
|
||||
//Dag View.
|
||||
if (hiddenDockWindows.find("Std_DAGView") == std::string::npos) {
|
||||
//work through parameter.
|
||||
@@ -445,6 +499,7 @@ MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)
|
||||
pDockMgr->registerDockWindow("Std_DAGView", dagDockWindow);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0 //defined(Q_OS_WIN32) this portion of code is not able to run with a vanilla Qtlib build on Windows.
|
||||
// The MainWindowTabBar is used to show tabbed dock windows with icons
|
||||
@@ -528,9 +583,98 @@ void MainWindow::closeActiveWindow ()
|
||||
d->mdiArea->closeActiveSubWindow();
|
||||
}
|
||||
|
||||
void MainWindow::closeAllWindows ()
|
||||
int MainWindow::confirmSave(const char *docName, QWidget *parent, bool addCheckbox) {
|
||||
QMessageBox box(parent?parent:this);
|
||||
box.setIcon(QMessageBox::Question);
|
||||
box.setWindowTitle(QObject::tr("Unsaved document"));
|
||||
if(docName)
|
||||
box.setText(QObject::tr("Do you want to save your changes to document '%1' before closing?")
|
||||
.arg(QString::fromUtf8(docName)));
|
||||
else
|
||||
box.setText(QObject::tr("Do you want to save your changes to document before closing?"));
|
||||
|
||||
box.setInformativeText(QObject::tr("If you don't save, your changes will be lost."));
|
||||
box.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save);
|
||||
box.setDefaultButton(QMessageBox::Save);
|
||||
box.setEscapeButton(QMessageBox::Cancel);
|
||||
|
||||
QCheckBox checkBox(QObject::tr("Apply answer to all"));
|
||||
ParameterGrp::handle hGrp;
|
||||
if(addCheckbox) {
|
||||
hGrp = App::GetApplication().GetUserParameter().
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("General");
|
||||
checkBox.setChecked(hGrp->GetBool("ConfirmAll",false));
|
||||
checkBox.blockSignals(true);
|
||||
box.addButton(&checkBox, QMessageBox::ResetRole);
|
||||
}
|
||||
|
||||
// add shortcuts
|
||||
QAbstractButton* saveBtn = box.button(QMessageBox::Save);
|
||||
if (saveBtn->shortcut().isEmpty()) {
|
||||
QString text = saveBtn->text();
|
||||
text.prepend(QLatin1Char('&'));
|
||||
saveBtn->setShortcut(QKeySequence::mnemonic(text));
|
||||
}
|
||||
|
||||
QAbstractButton* discardBtn = box.button(QMessageBox::Discard);
|
||||
if (discardBtn->shortcut().isEmpty()) {
|
||||
QString text = discardBtn->text();
|
||||
text.prepend(QLatin1Char('&'));
|
||||
discardBtn->setShortcut(QKeySequence::mnemonic(text));
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
switch (box.exec())
|
||||
{
|
||||
case QMessageBox::Save:
|
||||
res = checkBox.isChecked()?2:1;
|
||||
break;
|
||||
case QMessageBox::Discard:
|
||||
res = checkBox.isChecked()?-2:-1;
|
||||
break;
|
||||
}
|
||||
if(addCheckbox && res)
|
||||
hGrp->SetBool("ConfirmAll",checkBox.isChecked());
|
||||
return res;
|
||||
}
|
||||
|
||||
bool MainWindow::closeAllDocuments (bool close)
|
||||
{
|
||||
d->mdiArea->closeAllSubWindows();
|
||||
auto docs = App::GetApplication().getDocuments();
|
||||
try {
|
||||
docs = App::Document::getDependentDocuments(docs,true);
|
||||
}catch(Base::Exception &e) {
|
||||
e.ReportException();
|
||||
}
|
||||
bool checkModify = true;
|
||||
bool saveAll = false;
|
||||
for(auto doc : docs) {
|
||||
auto gdoc = Application::Instance->getDocument(doc);
|
||||
if(!gdoc)
|
||||
continue;
|
||||
if(!gdoc->canClose(false))
|
||||
return false;
|
||||
if(!gdoc->isModified() || doc->testStatus(App::Document::PartialDoc))
|
||||
continue;
|
||||
bool save = saveAll;
|
||||
if(!save && checkModify) {
|
||||
int res = confirmSave(doc->Label.getStrValue().c_str(),this,docs.size()>1);
|
||||
if(res==0)
|
||||
return false;
|
||||
if(res>0) {
|
||||
save = true;
|
||||
if(res==2)
|
||||
saveAll = true;
|
||||
} else if(res==-2)
|
||||
checkModify = false;
|
||||
}
|
||||
if(save && !gdoc->save())
|
||||
return false;
|
||||
}
|
||||
if(close)
|
||||
App::GetApplication().closeAllDocuments();
|
||||
// d->mdiArea->closeAllSubWindows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::activateNextWindow ()
|
||||
@@ -547,6 +691,7 @@ void MainWindow::activateWorkbench(const QString& name)
|
||||
{
|
||||
// emit this signal
|
||||
workbenchActivated(name);
|
||||
updateActions(true);
|
||||
}
|
||||
|
||||
void MainWindow::whatsThis()
|
||||
@@ -640,6 +785,10 @@ bool MainWindow::event(QEvent *e)
|
||||
qApp->sendEvent(viewWidget, &anotherEvent);
|
||||
}
|
||||
return true;
|
||||
}else if(e->type() == QEvent::StatusTip) {
|
||||
// make sure warning and error message don't get blocked by tooltips
|
||||
if(std::abs(d->currentStatusType) <= CustomMessageEvent::Wrn)
|
||||
return true;
|
||||
}
|
||||
return QMainWindow::event(e);
|
||||
}
|
||||
@@ -728,24 +877,27 @@ void MainWindow::addWindow(MDIView* view)
|
||||
{
|
||||
// make workspace parent of view
|
||||
bool isempty = d->mdiArea->subWindowList().isEmpty();
|
||||
QMdiSubWindow* child = new QMdiSubWindow(d->mdiArea->viewport());
|
||||
child->setAttribute(Qt::WA_DeleteOnClose);
|
||||
child->setWidget(view);
|
||||
child->setWindowIcon(view->windowIcon());
|
||||
QMenu* menu = child->systemMenu();
|
||||
QMdiSubWindow* child = qobject_cast<QMdiSubWindow*>(view->parentWidget());
|
||||
if(!child) {
|
||||
child = new QMdiSubWindow(d->mdiArea->viewport());
|
||||
child->setAttribute(Qt::WA_DeleteOnClose);
|
||||
child->setWidget(view);
|
||||
child->setWindowIcon(view->windowIcon());
|
||||
QMenu* menu = child->systemMenu();
|
||||
|
||||
// See StdCmdCloseActiveWindow (#0002631)
|
||||
QList<QAction*> acts = menu->actions();
|
||||
for (QList<QAction*>::iterator it = acts.begin(); it != acts.end(); ++it) {
|
||||
if ((*it)->shortcut() == QKeySequence(QKeySequence::Close)) {
|
||||
(*it)->setShortcuts(QList<QKeySequence>());
|
||||
break;
|
||||
// See StdCmdCloseActiveWindow (#0002631)
|
||||
QList<QAction*> acts = menu->actions();
|
||||
for (QList<QAction*>::iterator it = acts.begin(); it != acts.end(); ++it) {
|
||||
if ((*it)->shortcut() == QKeySequence(QKeySequence::Close)) {
|
||||
(*it)->setShortcuts(QList<QKeySequence>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QAction* action = menu->addAction(tr("Close All"));
|
||||
connect(action, SIGNAL(triggered()), d->mdiArea, SLOT(closeAllSubWindows()));
|
||||
d->mdiArea->addSubWindow(child);
|
||||
QAction* action = menu->addAction(tr("Close All"));
|
||||
connect(action, SIGNAL(triggered()), d->mdiArea, SLOT(closeAllSubWindows()));
|
||||
d->mdiArea->addSubWindow(child);
|
||||
}
|
||||
|
||||
connect(view, SIGNAL(message(const QString&, int)),
|
||||
this, SLOT(showMessage(const QString&, int)));
|
||||
@@ -768,13 +920,13 @@ void MainWindow::addWindow(MDIView* view)
|
||||
* If you want to avoid that the Gui::MDIView instance gets destructed too you
|
||||
* must reparent it afterwards, e.g. set parent to NULL.
|
||||
*/
|
||||
void MainWindow::removeWindow(Gui::MDIView* view)
|
||||
void MainWindow::removeWindow(Gui::MDIView* view, bool close)
|
||||
{
|
||||
// free all connections
|
||||
disconnect(view, SIGNAL(message(const QString&, int)),
|
||||
this, SLOT(showMessage(const QString&, int )));
|
||||
this, SLOT(showMessage(const QString&, int )));
|
||||
disconnect(this, SIGNAL(windowStateChanged(MDIView*)),
|
||||
view, SLOT(windowStateChanged(MDIView*)));
|
||||
view, SLOT(windowStateChanged(MDIView*)));
|
||||
view->removeEventFilter(this);
|
||||
|
||||
// check if the focus widget is a child of the view
|
||||
@@ -791,18 +943,33 @@ void MainWindow::removeWindow(Gui::MDIView* view)
|
||||
}
|
||||
|
||||
QWidget* parent = view->parentWidget();
|
||||
|
||||
// The call of 'd->mdiArea->removeSubWindow(parent)' causes the QMdiSubWindow
|
||||
// to lose its parent and thus the notification in QMdiSubWindow::closeEvent
|
||||
// of other mdi windows to get maximized if this window is maximized will fail.
|
||||
// However, we must let it here otherwise deleting MDI child views directly can
|
||||
// cause other problems.
|
||||
d->mdiArea->removeSubWindow(parent);
|
||||
parent->deleteLater();
|
||||
//
|
||||
// The above mentioned problem can be fixed by setParent(0) which triggers a
|
||||
// ChildRemoved event being handled properly inside QMidArea::viewportEvent()
|
||||
//
|
||||
auto subwindow = qobject_cast<QMdiSubWindow*>(parent);
|
||||
if(subwindow && d->mdiArea->subWindowList().contains(subwindow)) {
|
||||
subwindow->setParent(0);
|
||||
|
||||
assert(!d->mdiArea->subWindowList().contains(subwindow));
|
||||
// d->mdiArea->removeSubWindow(parent);
|
||||
}
|
||||
|
||||
if(close)
|
||||
parent->deleteLater();
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::tabChanged(MDIView* view)
|
||||
{
|
||||
Q_UNUSED(view);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::tabCloseRequested(int index)
|
||||
@@ -817,6 +984,7 @@ void MainWindow::tabCloseRequested(int index)
|
||||
QMdiSubWindow *subWindow = d->mdiArea->subWindowList().at(index);
|
||||
Q_ASSERT(subWindow);
|
||||
subWindow->close();
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::onSetActiveSubWindow(QWidget *window)
|
||||
@@ -824,13 +992,19 @@ void MainWindow::onSetActiveSubWindow(QWidget *window)
|
||||
if (!window)
|
||||
return;
|
||||
d->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::setActiveWindow(MDIView* view)
|
||||
{
|
||||
if(!view || d->activeView == view)
|
||||
return;
|
||||
if(!windows().contains(view->parentWidget()))
|
||||
addWindow(view);
|
||||
onSetActiveSubWindow(view->parentWidget());
|
||||
d->activeView = view;
|
||||
Application::Instance->viewActivated(view);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::onWindowActivated(QMdiSubWindow* w)
|
||||
@@ -854,6 +1028,7 @@ void MainWindow::onWindowActivated(QMdiSubWindow* w)
|
||||
// set active the appropriate window (it needs not to be part of mdiIds, e.g. directly after creation)
|
||||
d->activeView = view;
|
||||
Application::Instance->viewActivated(view);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
void MainWindow::onWindowsMenuAboutToShow()
|
||||
@@ -948,19 +1123,6 @@ QList<QWidget*> MainWindow::windows(QMdiArea::WindowOrder order) const
|
||||
return mdis;
|
||||
}
|
||||
|
||||
// set text to the pane
|
||||
void MainWindow::setPaneText(int i, QString text)
|
||||
{
|
||||
if (i==1) {
|
||||
d->actionLabel->setText(text);
|
||||
d->actionTimer->setSingleShot(true);
|
||||
d->actionTimer->start(5000);
|
||||
}
|
||||
else if (i==2) {
|
||||
d->sizeLabel->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
MDIView* MainWindow::activeWindow(void) const
|
||||
{
|
||||
// each activated window notifies this main window when it is activated
|
||||
@@ -1117,11 +1279,27 @@ void MainWindow::appendRecentFile(const QString& filename)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateActions()
|
||||
void MainWindow::updateActions(bool delay) {
|
||||
//make it safe to call before the main window is actually created
|
||||
if(!this)
|
||||
return;
|
||||
if(!d->activityTimer->isActive())
|
||||
d->activityTimer->start(150);
|
||||
else if(delay) {
|
||||
if(!d->actionUpdateDelay)
|
||||
d->actionUpdateDelay=1;
|
||||
}else
|
||||
d->actionUpdateDelay=-1;
|
||||
}
|
||||
|
||||
void MainWindow::_updateActions()
|
||||
{
|
||||
if (isVisible()) {
|
||||
if (isVisible() && d->actionUpdateDelay<=0) {
|
||||
FC_LOG("update actions");
|
||||
d->activityTimer->stop();
|
||||
Application::Instance->commandManager().testActive();
|
||||
}
|
||||
d->actionUpdateDelay = 0;
|
||||
}
|
||||
|
||||
void MainWindow::switchToTopLevelMode()
|
||||
@@ -1370,54 +1548,39 @@ void MainWindow::dragEnterEvent (QDragEnterEvent * e)
|
||||
}
|
||||
}
|
||||
|
||||
static QLatin1String _MimeDocObj("application/x-documentobject");
|
||||
static QLatin1String _MimeDocObjX("application/x-documentobject-x");
|
||||
static QLatin1String _MimeDocObjFile("application/x-documentobject-file");
|
||||
static QLatin1String _MimeDocObjXFile("application/x-documentobject-x-file");
|
||||
|
||||
QMimeData * MainWindow::createMimeDataFromSelection () const
|
||||
{
|
||||
std::vector<SelectionSingleton::SelObj> selobj = Selection().getCompleteSelection();
|
||||
std::set<App::DocumentObject*> unique_objs;
|
||||
std::map< App::Document*, std::vector<App::DocumentObject*> > objs;
|
||||
for (std::vector<SelectionSingleton::SelObj>::iterator it = selobj.begin(); it != selobj.end(); ++it) {
|
||||
if (it->pObject && it->pObject->getDocument()) {
|
||||
if (unique_objs.insert(it->pObject).second)
|
||||
objs[it->pObject->getDocument()].push_back(it->pObject);
|
||||
}
|
||||
std::vector<App::DocumentObject*> sel;
|
||||
std::set<App::DocumentObject*> objSet;
|
||||
for(auto &s : Selection().getCompleteSelection()) {
|
||||
if(s.pObject && s.pObject->getNameInDocument() && objSet.insert(s.pObject).second)
|
||||
sel.push_back(s.pObject);
|
||||
}
|
||||
|
||||
if (objs.empty())
|
||||
if(sel.empty())
|
||||
return 0;
|
||||
|
||||
std::vector<App::DocumentObject*> sel; // selected
|
||||
std::vector<App::DocumentObject*> all; // object sub-graph
|
||||
for (std::map< App::Document*, std::vector<App::DocumentObject*> >::iterator it = objs.begin(); it != objs.end(); ++it) {
|
||||
std::vector<App::DocumentObject*> dep = it->first->getDependencyList(it->second);
|
||||
sel.insert(sel.end(), it->second.begin(), it->second.end());
|
||||
all.insert(all.end(), dep.begin(), dep.end());
|
||||
auto all = App::Document::getDependencyList(sel);
|
||||
if (all.size() > sel.size()) {
|
||||
DlgObjectSelection dlg(sel,getMainWindow());
|
||||
if(dlg.exec()!=QDialog::Accepted)
|
||||
return 0;
|
||||
sel = dlg.getSelections();
|
||||
if(sel.empty())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (all.size() > sel.size()) {
|
||||
//check if selection are only geofeaturegroup objects, for them it is intuitive and wanted to copy the
|
||||
//dependencies
|
||||
bool hasGroup = false, hasNormal = false;
|
||||
for(auto obj : sel) {
|
||||
if(obj->hasExtension(App::GroupExtension::getExtensionClassTypeId()))
|
||||
hasGroup = true;
|
||||
else
|
||||
hasNormal = true;
|
||||
}
|
||||
if(hasGroup && !hasNormal) {
|
||||
sel = all;
|
||||
}
|
||||
else {
|
||||
//if there are normal objects selected it may be possible that some dependencies are
|
||||
//from them, and not only from groups. so ask the user what to do.
|
||||
int ret = QMessageBox::question(getMainWindow(),
|
||||
tr("Object dependencies"),
|
||||
tr("The selected objects have a dependency to unselected objects.\n"
|
||||
"Do you want to copy them, too?"),
|
||||
QMessageBox::Yes,QMessageBox::No);
|
||||
if (ret == QMessageBox::Yes) {
|
||||
sel = all;
|
||||
}
|
||||
}
|
||||
std::vector<App::Document*> unsaved;
|
||||
bool hasXLink = App::PropertyXLink::hasXLink(sel,&unsaved);
|
||||
if(unsaved.size()) {
|
||||
QMessageBox::critical(getMainWindow(), tr("Unsaved document"),
|
||||
tr("The exported object contains external link. Please save the document"
|
||||
"at least once before exporting."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int memsize=1000; // ~ for the meta-information
|
||||
@@ -1437,7 +1600,7 @@ QMimeData * MainWindow::createMimeDataFromSelection () const
|
||||
WaitCursor wc;
|
||||
QString mime;
|
||||
if (use_buffer) {
|
||||
mime = QLatin1String("application/x-documentobject");
|
||||
mime = hasXLink?_MimeDocObjX:_MimeDocObj;
|
||||
Base::ByteArrayOStreambuf buf(res);
|
||||
std::ostream str(&buf);
|
||||
// need this instance to call MergeDocuments::Save()
|
||||
@@ -1446,7 +1609,7 @@ QMimeData * MainWindow::createMimeDataFromSelection () const
|
||||
doc->exportObjects(sel, str);
|
||||
}
|
||||
else {
|
||||
mime = QLatin1String("application/x-documentobject-file");
|
||||
mime = hasXLink?_MimeDocObjXFile:_MimeDocObjFile;
|
||||
static Base::FileInfo fi(App::Application::getTempFileName());
|
||||
Base::ofstream str(fi, std::ios::out | std::ios::binary);
|
||||
// need this instance to call MergeDocuments::Save()
|
||||
@@ -1471,18 +1634,46 @@ bool MainWindow::canInsertFromMimeData (const QMimeData * source) const
|
||||
if (!source)
|
||||
return false;
|
||||
return source->hasUrls() ||
|
||||
source->hasFormat(QLatin1String("application/x-documentobject")) ||
|
||||
source->hasFormat(QLatin1String("application/x-documentobject-file"));
|
||||
source->hasFormat(_MimeDocObj) || source->hasFormat(_MimeDocObjX) ||
|
||||
source->hasFormat(_MimeDocObjFile) || source->hasFormat(_MimeDocObjXFile);
|
||||
}
|
||||
|
||||
void MainWindow::insertFromMimeData (const QMimeData * mimeData)
|
||||
{
|
||||
if (!mimeData)
|
||||
return;
|
||||
if (mimeData->hasFormat(QLatin1String("application/x-documentobject"))) {
|
||||
QByteArray res = mimeData->data(QLatin1String("application/x-documentobject"));
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if (!doc) doc = App::GetApplication().newDocument();
|
||||
bool fromDoc = false;
|
||||
bool hasXLink = false;
|
||||
QString format;
|
||||
if(mimeData->hasFormat(_MimeDocObj))
|
||||
format = _MimeDocObj;
|
||||
else if(mimeData->hasFormat(_MimeDocObjX)) {
|
||||
format = _MimeDocObjX;
|
||||
hasXLink = true;
|
||||
}else if(mimeData->hasFormat(_MimeDocObjFile))
|
||||
fromDoc = true;
|
||||
else if(mimeData->hasFormat(_MimeDocObjXFile)) {
|
||||
fromDoc = true;
|
||||
hasXLink = true;
|
||||
}else {
|
||||
if (mimeData->hasUrls())
|
||||
loadUrls(App::GetApplication().getActiveDocument(), mimeData->urls());
|
||||
return;
|
||||
}
|
||||
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if(!doc) doc = App::GetApplication().newDocument();
|
||||
|
||||
if(hasXLink && !doc->isSaved()) {
|
||||
int ret = QMessageBox::question(getMainWindow(), tr("Unsaved document"),
|
||||
tr("To link to external objects, the document must be saved at least once.\n"
|
||||
"Do you want to save the document now?"),
|
||||
QMessageBox::Yes,QMessageBox::No);
|
||||
if(ret != QMessageBox::Yes || !Application::Instance->getDocument(doc)->saveAs())
|
||||
return;
|
||||
}
|
||||
if(!fromDoc) {
|
||||
QByteArray res = mimeData->data(format);
|
||||
|
||||
doc->openTransaction("Paste");
|
||||
Base::ByteArrayIStreambuf buf(res);
|
||||
@@ -1498,10 +1689,8 @@ void MainWindow::insertFromMimeData (const QMimeData * mimeData)
|
||||
}
|
||||
doc->commitTransaction();
|
||||
}
|
||||
else if (mimeData->hasFormat(QLatin1String("application/x-documentobject-file"))) {
|
||||
QByteArray res = mimeData->data(QLatin1String("application/x-documentobject-file"));
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if (!doc) doc = App::GetApplication().newDocument();
|
||||
else {
|
||||
QByteArray res = mimeData->data(format);
|
||||
|
||||
doc->openTransaction("Paste");
|
||||
Base::FileInfo fi((const char*)res);
|
||||
@@ -1517,10 +1706,6 @@ void MainWindow::insertFromMimeData (const QMimeData * mimeData)
|
||||
}
|
||||
doc->commitTransaction();
|
||||
}
|
||||
else if (mimeData->hasUrls()) {
|
||||
// load the files into the active document if there is one, otherwise let create one
|
||||
loadUrls(App::GetApplication().getActiveDocument(), mimeData->urls());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setUrlHandler(const QString &scheme, Gui::UrlHandler* handler)
|
||||
@@ -1630,51 +1815,82 @@ void MainWindow::changeEvent(QEvent *e)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showMessage (const QString& message, int timeout)
|
||||
{
|
||||
QFontMetrics fm(statusBar()->font());
|
||||
QString msg = fm.elidedText(message, Qt::ElideMiddle, this->d->actionLabel->width());
|
||||
#if QT_VERSION <= 0x040600
|
||||
this->statusBar()->showMessage(msg, timeout);
|
||||
#else
|
||||
//#0000665: There is a crash under Ubuntu 12.04 (Qt 4.8.1)
|
||||
QMetaObject::invokeMethod(statusBar(), "showMessage",
|
||||
Qt::QueuedConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString,msg),
|
||||
Q_ARG(int, timeout));
|
||||
#endif
|
||||
void MainWindow::clearStatus() {
|
||||
d->currentStatusType = 100;
|
||||
statusBar()->setStyleSheet(QString::fromLatin1("#statusBar{}"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
void MainWindow::statusMessageChanged() {
|
||||
if(d->currentStatusType<0)
|
||||
d->currentStatusType = -d->currentStatusType;
|
||||
else {
|
||||
// here probably means the status bar message is changed by QMainWindow
|
||||
// internals, e.g. for displaying tooltip and stuff. Set reset what
|
||||
// we've changed.
|
||||
d->statusTimer->stop();
|
||||
clearStatus();
|
||||
}
|
||||
}
|
||||
|
||||
namespace Gui {
|
||||
void MainWindow::showMessage(const QString& message, int timeout) {
|
||||
if(QApplication::instance()->thread() != QThread::currentThread()) {
|
||||
QApplication::postEvent(this, new CustomMessageEvent(CustomMessageEvent::Tmp,message,timeout));
|
||||
return;
|
||||
}
|
||||
d->actionLabel->setText(message.simplified());
|
||||
if(timeout) {
|
||||
d->actionTimer->setSingleShot(true);
|
||||
d->actionTimer->start(timeout);
|
||||
}else
|
||||
d->actionTimer->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* The CustomMessageEvent class is used to send messages as events in the methods
|
||||
* Error(), Warning() and Message() of the StatusBarObserver class to the main window
|
||||
* to display them on the status bar instead of printing them directly to the status bar.
|
||||
*
|
||||
* This makes the usage of StatusBarObserver thread-safe.
|
||||
* @author Werner Mayer
|
||||
*/
|
||||
class CustomMessageEvent : public QEvent
|
||||
void MainWindow::showStatus(int type, const QString& message)
|
||||
{
|
||||
public:
|
||||
enum Type {Msg, Wrn, Err, Log};
|
||||
CustomMessageEvent(Type t, const QString& s)
|
||||
: QEvent(QEvent::User), _type(t), msg(s)
|
||||
{ }
|
||||
~CustomMessageEvent()
|
||||
{ }
|
||||
Type type() const
|
||||
{ return _type; }
|
||||
const QString& message() const
|
||||
{ return msg; }
|
||||
private:
|
||||
Type _type;
|
||||
QString msg;
|
||||
};
|
||||
if(QApplication::instance()->thread() != QThread::currentThread()) {
|
||||
QApplication::postEvent(this,
|
||||
new CustomMessageEvent((CustomMessageEvent::Type)type,message));
|
||||
return;
|
||||
}
|
||||
|
||||
if(d->currentStatusType < type)
|
||||
return;
|
||||
|
||||
d->statusTimer->setSingleShot(true);
|
||||
// TODO: hardcode?
|
||||
int timeout = 5000;
|
||||
d->statusTimer->start(timeout);
|
||||
|
||||
QFontMetrics fm(statusBar()->font());
|
||||
QString msg = fm.elidedText(message, Qt::ElideMiddle, this->d->actionLabel->width());
|
||||
switch(type) {
|
||||
case CustomMessageEvent::Err:
|
||||
statusBar()->setStyleSheet(d->status->err);
|
||||
break;
|
||||
case CustomMessageEvent::Wrn:
|
||||
statusBar()->setStyleSheet(d->status->wrn);
|
||||
break;
|
||||
case CustomMessageEvent::Pane:
|
||||
statusBar()->setStyleSheet(QString::fromLatin1("#statusBar{}"));
|
||||
break;
|
||||
default:
|
||||
statusBar()->setStyleSheet(d->status->msg);
|
||||
break;
|
||||
}
|
||||
d->currentStatusType = -type;
|
||||
statusBar()->showMessage(msg.simplified(), timeout);
|
||||
}
|
||||
|
||||
|
||||
// set text to the pane
|
||||
void MainWindow::setPaneText(int i, QString text)
|
||||
{
|
||||
if (i==1) {
|
||||
showStatus(CustomMessageEvent::Pane, text);
|
||||
}
|
||||
else if (i==2) {
|
||||
d->sizeLabel->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::customEvent(QEvent* e)
|
||||
@@ -1682,7 +1898,8 @@ void MainWindow::customEvent(QEvent* e)
|
||||
if (e->type() == QEvent::User) {
|
||||
Gui::CustomMessageEvent* ce = static_cast<Gui::CustomMessageEvent*>(e);
|
||||
QString msg = ce->message();
|
||||
if (ce->type() == CustomMessageEvent::Log) {
|
||||
switch(ce->type()) {
|
||||
case CustomMessageEvent::Log: {
|
||||
if (msg.startsWith(QLatin1String("#Inventor V2.1 ascii "))) {
|
||||
Gui::Document *d = Application::Instance->activeDocument();
|
||||
if (d) {
|
||||
@@ -1696,11 +1913,12 @@ void MainWindow::customEvent(QEvent* e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
d->actionLabel->setText(msg);
|
||||
d->actionTimer->setSingleShot(true);
|
||||
d->actionTimer->start(5000);
|
||||
break;
|
||||
} case CustomMessageEvent::Tmp: {
|
||||
showMessage(msg, ce->timeout());
|
||||
break;
|
||||
} default:
|
||||
showStatus(ce->type(),msg);
|
||||
}
|
||||
}
|
||||
else if (e->type() == ActionStyleEvent::EventType) {
|
||||
@@ -1723,9 +1941,9 @@ void MainWindow::customEvent(QEvent* e)
|
||||
StatusBarObserver::StatusBarObserver()
|
||||
: WindowParameter("OutputWindow")
|
||||
{
|
||||
msg = QString::fromLatin1("#000000"); // black
|
||||
wrn = QString::fromLatin1("#ffaa00"); // orange
|
||||
err = QString::fromLatin1("#ff0000"); // red
|
||||
msg = QString::fromLatin1("#statusBar{color: #000000}"); // black
|
||||
wrn = QString::fromLatin1("#statusBar{color: #ffaa00}"); // orange
|
||||
err = QString::fromLatin1("#statusBar{color: #ff0000}"); // red
|
||||
Base::Console().AttachObserver(this);
|
||||
getWindowParameter()->Attach(this);
|
||||
getWindowParameter()->NotifyAll();
|
||||
@@ -1740,17 +1958,18 @@ StatusBarObserver::~StatusBarObserver()
|
||||
void StatusBarObserver::OnChange(Base::Subject<const char*> &rCaller, const char * sReason)
|
||||
{
|
||||
ParameterGrp& rclGrp = ((ParameterGrp&)rCaller);
|
||||
auto format = QString::fromLatin1("#statusBar{color: %1}");
|
||||
if (strcmp(sReason, "colorText") == 0) {
|
||||
unsigned long col = rclGrp.GetUnsigned( sReason );
|
||||
this->msg = QColor((col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff).name();
|
||||
this->msg = format.arg(QColor((col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff).name());
|
||||
}
|
||||
else if (strcmp(sReason, "colorWarning") == 0) {
|
||||
unsigned long col = rclGrp.GetUnsigned( sReason );
|
||||
this->wrn = QColor((col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff).name();
|
||||
this->wrn = format.arg(QColor((col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff).name());
|
||||
}
|
||||
else if (strcmp(sReason, "colorError") == 0) {
|
||||
unsigned long col = rclGrp.GetUnsigned( sReason );
|
||||
this->err = QColor((col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff).name();
|
||||
this->err = format.arg(QColor((col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff).name());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1760,8 +1979,7 @@ void StatusBarObserver::OnChange(Base::Subject<const char*> &rCaller, const char
|
||||
void StatusBarObserver::Message(const char * m)
|
||||
{
|
||||
// Send the event to the main window to allow thread-safety. Qt will delete it when done.
|
||||
QString txt = QString::fromLatin1("<font color=\"%1\">%2</font>").arg(this->msg, QString::fromUtf8(m));
|
||||
CustomMessageEvent* ev = new CustomMessageEvent(CustomMessageEvent::Msg, txt);
|
||||
CustomMessageEvent* ev = new CustomMessageEvent(CustomMessageEvent::Msg, QString::fromUtf8(m));
|
||||
QApplication::postEvent(getMainWindow(), ev);
|
||||
}
|
||||
|
||||
@@ -1771,8 +1989,7 @@ void StatusBarObserver::Message(const char * m)
|
||||
void StatusBarObserver::Warning(const char *m)
|
||||
{
|
||||
// Send the event to the main window to allow thread-safety. Qt will delete it when done.
|
||||
QString txt = QString::fromLatin1("<font color=\"%1\">%2</font>").arg(this->wrn, QString::fromUtf8(m));
|
||||
CustomMessageEvent* ev = new CustomMessageEvent(CustomMessageEvent::Wrn, txt);
|
||||
CustomMessageEvent* ev = new CustomMessageEvent(CustomMessageEvent::Wrn, QString::fromUtf8(m));
|
||||
QApplication::postEvent(getMainWindow(), ev);
|
||||
}
|
||||
|
||||
@@ -1782,8 +1999,7 @@ void StatusBarObserver::Warning(const char *m)
|
||||
void StatusBarObserver::Error (const char *m)
|
||||
{
|
||||
// Send the event to the main window to allow thread-safety. Qt will delete it when done.
|
||||
QString txt = QString::fromLatin1("<font color=\"%1\">%2</font>").arg(this->err, QString::fromUtf8(m));
|
||||
CustomMessageEvent* ev = new CustomMessageEvent(CustomMessageEvent::Err, txt);
|
||||
CustomMessageEvent* ev = new CustomMessageEvent(CustomMessageEvent::Err, QString::fromUtf8(m));
|
||||
QApplication::postEvent(getMainWindow(), ev);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
* Removes an MDI window from the main window's workspace and its associated tab without
|
||||
* deleting the widget. If the main windows does not have such a window nothing happens.
|
||||
*/
|
||||
void removeWindow(MDIView* view);
|
||||
void removeWindow(MDIView* view, bool close=true);
|
||||
/**
|
||||
* Returns a list of all MDI windows in the worpspace.
|
||||
*/
|
||||
@@ -178,6 +178,8 @@ public:
|
||||
void unsetUrlHandler(const QString &scheme);
|
||||
//@}
|
||||
|
||||
void updateActions(bool delay = false);
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Sets text to the pane in the status bar.
|
||||
@@ -200,11 +202,12 @@ public Q_SLOTS:
|
||||
*/
|
||||
void closeActiveWindow ();
|
||||
/**
|
||||
* Closes all child windows.
|
||||
* The windows are closed in random order. The operation stops
|
||||
* if a window does not accept the close event.
|
||||
* Closes all document window.
|
||||
*/
|
||||
void closeAllWindows ();
|
||||
bool closeAllDocuments (bool close=true);
|
||||
/** Pop up a message box asking for saving document
|
||||
*/
|
||||
int confirmSave(const char *docName, QWidget *parent=0, bool addCheckBox=false);
|
||||
/**
|
||||
* Activates the next window in the child window chain.
|
||||
*/
|
||||
@@ -223,6 +226,9 @@ public Q_SLOTS:
|
||||
void whatsThis();
|
||||
void switchToTopLevelMode();
|
||||
void switchToDockedMode();
|
||||
|
||||
void statusMessageChanged();
|
||||
|
||||
void showMessage (const QString & message, int timeout = 0);
|
||||
|
||||
protected:
|
||||
@@ -250,6 +256,8 @@ protected:
|
||||
*/
|
||||
void changeEvent(QEvent *e);
|
||||
|
||||
void showStatus(int type, const QString & message);
|
||||
|
||||
private Q_SLOTS:
|
||||
/**
|
||||
* \internal
|
||||
@@ -278,7 +286,7 @@ private Q_SLOTS:
|
||||
/**
|
||||
* This method gets frequently activated and test the commands if they are still active.
|
||||
*/
|
||||
void updateActions();
|
||||
void _updateActions();
|
||||
/**
|
||||
* \internal
|
||||
*/
|
||||
@@ -291,6 +299,10 @@ private Q_SLOTS:
|
||||
* \internal
|
||||
*/
|
||||
void processMessages(const QList<QByteArray> &);
|
||||
/**
|
||||
* \internal
|
||||
*/
|
||||
void clearStatus();
|
||||
|
||||
Q_SIGNALS:
|
||||
void timeEvent();
|
||||
@@ -339,6 +351,7 @@ public:
|
||||
/// name of the observer
|
||||
const char *Name(void){return "StatusBar";}
|
||||
|
||||
friend class MainWindow;
|
||||
private:
|
||||
QString msg, wrn, err;
|
||||
};
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <bitset>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
// Boost
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
@@ -48,6 +48,7 @@ struct SequencerPrivate
|
||||
WaitCursor* waitCursor;
|
||||
QTime measureTime;
|
||||
QTime progressTime;
|
||||
QTime checkAbortTime;
|
||||
QString text;
|
||||
bool guiThread;
|
||||
};
|
||||
@@ -133,6 +134,7 @@ void Sequencer::startStep()
|
||||
d->guiThread = false;
|
||||
d->bar->setRange(0, (int)nTotalSteps);
|
||||
d->progressTime.start();
|
||||
d->checkAbortTime.start();
|
||||
d->measureTime.start();
|
||||
QMetaObject::invokeMethod(d->bar, "aboutToShow", Qt::QueuedConnection);
|
||||
}
|
||||
@@ -140,6 +142,7 @@ void Sequencer::startStep()
|
||||
d->guiThread = true;
|
||||
d->bar->setRange(0, (int)nTotalSteps);
|
||||
d->progressTime.start();
|
||||
d->checkAbortTime.start();
|
||||
d->measureTime.start();
|
||||
d->waitCursor = new Gui::WaitCursor;
|
||||
d->bar->enterControlEvents();
|
||||
@@ -147,6 +150,30 @@ void Sequencer::startStep()
|
||||
}
|
||||
}
|
||||
|
||||
void Sequencer::checkAbort() {
|
||||
if(d->bar->thread() != QThread::currentThread())
|
||||
return;
|
||||
if (!wasCanceled()) {
|
||||
if(d->checkAbortTime.elapsed() < 500)
|
||||
return;
|
||||
d->checkAbortTime.restart();
|
||||
qApp->processEvents();
|
||||
return;
|
||||
}
|
||||
// restore cursor
|
||||
pause();
|
||||
bool ok = d->bar->canAbort();
|
||||
// continue and show up wait cursor if needed
|
||||
resume();
|
||||
|
||||
// force to abort the operation
|
||||
if ( ok ) {
|
||||
abort();
|
||||
} else {
|
||||
rejectCancel();
|
||||
}
|
||||
}
|
||||
|
||||
void Sequencer::nextStep(bool canAbort)
|
||||
{
|
||||
QThread *currentThread = QThread::currentThread();
|
||||
@@ -246,7 +273,7 @@ void Sequencer::showRemainingTime()
|
||||
QString status = QString::fromLatin1("%1\t[%2]").arg(txt, remain);
|
||||
|
||||
if (thr != currentThread) {
|
||||
QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
|
||||
QMetaObject::invokeMethod(getMainWindow(), "showMessage",
|
||||
Qt::/*Blocking*/QueuedConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString,status));
|
||||
@@ -265,7 +292,7 @@ void Sequencer::resetData()
|
||||
if (thr != currentThread) {
|
||||
QMetaObject::invokeMethod(d->bar, "reset", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(d->bar, "aboutToHide", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
|
||||
QMetaObject::invokeMethod(getMainWindow(), "showMessage",
|
||||
Qt::/*Blocking*/QueuedConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString,QString()));
|
||||
@@ -295,7 +322,7 @@ void Sequencer::abort()
|
||||
{
|
||||
//resets
|
||||
resetData();
|
||||
Base::AbortException exc("Aborting...");
|
||||
Base::AbortException exc("User aborted");
|
||||
throw exc;
|
||||
}
|
||||
|
||||
@@ -307,7 +334,7 @@ void Sequencer::setText (const char* pszTxt)
|
||||
// print message to the statusbar
|
||||
d->text = pszTxt ? QString::fromUtf8(pszTxt) : QLatin1String("");
|
||||
if (thr != currentThread) {
|
||||
QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
|
||||
QMetaObject::invokeMethod(getMainWindow(), "showMessage",
|
||||
Qt::/*Blocking*/QueuedConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString,d->text));
|
||||
|
||||
@@ -107,6 +107,8 @@ public:
|
||||
/** Returns an instance of the progress bar. It creates one if needed. */
|
||||
QProgressBar* getProgressBar(QWidget* parent=0);
|
||||
|
||||
virtual void checkAbort() override;
|
||||
|
||||
protected:
|
||||
/** Construction */
|
||||
Sequencer ();
|
||||
|
||||
@@ -36,6 +36,7 @@ struct SequencerDialogPrivate
|
||||
ProgressDialog* dlg;
|
||||
QTime measureTime;
|
||||
QTime progressTime;
|
||||
QTime checkAbortTime;
|
||||
QString text;
|
||||
bool guiThread;
|
||||
};
|
||||
@@ -91,6 +92,7 @@ void SequencerDialog::startStep()
|
||||
d->dlg->setModal(false);
|
||||
if (nTotalSteps == 0) {
|
||||
d->progressTime.start();
|
||||
d->checkAbortTime.start();
|
||||
}
|
||||
|
||||
d->measureTime.start();
|
||||
@@ -103,6 +105,7 @@ void SequencerDialog::startStep()
|
||||
d->dlg->setModal(true);
|
||||
if (nTotalSteps == 0) {
|
||||
d->progressTime.start();
|
||||
d->checkAbortTime.start();
|
||||
}
|
||||
|
||||
d->measureTime.start();
|
||||
@@ -111,6 +114,30 @@ void SequencerDialog::startStep()
|
||||
}
|
||||
}
|
||||
|
||||
void SequencerDialog::checkAbort() {
|
||||
if(d->dlg->thread() != QThread::currentThread())
|
||||
return;
|
||||
if (!wasCanceled()) {
|
||||
if(d->checkAbortTime.elapsed() < 500)
|
||||
return;
|
||||
d->checkAbortTime.restart();
|
||||
qApp->processEvents();
|
||||
return;
|
||||
}
|
||||
// restore cursor
|
||||
pause();
|
||||
bool ok = d->dlg->canAbort();
|
||||
// continue and show up wait cursor if needed
|
||||
resume();
|
||||
|
||||
// force to abort the operation
|
||||
if ( ok ) {
|
||||
abort();
|
||||
} else {
|
||||
rejectCancel();
|
||||
}
|
||||
}
|
||||
|
||||
void SequencerDialog::nextStep(bool canAbort)
|
||||
{
|
||||
QThread *currentThread = QThread::currentThread();
|
||||
@@ -239,7 +266,7 @@ void SequencerDialog::abort()
|
||||
{
|
||||
//resets
|
||||
resetData();
|
||||
Base::AbortException exc("Aborting...");
|
||||
Base::AbortException exc("User aborted");
|
||||
throw exc;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
void resume();
|
||||
bool isBlocking() const;
|
||||
|
||||
virtual void checkAbort() override;
|
||||
|
||||
protected:
|
||||
/** Construction */
|
||||
SequencerDialog ();
|
||||
|
||||
@@ -353,6 +353,12 @@ ReportOutput::ReportOutput(QWidget* parent)
|
||||
_prefs->Attach(this);
|
||||
_prefs->Notify("FontSize");
|
||||
|
||||
#ifdef FC_DEBUG
|
||||
messageSize = _prefs->GetInt("LogMessageSize",0);
|
||||
#else
|
||||
messageSize = _prefs->GetInt("LogMessageSize",2048);
|
||||
#endif
|
||||
|
||||
// scroll to bottom at startup to make sure that last appended text is visible
|
||||
ensureCursorVisible();
|
||||
}
|
||||
@@ -399,11 +405,13 @@ void ReportOutput::Error (const char * s)
|
||||
void ReportOutput::Log (const char * s)
|
||||
{
|
||||
QString msg = QString::fromUtf8(s);
|
||||
if (msg.length() < 1000){
|
||||
// Send the event to itself to allow thread-safety. Qt will delete it when done.
|
||||
CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::LogText, msg);
|
||||
QApplication::postEvent(this, ev);
|
||||
if(messageSize>0 && msg.size()>messageSize) {
|
||||
msg.truncate(messageSize);
|
||||
msg += QString::fromLatin1("...\n");
|
||||
}
|
||||
// Send the event to itself to allow thread-safety. Qt will delete it when done.
|
||||
CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::LogText, msg);
|
||||
QApplication::postEvent(this, ev);
|
||||
}
|
||||
|
||||
void ReportOutput::customEvent ( QEvent* ev )
|
||||
@@ -626,6 +634,12 @@ void ReportOutput::OnChange(Base::Subject<const char*> &rCaller, const char * sR
|
||||
bool checked = rclGrp.GetBool(sReason, true);
|
||||
if (checked != d->redirected_stderr)
|
||||
onToggleRedirectPythonStderr();
|
||||
}else if(strcmp(sReason, "LogMessageSize") == 0) {
|
||||
#ifdef FC_DEBUG
|
||||
messageSize = rclGrp.GetInt(sReason,0);
|
||||
#else
|
||||
messageSize = rclGrp.GetInt(sReason,2048);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +188,7 @@ private:
|
||||
Data* d;
|
||||
bool gotoEnd;
|
||||
ReportHighlighter* reportHl; /**< Syntax highlighter */
|
||||
int messageSize;
|
||||
ParameterGrp::handle _prefs;
|
||||
};
|
||||
|
||||
|
||||
@@ -195,6 +195,11 @@ View3DInventor::View3DInventor(Gui::Document* pcDocument, QWidget* parent,
|
||||
|
||||
View3DInventor::~View3DInventor()
|
||||
{
|
||||
if(_pcDocument) {
|
||||
SoCamera * Cam = _viewer->getSoRenderManager()->getCamera();
|
||||
if (Cam)
|
||||
_pcDocument->saveCameraSettings(SoFCDB::writeNodesToString(Cam).c_str());
|
||||
}
|
||||
hGrp->Detach(this);
|
||||
|
||||
//If we destroy this viewer by calling 'delete' directly the focus proxy widget which is defined
|
||||
|
||||
Reference in New Issue
Block a user