/*========================================================================= medInria Copyright (c) INRIA 2013 - 2020. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * @brief Private class that contains data members for medTabbedView Containers */ class medTabbedViewContainersPrivate { public: QShortcut *closeShortcut; QPushButton *addTabButton; QHash > containerSelectedForTabIndex; medParameterPoolL *pool; medAbstractWorkspaceLegacy * owningWorkspace; bool bKeepLeastOne; int currentIdx; }; medTabbedViewContainers::medTabbedViewContainers(medAbstractWorkspaceLegacy* owningWorkspace, QWidget *parent) : QTabWidget(parent), d(new medTabbedViewContainersPrivate) { d->bKeepLeastOne = false; d->owningWorkspace = owningWorkspace; this->setTabsClosable(true); this->setMovable(true); d->addTabButton = new QPushButton(this); d->addTabButton->setObjectName("addTabButton"); d->addTabButton->setShortcut(Qt::ControlModifier + Qt::Key_T); this->setCornerWidget(d->addTabButton); d->closeShortcut = new QShortcut(this); d->closeShortcut->setKey(Qt::ControlModifier + Qt::Key_W); // Connect for creation, removal and move of tabs connect(d->addTabButton, SIGNAL(clicked()), this, SLOT(addContainerInTabUnNamed())); connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); connect(d->closeShortcut, SIGNAL(activated()), this, SLOT(closeCurrentTab())); connect(tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(movedTabs(int,int))); // Connect group of view handling connect(&medViewContainerManager::instance(), SIGNAL(containerAboutToBeDestroyed(QUuid)), this, SLOT(removeContainerFromSelection(QUuid))); connect(this, SIGNAL(containersSelectedChanged()), this, SLOT(buildTemporaryPool())); connect(this, SIGNAL(currentChanged(int)), this, SLOT(connectContainerSelectedForCurrentTab())); d->pool = new medParameterPoolL(this); } medTabbedViewContainers::~medTabbedViewContainers(void) { delete d; d = nullptr; } void medTabbedViewContainers::lockTabs() { this->setTabsClosable(false); this->setMovable(false); d->addTabButton->hide(); d->closeShortcut->blockSignals(true); d->addTabButton->blockSignals(true); } void medTabbedViewContainers::unlockTabs() { this->setTabsClosable(true); this->setMovable(true); d->addTabButton->show(); d->closeShortcut->blockSignals(false); d->addTabButton->blockSignals(false); } void medTabbedViewContainers::hideTabBar() { QTabBar *tabBar = this->tabBar(); tabBar->hide(); } void medTabbedViewContainers::adjustContainersSize() { medViewContainerSplitter *splitter = static_cast(this->widget(this->currentIndex()))->getSplitter(); if (splitter) { splitter->adjustContainersSize(); } } QList medTabbedViewContainers::containersSelected() { return d->containerSelectedForTabIndex.value(this->currentIndex()); } medViewContainer* medTabbedViewContainers::getFirstSelectedContainer() { QList listUuid = containersSelected(); medViewContainer* view = nullptr; if (listUuid.count() > 0) { // only keep the first selected container view = medViewContainerManager::instance().container(listUuid[0]); } return view; } medAbstractView* medTabbedViewContainers::getFirstSelectedContainerView() { medViewContainer* currentFirstContainer = getFirstSelectedContainer(); medAbstractView* view = nullptr; if (currentFirstContainer) { view = currentFirstContainer->view(); } return view; } QList medTabbedViewContainers::viewsInTab(int index) { QList views; medRootContainer *root = dynamic_cast(this->widget(index)); if (!root) return views; medViewContainerSplitter *splitter = root->getSplitter(); if (!splitter) return views; QList containers = splitter->containers(); for(medViewContainer *container : containers) { if(container->view()) { views << container->view(); } } return views; } QList medTabbedViewContainers::containersInTab(int index) { medRootContainer *root = dynamic_cast(this->widget(index)); if (!root) return QList(); medViewContainerSplitter *splitter = root->getSplitter(); if (!splitter) return QList(); return splitter->containers(); } void medTabbedViewContainers::setKeepLeastOne(bool pi_bVal) { d->bKeepLeastOne = pi_bVal; } void medTabbedViewContainers::setSplitter(int index, medViewContainerSplitter *splitter) { if (this->widget(index)) this->closeTab(index); medRootContainer* poRootContainer = new medRootContainer(this); poRootContainer->replaceSplitter(splitter); int idx = this->insertTab(index, poRootContainer, QString("%0 %1").arg(d->owningWorkspace->name()).arg(count())); this->setCurrentIndex(idx); d->containerSelectedForTabIndex.insert(idx, QList()); connect(splitter, SIGNAL(newContainer(QUuid)), this, SLOT(connectContainer(QUuid)), Qt::UniqueConnection); for(medViewContainer* container : splitter->containers()) { this->connectContainer(container->uuid()); } } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// medViewContainer* medTabbedViewContainers::addContainerInTabUnNamed() { return this->addContainerInTabNamed(QString("%0 %1").arg(d->owningWorkspace->name()).arg(count()+1)); } medViewContainer* medTabbedViewContainers::addContainerInTabNamed(const QString &name) { return this->insertNewTab(this->count(), name); } medViewContainer *medTabbedViewContainers::insertNewTab(int index, const QString &name) { medRootContainer* poRootContainer = new medRootContainer(this); medViewContainerSplitter *splitter = new medViewContainerSplitter; poRootContainer->replaceSplitter(splitter); int idx = this->insertTab(index, poRootContainer, name); this->setCurrentIndex(idx); d->containerSelectedForTabIndex.insert(idx, QList()); connect(splitter, SIGNAL(newContainer(QUuid)), this, SLOT(connectContainer(QUuid)), Qt::UniqueConnection); medViewContainer* container = new medViewContainer; splitter->addViewContainer(container); return container; } void medTabbedViewContainers::closeCurrentTab() { this->closeTab(this->currentIndex()); } void medTabbedViewContainers::closeTab(int index) { //Remove widget of tab and delete it QWidget *poTmp = widget(index); this->removeTab(index); poTmp->disconnect(this); medViewContainerSplitter* poMainSpliter = static_cast(poTmp)->getSplitter(); if (poMainSpliter) { poMainSpliter->disconnect(this); } poTmp->deleteLater(); //Reset tab name if it follows the rule '###' is follows for (int i = 0; i < this->count(); ++i) { if (this->tabText(i).isEmpty() || this->tabText(i).startsWith(d->owningWorkspace->name(), Qt::CaseSensitive)) { this->setTabText(i, d->owningWorkspace->name() + " " + QString::number(i + 1)); } } // If medTabbedViewContainers is empty and empty is not allowed, we recreate one tab if (d->bKeepLeastOne && (this->count() < 1)) { this->addContainerInTabUnNamed(); } else { // Update the remaining tab indexes d->containerSelectedForTabIndex.remove(index); QHash > copyHash; for(auto currentTabIndex : d->containerSelectedForTabIndex.keys()) { auto previousTab = d->containerSelectedForTabIndex.take(currentTabIndex); copyHash.insert(copyHash.count(), previousTab); } d->containerSelectedForTabIndex.clear(); d->containerSelectedForTabIndex = copyHash; // Update the current tab to the last one setCurrentIndex(this->count()-1); emit containersSelectedChanged(); } } void medTabbedViewContainers::tabBarDoubleClickedHandler(int index) { if (index < 0) { addContainerInTabUnNamed(); } } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PRIVET SLOTS // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void medTabbedViewContainers::disconnectTabFromSplitter(int index) { Q_UNUSED(index); medViewContainerSplitter* splitter = dynamic_cast(this->sender()); if(!splitter) return; splitter->disconnect(this); } void medTabbedViewContainers::connectContainer(QUuid container) { medViewContainer* cont = medViewContainerManager::instance().container(container); connect(cont, SIGNAL(containerSelected(QUuid)), this, SLOT(addContainerToSelection(QUuid)), Qt::UniqueConnection); connect(cont, SIGNAL(containerUnSelected(QUuid)), this, SLOT(removeContainerFromSelection(QUuid)), Qt::UniqueConnection); connect(cont, SIGNAL(maximized(QUuid,bool)), this, SLOT(minimizeOtherContainers(QUuid,bool))); cont->setSelected(true); } void medTabbedViewContainers::addContainerToSelection(QUuid container) { QList containersSelected = d->containerSelectedForTabIndex.value(this->currentIndex()); if(QApplication::keyboardModifiers() != Qt::ControlModifier) { for(QUuid uuid : containersSelected) { medViewContainerManager::instance().container(uuid)->setSelected(false); } } // containersSelected may have been modified in removeContainerFromSelection so // we have to get it back again containersSelected = d->containerSelectedForTabIndex.value(this->currentIndex()); containersSelected.append(container); medViewContainer *newSelectedContainer = medViewContainerManager::instance().container(container); d->containerSelectedForTabIndex.insert(this->currentIndex(), containersSelected); connect(newSelectedContainer, SIGNAL(viewRemoved()), this, SIGNAL(containersSelectedChanged()), Qt::UniqueConnection); connect(newSelectedContainer, SIGNAL(viewContentChanged()), this, SIGNAL(containersSelectedChanged()), Qt::UniqueConnection); emit containersSelectedChanged(); } void medTabbedViewContainers::removeContainerFromSelection(QUuid container) { for(QList containersSelected : d->containerSelectedForTabIndex.values()) { int tabIndex = d->containerSelectedForTabIndex.key(containersSelected); if(containersSelected.removeOne(container)) { d->containerSelectedForTabIndex.insert(tabIndex, containersSelected); medViewContainer *unSelectedContainer = medViewContainerManager::instance().container(container); this->disconnect(unSelectedContainer, SIGNAL(viewRemoved()), this, 0); this->disconnect(unSelectedContainer, SIGNAL(viewContentChanged()), this, 0); emit containersSelectedChanged(); break; } } } void medTabbedViewContainers::buildTemporaryPool() { d->pool->clear(); for(QUuid uuid : this->containersSelected()) { medViewContainer *container = medViewContainerManager::instance().container(uuid); if(!container->view()) { continue; } for(medAbstractParameterL *param : container->view()->linkableParameters()) { d->pool->append(param); } } } void medTabbedViewContainers::connectContainerSelectedForCurrentTab() { for(QList containersSelected : d->containerSelectedForTabIndex.values()) { int tabIndex = d->containerSelectedForTabIndex.key(containersSelected); if(tabIndex == this->currentIndex()) { for(QUuid uuid : containersSelected) { medViewContainer *container = medViewContainerManager::instance().container(uuid); connect(container, SIGNAL(viewRemoved()), this, SIGNAL(containersSelectedChanged()), Qt::UniqueConnection); connect(container, SIGNAL(viewContentChanged()), this, SIGNAL(containersSelectedChanged()), Qt::UniqueConnection); } } else { for(QUuid uuid : containersSelected) { medViewContainer *container = medViewContainerManager::instance().container(uuid); this->disconnect(container, SIGNAL(viewRemoved()), this, 0); this->disconnect(container, SIGNAL(viewContentChanged()), this, 0); } } } emit containersSelectedChanged(); } void medTabbedViewContainers::minimizeOtherContainers(QUuid containerMaximized, bool maximized) { medViewContainerSplitter *splitter = static_cast(this->widget(this->currentIndex()))->getSplitter(); if(!splitter) return; // hide/show all other containers and their splitter minimizeSplitterContainers(containerMaximized, maximized, splitter); if(maximized) { medViewContainer *container = medViewContainerManager::instance().container(containerMaximized); QWidget *parent = container->parentWidget(); //iterates through parents to restablish splitter visibility (hidden by minimizeSplitterContainers) while(parent) { parent->show(); parent = parent->parentWidget(); } } } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PRIVET // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void medTabbedViewContainers::minimizeSplitterContainers(QUuid containerMaximized, bool maximized, medViewContainerSplitter *splitter) { for(int i=0; icount(); i++) { if(medViewContainerSplitter *nestedSplitter = dynamic_cast(splitter->widget(i))) { minimizeSplitterContainers(containerMaximized, maximized, nestedSplitter); } else if(medViewContainer *container = dynamic_cast(splitter->widget(i))) { if(container->uuid() != containerMaximized) { if(maximized) { container->hide(); } else { container->show(); } } } } if(maximized) { splitter->hide(); } else { splitter->show(); } } /** * @brief Launched when a tab from the tabs bar is moved to a new position * * @param from index tab before * @param to index tab after */ void medTabbedViewContainers::movedTabs(int from, int to) { auto previousFrom = d->containerSelectedForTabIndex.take(from); auto previousTo = d->containerSelectedForTabIndex.take(to); d->containerSelectedForTabIndex.insert(from, previousTo); d->containerSelectedForTabIndex.insert(to, previousFrom); emit containersSelectedChanged(); }