1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "ObjectViewModel.h"
23 
24 #include <QScrollArea>
25 #include <QSplitter>
26 #include <QVBoxLayout>
27 
28 #include <U2Core/AppContext.h>
29 #include <U2Core/DocumentModel.h>
30 #include <U2Core/GObject.h>
31 #include <U2Core/Log.h>
32 #include <U2Core/ProjectModel.h>
33 #include <U2Core/TextUtils.h>
34 #include <U2Core/U2SafePoints.h>
35 
36 #include <U2Gui/OptionsPanel.h>
37 #include <U2Gui/OptionsPanelWidget.h>
38 
39 // BUG:535 refactor closing interface.
40 // Idea: make it QObject and call 'deleteLater' on it
41 
42 namespace U2 {
43 
44 const QString GObjectViewState::APP_CLOSING_STATE_NAME("Auto saved");
45 const GObjectViewFactoryId GObjectViewFactory::SIMPLE_TEXT_FACTORY("SimpleTextView");
46 const QString GObjectViewMenuType::CONTEXT("gobject-view-menu-type-context");
47 const QString GObjectViewMenuType::STATIC("object-view-menu-type-static");
48 
setViewName(const QString & newName)49 void GObjectViewState::setViewName(const QString &newName) {
50     // this method is not a real state modification: state caches view name as a reference, but not its internal data
51     // it is used only on view renaming
52     viewName = newName;
53 }
54 
setStateName(const QString & newName)55 void GObjectViewState::setStateName(const QString &newName) {
56     if (newName == stateName) {
57         return;
58     }
59     stateName = newName;
60     emit si_stateModified(this);
61 }
62 
setStateData(const QVariantMap & data)63 void GObjectViewState::setStateData(const QVariantMap &data) {
64     stateData = data;
65     emit si_stateModified(this);
66 }
67 
registerGObjectViewFactory(GObjectViewFactory * f)68 void GObjectViewFactoryRegistry::registerGObjectViewFactory(GObjectViewFactory *f) {
69     mapping[f->getId()] = f;
70 }
71 
unregisterGObjectViewFactory(GObjectViewFactory * f)72 void GObjectViewFactoryRegistry::unregisterGObjectViewFactory(GObjectViewFactory *f) {
73     assert(GObjectViewUtils::findViewsByFactoryId(f->getId()).isEmpty());
74     mapping.remove(f->getId());
75 }
76 
getFactoryById(GObjectViewFactoryId t) const77 GObjectViewFactory *GObjectViewFactoryRegistry::getFactoryById(GObjectViewFactoryId t) const {
78     return mapping.value(t, nullptr);
79 }
80 
81 //////////////////////////////////////////////////////////////////////////
82 /// GObjectViewFactory
83 
isStateInSelection(const MultiGSelection &,const QVariantMap &)84 bool GObjectViewFactory::isStateInSelection(const MultiGSelection &, const QVariantMap &) {
85     return false;
86 }
87 
createViewTask(const QString &,const QVariantMap &)88 Task *GObjectViewFactory::createViewTask(const QString &, const QVariantMap &) {
89     SAFE_POINT(false, "createViewTask from state is not supported by the view", nullptr);
90 }
91 
92 //////////////////////////////////////////////////////////////////////////
93 /// GObjectView
GObjectView(GObjectViewFactoryId _factoryId,const QString & _viewName,QObject * prnt)94 GObjectView::GObjectView(GObjectViewFactoryId _factoryId, const QString &_viewName, QObject *prnt)
95     : QObject(prnt) {
96     factoryId = _factoryId;
97     viewName = _viewName;
98     widget = nullptr;
99     optionsPanel = nullptr;
100     closeInterface = nullptr;
101     closing = false;
102 
103     Project *project = AppContext::getProject();
104     SAFE_POINT(project != nullptr, "project is null in GObjectView()", );
105 
106     connect(project, SIGNAL(si_documentAdded(Document *)), SLOT(sl_onDocumentAdded(Document *)));
107     connect(project, SIGNAL(si_documentRemoved(Document *)), SLOT(sl_onDocumentRemoved(Document *)));
108     for (Document *document : qAsConst(project->getDocuments())) {
109         sl_onDocumentAdded(document);
110     }
111 }
112 
canAddObject(GObject * obj)113 bool GObjectView::canAddObject(GObject *obj) {
114     if (objects.contains(obj)) {
115         // the 'obj' is already in the view.
116         return false;
117     }
118     for (GObjectViewObjectHandler *objectHandler : qAsConst(objectHandlers)) {
119         if (objectHandler->canHandle(this, obj)) {
120             return true;
121         }
122     }
123     return false;
124 }
125 
addObject(GObject * o)126 QString GObjectView::addObject(GObject *o) {
127     if (closing) {
128         return tr("Can't add object: %1 to the closing view").arg(o->getGObjectName());
129     }
130     if (objects.contains(o)) {
131         return tr("Object is already added to view %1!").arg(o->getGObjectName());
132     }
133 
134     bool canBeAdded = canAddObject(o);
135     if (!canBeAdded) {
136         for (GObjectViewObjectHandler *objectHandler : qAsConst(objectHandlers)) {
137             canBeAdded = objectHandler->canHandle(this, o);
138             if (canBeAdded) {
139                 break;
140             }
141         }
142     }
143 
144     if (!canBeAdded) {
145         return tr("Can't add object: %1").arg(o->getGObjectName());
146     }
147 
148     objects << o;
149     onObjectAdded(o);
150     emit si_objectAdded(this, o);
151     return QString();
152 }
153 
_removeObject(GObject * o)154 void GObjectView::_removeObject(GObject *o) {
155     o->disconnect(this);
156     int i = objects.removeAll(o);
157     assert(i == 1);
158     Q_UNUSED(i);
159     closing = onObjectRemoved(o) || closing;
160     emit si_objectRemoved(this, o);
161 
162     if (requiredObjects.contains(o)) {
163         closing = true;
164     }
165 }
166 
removeObject(GObject * o)167 void GObjectView::removeObject(GObject *o) {
168     assert(!closing);
169     _removeObject(o);
170     if (closing) {
171         SAFE_POINT(closeInterface != nullptr, "No close interface", );
172         closeInterface->closeView();
173     }
174 }
175 
sl_onObjectRemovedFromDocument(GObject * o)176 void GObjectView::sl_onObjectRemovedFromDocument(GObject *o) {
177     if (objects.contains(o)) {
178         _removeObject(o);
179         if (closing) {
180             SAFE_POINT(closeInterface != nullptr, "No close interface", );
181             closeInterface->closeView();
182         }
183     }
184 }
185 
onObjectRemoved(GObject * obj)186 bool GObjectView::onObjectRemoved(GObject *obj) {
187     for (GObjectViewObjectHandler *objectHandler : qAsConst(objectHandlers)) {
188         objectHandler->onObjectRemoved(this, obj);
189     }
190     return false;
191 }
192 
onObjectAdded(GObject * obj)193 void GObjectView::onObjectAdded(GObject *obj) {
194     connect(obj, SIGNAL(si_nameChanged(const QString &)), SLOT(sl_onObjectNameChanged(const QString &)));
195     for (GObjectViewObjectHandler *objectHandler : qAsConst(objectHandlers)) {
196         objectHandler->onObjectAdded(this, obj);
197     }
198 }
199 
onObjectRenamed(GObject *,const QString &)200 void GObjectView::onObjectRenamed(GObject *, const QString &) {
201     // Do nothing by default.
202 }
203 
sl_onDocumentAdded(Document * d)204 void GObjectView::sl_onDocumentAdded(Document *d) {
205     connect(d, SIGNAL(si_objectRemoved(GObject *)), SLOT(sl_onObjectRemovedFromDocument(GObject *)));
206     connect(d, SIGNAL(si_loadedStateChanged()), SLOT(sl_onDocumentLoadedStateChanged()));
207 }
208 
sl_onDocumentRemoved(Document * d)209 void GObjectView::sl_onDocumentRemoved(Document *d) {
210     if (closing) {
211         return;
212     }
213     d->disconnect(this);
214     for (GObject *object : qAsConst(d->getObjects())) {
215         if (objects.contains(object)) {
216             _removeObject(object);
217         }
218         if (closing) {
219             SAFE_POINT(closeInterface != nullptr, "No close interface", );
220             closeInterface->closeView();
221             break;
222         }
223     }
224 }
225 
sl_onDocumentLoadedStateChanged()226 void GObjectView::sl_onDocumentLoadedStateChanged() {
227 }
228 
sl_onObjectNameChanged(const QString & oldName)229 void GObjectView::sl_onObjectNameChanged(const QString &oldName) {
230     CHECK(AppContext::getProject() != nullptr, );
231     GObject *object = qobject_cast<GObject *>(sender());
232     SAFE_POINT(object != nullptr, "Can't locate renamed object!", );
233     onObjectRenamed(object, oldName);
234 }
235 
getWidget()236 QWidget *GObjectView::getWidget() {
237     if (widget == nullptr) {
238         assert(closeInterface != nullptr);
239         widget = createWidget();
240     }
241     return widget;
242 }
243 
getOptionsPanel()244 OptionsPanel *GObjectView::getOptionsPanel() {
245     return 0;
246 }
247 
setClosingInterface(GObjectViewCloseInterface * i)248 void GObjectView::setClosingInterface(GObjectViewCloseInterface *i) {
249     closeInterface = i;
250 }
251 
buildStaticToolbar(QToolBar * tb)252 void GObjectView::buildStaticToolbar(QToolBar *tb) {
253     emit si_buildStaticToolbar(this, tb);
254 }
255 
buildMenu(QMenu * m,const QString & type)256 void GObjectView::buildMenu(QMenu *m, const QString &type) {
257     emit si_buildMenu(this, m, type);
258 }
259 
260 // Returns true if view  contains this object
containsObject(GObject * obj) const261 bool GObjectView::containsObject(GObject *obj) const {
262     return objects.contains(obj);
263 }
264 
265 // Returns true if view  contains any objects from the document
containsDocumentObjects(Document * doc) const266 bool GObjectView::containsDocumentObjects(Document *doc) const {
267     for (GObject *object : qAsConst(doc->getObjects())) {
268         if (containsObject(object)) {
269             return true;
270         }
271     }
272     return false;
273 }
274 
setName(const QString & newName)275 void GObjectView::setName(const QString &newName) {
276     QString oldName = viewName;
277     if (oldName == newName) {
278         return;
279     }
280     viewName = newName;
281     emit si_nameChanged(oldName);
282 }
283 
284 //////////////////////////////////////////////////////////////////////////
285 /// GObjectViewWindow
286 
GObjectViewWindow(GObjectView * v,const QString & _viewName,bool _persistent)287 GObjectViewWindow::GObjectViewWindow(GObjectView *v, const QString &_viewName, bool _persistent)
288     : MWMDIWindow(_viewName), view(v), persistent(_persistent) {
289     v->setParent(this);
290     v->setClosingInterface(this);
291     // Get the GObject widget and options panel
292     QWidget *viewWidget = v->getWidget();
293     if (viewWidget == nullptr) {
294         coreLog.error("Internal error: Object View widget is not initialized");
295         v->setClosingInterface(nullptr);
296         v->setParent(nullptr);
297         return;
298     }
299     // Initialize the layout of the whole windows
300     QHBoxLayout *windowLayout = new QHBoxLayout();
301     windowLayout->setContentsMargins(0, 0, 0, 0);
302     windowLayout->setSpacing(0);
303 
304     QWidget *objectWidget = new QWidget(this);
305     // Initialize the layout of the object part only
306     QVBoxLayout *objectLayout = new QVBoxLayout(objectWidget);
307     objectLayout->setContentsMargins(0, 0, 0, 0);
308     objectLayout->setSpacing(0);
309 
310     // Add the widget to the layout and "parent" it
311     objectLayout->addWidget(viewWidget);
312 
313     OptionsPanel *optionsPanel = v->getOptionsPanel();
314     if (optionsPanel == nullptr) {
315         // Set the layout of the whole window
316         windowLayout->addWidget(objectWidget);
317     } else {
318         OptionsPanelWidget *optionsPanelWidget = optionsPanel->getMainWidget();
319         QSplitter *splitter = new QSplitter();
320         splitter->setObjectName("OPTIONS_PANEL_SPLITTER");
321         splitter->setOrientation(Qt::Horizontal);
322         splitter->setChildrenCollapsible(false);
323         splitter->addWidget(objectWidget);
324         splitter->addWidget(optionsPanelWidget->getOptionsWidget());
325         splitter->setStretchFactor(0, 1);
326         splitter->setStretchFactor(1, 0);
327 
328         windowLayout->addWidget(splitter);
329         windowLayout->addWidget(optionsPanelWidget);
330     }
331 
332     QScrollArea *windowScrollArea = new QScrollArea();
333     windowScrollArea->setFrameStyle(QFrame::NoFrame);
334     windowScrollArea->setWidgetResizable(true);
335 
336     auto windowContentWidget = new QWidget();
337     windowContentWidget->setObjectName("object_view_window_content_widget");
338     windowContentWidget->setLayout(windowLayout);
339     windowScrollArea->setWidget(windowContentWidget);
340 
341     QHBoxLayout *l = new QHBoxLayout();
342     l->setContentsMargins(0, 0, 0, 0);
343     l->addWidget(windowScrollArea);
344     setLayout(l);
345 
346     // Set the icon
347     setWindowIcon(viewWidget->windowIcon());
348 }
349 
setPersistent(bool v)350 void GObjectViewWindow::setPersistent(bool v) {
351     if (persistent == v) {
352         return;
353     }
354     persistent = v;
355     emit si_persistentStateChanged(this);
356 }
357 
closeView()358 void GObjectViewWindow::closeView() {
359     AppContext::getMainWindow()->getMDIManager()->closeMDIWindow(this);
360     emit si_windowClosed(this);
361 }
362 
onCloseEvent()363 bool GObjectViewWindow::onCloseEvent() {
364     view->saveWidgetState();
365     return view->onCloseEvent();
366 }
367 
getViewFactory() const368 GObjectViewFactory *GObjectViewWindow::getViewFactory() const {
369     GObjectViewFactory *viewFactory = AppContext::getObjectViewFactoryRegistry()->getFactoryById(view->getFactoryId());
370     SAFE_POINT(viewFactory != nullptr, "viewFactory is null!", nullptr)
371     return viewFactory;
372 }
373 
setupMDIToolbar(QToolBar * tb)374 void GObjectViewWindow::setupMDIToolbar(QToolBar *tb) {
375     view->buildStaticToolbar(tb);
376 }
377 
setupViewMenu(QMenu * m)378 void GObjectViewWindow::setupViewMenu(QMenu *m) {
379     view->buildMenu(m, GObjectViewMenuType::STATIC);
380 }
381 
382 //////////////////////////////////////////////////////////////////////////
383 /// Utils
384 
findViewByName(const QString & name)385 GObjectViewWindow *GObjectViewUtils::findViewByName(const QString &name) {
386     QList<MWMDIWindow *> mdiWindows = AppContext::getMainWindow()->getMDIManager()->getWindows();
387     for (MWMDIWindow *mdiWindow : qAsConst(mdiWindows)) {
388         if (mdiWindow->windowTitle() == name) {
389             GObjectViewWindow *objectViewWindow = qobject_cast<GObjectViewWindow *>(mdiWindow);
390             if (objectViewWindow != nullptr) {
391                 return objectViewWindow;
392             }
393         }
394     }
395     return nullptr;
396 }
397 
genUniqueViewName(const QString & name)398 QString GObjectViewUtils::genUniqueViewName(const QString &name) {
399     SAFE_POINT(!name.isEmpty(), "genUniqueViewName got empty name!", "");
400 
401     QSet<QString> usedNames;  // set of names is derived from active views & saved states
402     QList<MWMDIWindow *> windows = AppContext::getMainWindow()->getMDIManager()->getWindows();
403     for (const MWMDIWindow *w : qAsConst(windows)) {
404         usedNames.insert(w->windowTitle());
405     }
406     Project *project = AppContext::getProject();
407     if (project != nullptr) {
408         for (const GObjectViewState *state : qAsConst(project->getGObjectViewStates())) {
409             usedNames.insert(state->getViewName());
410         }
411     }
412     return TextUtils::variate(name, " ", usedNames, false, 2);
413 }
414 
genUniqueStateName(const QString & stateName)415 QString GObjectViewUtils::genUniqueStateName(const QString &stateName) {
416     SAFE_POINT(!stateName.isEmpty(), "genUniqueStateName got empty state name!", "");
417 
418     QSet<QString> usedNames;
419     const QList<GObjectViewState *> states = AppContext::getProject()->getGObjectViewStates();
420     for (const GObjectViewState *state : qAsConst(states)) {
421         usedNames.insert(state->getStateName());
422     }
423     return TextUtils::variate(stateName, " ", usedNames, false, 2);
424 }
425 
genUniqueViewName(const Document * doc,const GObject * obj)426 QString GObjectViewUtils::genUniqueViewName(const Document *doc, const GObject *obj) {
427     QString fileName = doc->getURL().fileName();
428     QString viewName = obj->getGObjectName() + (fileName.isEmpty() ? "" : " [" + fileName + "]");
429     return genUniqueViewName(viewName);
430 }
431 
findStatesByViewName(const QString & viewName)432 QList<GObjectViewState *> GObjectViewUtils::findStatesByViewName(const QString &viewName) {
433     QList<GObjectViewState *> result;
434     Project *project = AppContext::getProject();
435     if (project != nullptr) {
436         for (GObjectViewState *state : qAsConst(project->getGObjectViewStates())) {
437             if (state->getViewName() == viewName) {
438                 result << state;
439             }
440         }
441     }
442     return result;
443 }
444 
findStateByName(const QString & viewName,const QString & stateName)445 GObjectViewState *GObjectViewUtils::findStateByName(const QString &viewName, const QString &stateName) {
446     Project *project = AppContext::getProject();
447     SAFE_POINT(project != nullptr, "project is null!", nullptr);
448     const QList<GObjectViewState *> &allStates = project->getGObjectViewStates();
449     return findStateInList(viewName, stateName, allStates);
450 }
451 
findStateInList(const QString & viewName,const QString & stateName,const QList<GObjectViewState * > & states)452 GObjectViewState *GObjectViewUtils::findStateInList(const QString &viewName, const QString &stateName, const QList<GObjectViewState *> &states) {
453     for (GObjectViewState *state : qAsConst(states)) {
454         if (state->getViewName() == viewName && state->getStateName() == stateName) {
455             return state;
456         }
457     }
458     return nullptr;
459 }
460 
getAllActiveViews()461 QList<GObjectViewWindow *> GObjectViewUtils::getAllActiveViews() {
462     QList<MWMDIWindow *> mdiWindows = AppContext::getMainWindow()->getMDIManager()->getWindows();
463     QList<GObjectViewWindow *> objectViewWindows;
464     for (MWMDIWindow *mdiWindow : qAsConst(mdiWindows)) {
465         GObjectViewWindow *objectViewWindow = qobject_cast<GObjectViewWindow *>(mdiWindow);
466         if (objectViewWindow != nullptr) {
467             objectViewWindows << objectViewWindow;
468         }
469     }
470     return objectViewWindows;
471 }
472 
findViewsByFactoryId(GObjectViewFactoryId id)473 QList<GObjectViewWindow *> GObjectViewUtils::findViewsByFactoryId(GObjectViewFactoryId id) {
474     QList<GObjectViewWindow *> resultWindowList;
475     MainWindow *mainWindow = AppContext::getMainWindow();
476     if (mainWindow == nullptr || mainWindow->getMDIManager() == nullptr) {
477         return resultWindowList;  // Main window is closed.
478     }
479     QList<MWMDIWindow *> mdiWindows = mainWindow->getMDIManager()->getWindows();
480     for (MWMDIWindow *mdiWindow : qAsConst(mdiWindows)) {
481         GObjectViewWindow *objectViewWindow = qobject_cast<GObjectViewWindow *>(mdiWindow);
482         if (objectViewWindow != nullptr && objectViewWindow->getViewFactoryId() == id) {
483             resultWindowList << objectViewWindow;
484         }
485     }
486     return resultWindowList;
487 }
488 
selectStates(const MultiGSelection & ms,const QList<GObjectViewState * > & states)489 QList<GObjectViewState *> GObjectViewUtils::selectStates(const MultiGSelection &ms, const QList<GObjectViewState *> &states) {
490     QList<GObjectViewFactory *> objectViewFactoryList = AppContext::getObjectViewFactoryRegistry()->getAllFactories();
491 
492     QList<GObjectViewState *> resultStateList;
493     for (GObjectViewFactory *objectViewFactory : qAsConst(objectViewFactoryList)) {
494         QList<GObjectViewState *> stateList = selectStates(objectViewFactory, ms, states);
495         resultStateList += stateList;
496     }
497     return resultStateList;
498 }
499 
selectStates(GObjectViewFactory * f,const MultiGSelection & ms,const QList<GObjectViewState * > & states)500 QList<GObjectViewState *> GObjectViewUtils::selectStates(GObjectViewFactory *f, const MultiGSelection &ms, const QList<GObjectViewState *> &states) {
501     QList<GObjectViewState *> resultStateList;
502     for (GObjectViewState *state : qAsConst(states)) {
503         if (state->getViewFactoryId() == f->getId()) {
504             if (f->isStateInSelection(ms, state->getStateData())) {
505                 resultStateList << state;
506             }
507         }
508     }
509     return resultStateList;
510 }
511 
findViewsWithObject(GObject * obj)512 QList<GObjectViewWindow *> GObjectViewUtils::findViewsWithObject(GObject *obj) {
513     QList<GObjectViewWindow *> resultWindowList;
514     QList<GObjectViewWindow *> activeViewWindowList = getAllActiveViews();
515     for (GObjectViewWindow *activeViewWindow : qAsConst(activeViewWindowList)) {
516         if (activeViewWindow->getObjects().contains(obj)) {
517             resultWindowList << activeViewWindow;
518         }
519     }
520     return resultWindowList;
521 }
522 
findViewsWithAnyOfObjects(const QList<GObject * > & objs)523 QList<GObjectViewWindow *> GObjectViewUtils::findViewsWithAnyOfObjects(const QList<GObject *> &objs) {
524     QList<GObjectViewWindow *> resultViewWindowList;
525     for (GObject *object : qAsConst(objs)) {
526         QList<GObjectViewWindow *> viewWindowWithObjectList = findViewsWithObject(object);
527         for (GObjectViewWindow *viewWindow : qAsConst(viewWindowWithObjectList)) {
528             if (!resultViewWindowList.contains(viewWindow)) {
529                 resultViewWindowList += viewWindowWithObjectList;
530             }
531         }
532     }
533     return resultViewWindowList;
534 }
535 
getActiveObjectViewWindow()536 GObjectViewWindow *GObjectViewUtils::getActiveObjectViewWindow() {
537     MWMDIWindow *activeWindow = AppContext::getMainWindow()->getMDIManager()->getActiveWindow();
538     return qobject_cast<GObjectViewWindow *>(activeWindow);
539 }
540 
541 //////////////////////////////////////////////////////////////////////////
542 // GObjectViewWindowContext
543 
GObjectViewWindowContext(QObject * p,const GObjectViewFactoryId & _id)544 GObjectViewWindowContext::GObjectViewWindowContext(QObject *p, const GObjectViewFactoryId &_id)
545     : QObject(p), id(_id) {
546 }
547 
init()548 void GObjectViewWindowContext::init() {
549     MWMDIManager *mdiManager = AppContext::getMainWindow()->getMDIManager();
550     connect(mdiManager, SIGNAL(si_windowAdded(MWMDIWindow *)), SLOT(sl_windowAdded(MWMDIWindow *)));
551     connect(mdiManager, SIGNAL(si_windowClosing(MWMDIWindow *)), SLOT(sl_windowClosing(MWMDIWindow *)));
552     const QList<MWMDIWindow *> windowList = mdiManager->getWindows();
553     for (MWMDIWindow *mdiWindow : qAsConst(windowList)) {
554         sl_windowAdded(mdiWindow);
555     }
556 }
557 
~GObjectViewWindowContext()558 GObjectViewWindowContext::~GObjectViewWindowContext() {
559     MWMDIManager *mdiManager = AppContext::getMainWindow()->getMDIManager();
560     if (mdiManager == nullptr) {  // TODO: disconnect context on view removal and assert (mdi!=NULL) here.
561         return;
562     }
563     const QList<MWMDIWindow *> windowList = mdiManager->getWindows();
564     for (MWMDIWindow *window : qAsConst(windowList)) {
565         GObjectViewWindow *objectViewWindow = qobject_cast<GObjectViewWindow *>(window);
566         if (objectViewWindow == nullptr || (!id.isEmpty() && objectViewWindow->getViewFactoryId() != id)) {
567             continue;
568         }
569         GObjectView *objectView = objectViewWindow->getObjectView();
570         disconnectView(objectView);
571     }
572 }
573 
sl_windowAdded(MWMDIWindow * w)574 void GObjectViewWindowContext::sl_windowAdded(MWMDIWindow *w) {
575     GObjectViewWindow *objectViewWindow = qobject_cast<GObjectViewWindow *>(w);
576     if (objectViewWindow == nullptr || (!id.isEmpty() && objectViewWindow->getViewFactoryId() != id)) {
577         return;
578     }
579     GObjectView *objectView = objectViewWindow->getObjectView();
580     assert(!viewResources.contains(objectView));
581 
582     objectView->addObjectHandler(this);
583 
584     initViewContext(objectView);
585 
586     connect(objectView, SIGNAL(si_buildMenu(GObjectView *, QMenu *, const QString &)), SLOT(sl_buildMenu(GObjectView *, QMenu *, const QString &)));
587 }
588 
sl_windowClosing(MWMDIWindow * w)589 void GObjectViewWindowContext::sl_windowClosing(MWMDIWindow *w) {
590     GObjectViewWindow *objectViewWindow = qobject_cast<GObjectViewWindow *>(w);
591     if (objectViewWindow == nullptr || (!id.isEmpty() && objectViewWindow->getViewFactoryId() != id)) {
592         return;
593     }
594     GObjectView *objectView = objectViewWindow->getObjectView();
595     disconnectView(objectView);
596 }
597 
sl_buildMenu(GObjectView * v,QMenu * m,const QString & type)598 void GObjectViewWindowContext::sl_buildMenu(GObjectView *v, QMenu *m, const QString &type) {
599     if (type == GObjectViewMenuType::STATIC) {
600         buildStaticMenu(v, m);
601     } else if (type == GObjectViewMenuType::CONTEXT) {
602         buildContextMenu(v, m);
603     } else {
604         buildActionMenu(v, m, type);
605     }
606 }
607 
buildStaticMenu(GObjectView * view,QMenu * menu)608 void GObjectViewWindowContext::buildStaticMenu(GObjectView *view, QMenu *menu) {
609     buildStaticOrContextMenu(view, menu);
610 }
611 
buildContextMenu(GObjectView * view,QMenu * menu)612 void GObjectViewWindowContext::buildContextMenu(GObjectView *view, QMenu *menu) {
613     buildStaticOrContextMenu(view, menu);
614 }
615 
buildStaticOrContextMenu(GObjectView *,QMenu *)616 void GObjectViewWindowContext::buildStaticOrContextMenu(GObjectView *, QMenu *) {
617     // No extra static/context menu items by default.
618 }
619 
buildActionMenu(GObjectView * view,QMenu * menu,const QString & menuType)620 void GObjectViewWindowContext::buildActionMenu(GObjectView *view, QMenu *menu, const QString &menuType) {
621     QList<GObjectViewAction *> viewActions = getViewActions(view);
622     for (GObjectViewAction *action : viewActions) {
623         if (action->isInMenu(menuType)) {
624             action->addToMenuWithOrder(menu);
625         }
626     }
627 }
628 
disconnectView(GObjectView * v)629 void GObjectViewWindowContext::disconnectView(GObjectView *v) {
630     QList<QObject *> resourceObjectList = viewResources[v];
631     for (QObject *resourceObject : qAsConst(resourceObjectList)) {
632         resourceObject->deleteLater();  // deliver close signals, save view states first
633     }
634     viewResources.remove(v);
635     v->removeObjectHandler(this);
636 }
637 
addViewResource(GObjectView * v,QObject * r)638 void GObjectViewWindowContext::addViewResource(GObjectView *v, QObject *r) {
639     assert(v != nullptr && (!id.isEmpty() || v->getFactoryId() == id));
640 
641     QList<QObject *> resources = viewResources[v];
642     assert(!resources.contains(r));
643     resources.append(r);
644     viewResources[v] = resources;
645 }
646 
addViewAction(GObjectViewAction * a)647 void GObjectViewWindowContext::addViewAction(GObjectViewAction *a) {
648     addViewResource(a->getObjectView(), a);
649 }
650 
findViewAction(GObjectView * v,const QString & actionName) const651 GObjectViewAction *GObjectViewWindowContext::findViewAction(GObjectView *v, const QString &actionName) const {
652     const QList<GObjectViewAction *> viewActionList = getViewActions(v);
653     for (GObjectViewAction *viewAction : qAsConst(viewActionList)) {
654         if (viewAction->objectName() == actionName) {
655             return viewAction;
656         }
657     }
658     return nullptr;
659 }
660 
getViewActions(GObjectView * v) const661 QList<GObjectViewAction *> GObjectViewWindowContext::getViewActions(GObjectView *v) const {
662     QList<GObjectViewAction *> actions;
663     QList<QObject *> resourceObjectList = viewResources[v];
664     for (QObject *resourceObject : qAsConst(resourceObjectList)) {
665         GObjectViewAction *viewAction = qobject_cast<GObjectViewAction *>(resourceObject);
666         if (viewAction != nullptr) {
667             actions << viewAction;
668         }
669     }
670     return actions;
671 }
672 
onObjectRemoved(GObjectView * v,GObject * obj)673 void GObjectViewWindowContext::onObjectRemoved(GObjectView *v, GObject *obj) {
674     GObjectViewObjectHandler::onObjectRemoved(v, obj);
675     const QList<GObjectViewAction *> viewActionList = getViewActions(v);
676     for (GObjectViewAction *action : qAsConst(viewActionList)) {
677         obj->disconnect(action);
678     }
679 }
680 
681 //////////////////////////////////////////////////////////////////////////
682 // GObjectViewAction
683 
GObjectViewAction(QObject * p,GObjectView * v,const QString & text,int order)684 GObjectViewAction::GObjectViewAction(QObject *p, GObjectView *v, const QString &text, int order)
685     : QAction(text, p), view(v), actionOrder(order) {
686 }
687 
getObjectView() const688 GObjectView *GObjectViewAction::getObjectView() const {
689     return view;
690 }
691 
getActionOrder() const692 int GObjectViewAction::getActionOrder() const {
693     return actionOrder;
694 }
695 
isInMenu(const QString & menuType) const696 bool GObjectViewAction::isInMenu(const QString &menuType) const {
697     return menuTypes.contains(menuType);
698 }
699 
setMenuTypes(const QList<QString> & newMenuTypes)700 void GObjectViewAction::setMenuTypes(const QList<QString> &newMenuTypes) {
701     menuTypes = newMenuTypes;
702 }
703 
addToMenuWithOrder(QMenu * menu)704 void GObjectViewAction::addToMenuWithOrder(QMenu *menu) {
705     QList<QAction *> actionList = menu->actions();
706     for (QAction *action : actionList) {
707         GObjectViewAction *viewAction = qobject_cast<GObjectViewAction *>(action);
708         if (viewAction != nullptr && viewAction->getActionOrder() > actionOrder) {
709             menu->insertAction(action, this);
710             return;
711         }
712     }
713     menu->addAction(this);
714 }
715 
canHandle(GObjectView *,GObject *)716 bool GObjectViewObjectHandler::canHandle(GObjectView *, GObject *) {
717     return false;
718 }
719 
onObjectAdded(GObjectView *,GObject *)720 void GObjectViewObjectHandler::onObjectAdded(GObjectView *, GObject *) {
721 }
722 
onObjectRemoved(GObjectView *,GObject *)723 void GObjectViewObjectHandler::onObjectRemoved(GObjectView *, GObject *) {
724 }
725 
726 }  // namespace U2
727