1 /****************************************************************************
2 * MeshLab                                                           o o     *
3 * A versatile mesh processing toolbox                             o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2005                                                \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 #include "multiViewer_Container.h"
24 #include "glarea.h"
25 #include <QMouseEvent>
26 #include "mainwindow.h"
27 #include <common/mlapplication.h>
28 
29 using namespace vcg;
30 
Splitter(QWidget * parent)31 Splitter::Splitter ( QWidget * parent):QSplitter(parent){}
Splitter(Qt::Orientation orientation,QWidget * parent)32 Splitter::Splitter(Qt::Orientation orientation, QWidget *parent):QSplitter(orientation,parent){}
33 
createHandle()34 QSplitterHandle *Splitter::createHandle()
35 {
36 	return new SplitterHandle(orientation(), this);
37 }
38 
getRootContainer()39 MultiViewer_Container *Splitter::getRootContainer()
40 {
41 	Splitter * parentSplitter = this;
42 	MultiViewer_Container* mvc = qobject_cast<MultiViewer_Container *>(parentSplitter);
43 	while(!mvc)
44 	{
45 		parentSplitter = qobject_cast<Splitter *>(parentSplitter->parent());
46 		mvc= qobject_cast<MultiViewer_Container *>(parentSplitter);
47 	}
48 	return mvc;
49 }
50 
SplitterHandle(Qt::Orientation orientation,QSplitter * parent)51 SplitterHandle::SplitterHandle(Qt::Orientation orientation, QSplitter *parent):QSplitterHandle(orientation, parent){}
52 
mousePressEvent(QMouseEvent * e)53 void SplitterHandle::mousePressEvent ( QMouseEvent * e )
54 {
55 	QSplitterHandle::mousePressEvent(e);
56 
57 	if(e->button()== Qt::RightButton)
58 	{
59 		MainWindow *window = qobject_cast<MainWindow *>(QApplication::activeWindow());
60 		if (window) window->setHandleMenu(mapToGlobal(e->pos()), orientation(), splitter());
61 	}
62 }
63 
MultiViewer_Container(vcg::QtThreadSafeMemoryInfo & meminfo,bool highprec,size_t perbatchprimitives,size_t minfacespersmoothrendering,QWidget * parent)64 MultiViewer_Container::MultiViewer_Container(vcg::QtThreadSafeMemoryInfo& meminfo, bool highprec,size_t perbatchprimitives, size_t minfacespersmoothrendering,QWidget *parent)
65     : Splitter(parent),meshDoc()
66 {
67 	setChildrenCollapsible(false);
68     scenecontext = new MLSceneGLSharedDataContext(meshDoc,meminfo,highprec,perbatchprimitives,minfacespersmoothrendering);
69 	scenecontext->setHidden(true);
70 	scenecontext->initializeGL();
71 	currentId=-1;
72 	currentgla = NULL;
73 }
74 
~MultiViewer_Container()75 MultiViewer_Container::~MultiViewer_Container()
76 {
77     /*for(int ii = 0;ii < viewerList.size();++ii)
78         delete viewerList[ii];*/
79 
80     //WARNING!!!! here it's just destroyed the pointer to the MLSceneGLSharedDataContext
81     //the data contained in the GPU are deallocated in the closeEvent function
82     delete scenecontext;
83 }
84 
getNextViewerId()85 int MultiViewer_Container::getNextViewerId(){
86     int newId=-1;
87 
88 	foreach(GLArea* view, viewerList)
89 	{
90 		if(newId < view->getId()) newId = view->getId();
91 	}
92 
93 	return ++newId;
94 }
95 
96 
97 /*********************************************************************************************************/
98 /*********************************************************************************************************/
99 /*WARNING!!!!!!!!!!!! Horizontal and Vertical in QT are the opposite on how we consider them in Meshlab*/
100 /*********************************************************************************************************/
101 /*********************************************************************************************************/
102 
103 
addView(GLArea * viewer,Qt::Orientation orient)104 void MultiViewer_Container::addView(GLArea* viewer,Qt::Orientation orient)
105 {
106 
107     MLRenderingData dt;
108     MainWindow *window = qobject_cast<MainWindow *>(QApplication::activeWindow());
109     if ((window != NULL) && (scenecontext != NULL))
110     {
111         //window->defaultPerViewRenderingData(dt);
112         scenecontext->addView(viewer->context(),dt);
113     }
114     /* The Viewers are organized like a BSP tree.
115 	Every new viewer is added within an Horizontal splitter. Its orientation could change according to next insertions.
116 	  HSplit
117 	/       \
118 	View1   VSplit
119 	        /   \
120 	      View2  View3
121 
122 	In the GUI, when a viewer is split, the new one appears on its right (the space is split in two equal portions).
123 	*/
124 	//CASE 0: only when the first viewer is opened, just add it and return;
125 	if (viewerCounter()==0)
126 	{
127 		viewerList.append(viewer);
128 		addWidget(viewer);
129 		updateCurrent(viewer->getId());
130 		//action for new viewer
131 		connect(viewer, SIGNAL(currentViewerChanged(int)), this, SLOT(updateCurrent(int)));
132 		return;
133 	}
134 
135 	//CASE 1: happens only at the FIRST split;
136 	if (viewerCounter()==1)
137 	{
138 		viewerList.append(viewer);
139 		this->setOrientation(orient);
140         addWidget(viewer);
141 		QList<int> sizes;
142 		if(this->orientation()== Qt::Horizontal){
143 			sizes.append(this->width()/2);
144 			sizes.append(this->width()/2);
145 		}
146 		else{
147 			sizes.append(this->height()/2);
148 			sizes.append(this->height()/2);
149 		}
150 
151 		this->setSizes(sizes);
152 		this->setHandleWidth(2);
153 		this->setChildrenCollapsible(false);
154 
155 		updateCurrent(viewer->getId());
156 		//action for new viewer
157 		connect(viewer, SIGNAL(currentViewerChanged(int)), this, SLOT(updateCurrent(int)));
158 		return;
159 	}
160 
161 	// Generic Case: Each splitter Has ALWAYS two children.
162 	viewerList.append(viewer);
163 	GLArea* currentGLA = this->currentView();
164 	Splitter* currentSplitter = qobject_cast<Splitter *>(currentGLA->parent());
165 	QList<int> parentSizes = currentSplitter->sizes();
166 
167 	int splittedIndex = currentSplitter->indexOf(currentGLA);
168 	Splitter* newSplitter = new Splitter(orient);
169 	currentSplitter->insertWidget(splittedIndex,newSplitter);
170 
171 	newSplitter->addWidget(viewer);
172 	newSplitter->addWidget(currentGLA);
173 
174 	QList<int> sizes;
175 	if(orient== Qt::Horizontal){
176 		sizes.append(currentSplitter->width()/2);
177 		sizes.append(currentSplitter->width()/2);
178 	}
179 	else{
180 		sizes.append(currentSplitter->height()/2);
181 		sizes.append(currentSplitter->height()/2);
182 	}
183 	currentSplitter->setSizes(parentSizes);
184 	newSplitter->setSizes(sizes);
185 	newSplitter->setHandleWidth(2);
186 	newSplitter->setChildrenCollapsible(false);
187 
188 	updateCurrent(viewer->getId());
189 	//action for new viewer
190 	connect(viewer, SIGNAL(currentViewerChanged(int)), this, SLOT(updateCurrent(int)));
191 	return;
192 }
193 
removeView(int viewerId)194 void MultiViewer_Container::removeView(int viewerId)
195 {
196 	GLArea* viewer = NULL;
197 	for (int i=0; i< viewerList.count(); i++)
198 	{
199 		if(viewerList.at(i)->getId() == viewerId)
200 			viewer = viewerList.at(i);
201 	}
202 	assert(viewer);
203     if (viewer != NULL)
204         scenecontext->removeView(viewer->context());
205     Splitter* parentSplitter = qobject_cast<Splitter *>(viewer->parent());
206 	int currentIndex = parentSplitter->indexOf(viewer);
207 
208     viewer->deleteLater();
209 	// Very basic case of just two son of the MultiviewContainer.
210 	if(viewerList.count()==2)
211 	{
212 		viewerList.removeAll(viewer);
213         updateCurrent(viewerList.first()->getId());
214 		return;
215 	}
216 
217 	// generic tree with more of two leaves (some splitter involved)
218 	// two cases
219 	// 1) the deleted object is a direct child of the root
220 	// 2) otherwise; e.g. parent->parent exists.
221 
222 
223 	// First Case: deleting the direct son of the root (the mvc)
224 	// the sibling content (that is a splitter) surely will be moved up
225 	if(parentSplitter == this)
226 	{
227 		int insertIndex;
228 		if(currentIndex == 0) insertIndex = 1;
229 		else insertIndex = 0;
230 
231 		Splitter *siblingSplitter = qobject_cast<Splitter *>(this->widget(insertIndex));
232 		assert(siblingSplitter);
233         siblingSplitter->hide();
234         siblingSplitter->deleteLater();
235 
236 		QWidget *sonLeft = siblingSplitter->widget(0);
237 		QWidget *sonRight = siblingSplitter->widget(1);
238 		this->setOrientation(siblingSplitter->orientation());
239         this->insertWidget(0,sonLeft);
240 		this->insertWidget(1,sonRight);
241 
242         patchForCorrectResize(this);
243 		viewerList.removeAll(viewer);
244 		//currentId = viewerList.first()->getId();
245 		updateCurrent(viewerList.first()->getId());
246         return;
247 	}
248 
249 	// Final case. Very generic, not son of the root.
250 
251 	Splitter* parentParentSplitter = qobject_cast<Splitter *>(parentSplitter->parent());
252 	assert(parentParentSplitter);
253 	int parentIndex= parentParentSplitter->indexOf(parentSplitter);
254 
255 	int siblingIndex;
256 	if(currentIndex == 0) siblingIndex = 1;
257 	else siblingIndex = 0;
258 
259 	QWidget  *siblingWidget = parentSplitter->widget(siblingIndex);
260 
261     parentSplitter->hide();
262     parentSplitter->deleteLater();
263     parentParentSplitter->insertWidget(parentIndex,siblingWidget);
264 
265     patchForCorrectResize(parentParentSplitter);
266 	viewerList.removeAll(viewer);
267 	updateCurrent(viewerList.first()->getId());
268 }
269 
updateCurrent(int current)270 void MultiViewer_Container::updateCurrent(int current)
271 {
272 	int previousCurrentId = currentId;
273 	currentId=current;
274 	currentgla = getViewer(currentId);
275 	if(getViewer(previousCurrentId))
276 		update(previousCurrentId);
277     emit updateMainWindowMenus();
278     if (current != previousCurrentId)
279         emit updateDocumentViewer();
280 }
281 
getViewer(int id)282 GLArea * MultiViewer_Container::getViewer(int id)
283 {
284 	foreach ( GLArea* viewer, viewerList)
285 		if ((viewer != NULL) && (viewer->getId() == id))
286 			return viewer;
287 	return 0;
288 }
289 
getViewerByPicking(QPoint p)290 int MultiViewer_Container::getViewerByPicking(QPoint p){
291 	foreach ( GLArea* viewer, viewerList)
292 	{
293 		if (viewer != NULL)
294 		{
295 			QPoint pViewer = viewer->mapFromGlobal(p);
296 			if (viewer->visibleRegion().contains(pViewer))
297 				return viewer->getId();
298 		}
299 	}
300 	return -1;
301 }
302 
currentView()303 GLArea* MultiViewer_Container::currentView(){
304 	return getViewer(currentId);
305 }
306 
viewerCounter()307 int MultiViewer_Container::viewerCounter(){
308 	return viewerList.count();
309 }
310 
updateAllViewers()311 void MultiViewer_Container::updateAllViewers(){
312 	foreach(GLArea* viewer, viewerList)
313 	{
314 		if (viewer != NULL)
315 			viewer->update();
316 	}
317 }
318 
updateAllDecoratorsForAllViewers()319 void MultiViewer_Container::updateAllDecoratorsForAllViewers()
320 {
321 	foreach(GLArea* viewer, viewerList)
322 	{
323 		if (viewer != NULL)
324 			viewer->updateAllPerMeshDecorators();
325 	}
326 }
327 
328 
329 
resetAllTrackBall()330 void MultiViewer_Container::resetAllTrackBall()
331 {
332 	foreach(GLArea* viewer, viewerList)
333 	{
334 		if (viewer != NULL)
335 			viewer->resetTrackBall();
336 	}
337 }
338 
update(int id)339 void MultiViewer_Container::update(int id){
340 	getViewer(id)->update();
341 }
342 
updateTrackballInViewers()343 void MultiViewer_Container::updateTrackballInViewers()
344 {
345 	GLArea* glArea = currentView();
346 	if(glArea)
347 	{
348 		QPair<Shotm,float> shotAndScale = glArea->shotFromTrackball();
349 		foreach(GLArea* viewer, viewerList)
350 			if(viewer->getId() != currentId){
351 				((GLArea*) viewer)->loadShot(shotAndScale);
352 			}
353 	}
354 }
355 
closeEvent(QCloseEvent * event)356 void MultiViewer_Container::closeEvent( QCloseEvent *event )
357 {
358 	if (meshDoc.hasBeenModified())
359 	{
360 		QMessageBox::StandardButton ret=QMessageBox::question(
361 			this,  tr("MeshLab"), tr("Project '%1' modified.\n\nClose without saving?").arg(meshDoc.docLabel()),
362 			QMessageBox::Yes|QMessageBox::No,
363 			QMessageBox::No);
364 
365 		if(ret==QMessageBox::No)	// don't close please!
366 		{
367 			event->ignore();
368 			return;
369 		}
370 	}
371 	bool close = true;
372 	int ii = 0;
373     scenecontext->deAllocateGPUSharedData();
374 	while(close && (ii < viewerList.size()))
375 	{
376 		close = viewerList.at(ii)->readyToClose();
377 		++ii;
378 	}
379 
380 	if (close)
381 	{
382 		emit closingMultiViewerContainer();
383 		event->accept();
384 	}
385 	else
386 		event->ignore();
387 }
388 
patchForCorrectResize(QSplitter * split)389 void MultiViewer_Container::patchForCorrectResize( QSplitter* split )
390 {
391     /***************************patch to avoid a qt problem**********************/
392     /*in qt it's not possible to remove a widget from a splitter (no comment....).
393     it's not sufficient to hide it.
394     it looks like that anyway the framework allocates space on the screen also for the hidden splitter.
395     So we have to resize all the visible glareas to half of the size of the new splitter in which they are going to be inserted and set,
396     by hand, to zero the size of the splitter that is going to be deleted */
397     /***************************************************************************/
398 
399     QSize sz = split->size();
400     int newsz = 0;
401     if(split->orientation() == Qt::Horizontal)
402         newsz = sz.width()/2;
403     else
404         newsz = sz.height()/2;
405 
406     QList<int> newwigsizes;
407     for(int ii = 0;ii < split->count();++ii)
408     {
409         QWidget* tmpwid = split->widget(ii);
410         if (tmpwid->isVisible())
411             newwigsizes.push_back(newsz);
412         else
413             newwigsizes.push_back(0);
414     }
415 
416     split->setSizes(newwigsizes);
417 }
418