1 /*
2 # PostgreSQL Database Modeler (pgModeler)
3 #
4 # Copyright 2006-2020 - Raphael Araújo e Silva <raphael@pgmodeler.io>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation version 3.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # The complete text of GPLv3 is at LICENSE file on source code root directory.
16 # Also, you can get the complete GNU General Public License at <http://www.gnu.org/licenses/>
17 */
18 
19 #include "objectsscene.h"
20 
21 bool ObjectsScene::align_objs_grid=false;
22 bool ObjectsScene::show_grid=true;
23 bool ObjectsScene::show_page_delim=true;
24 unsigned ObjectsScene::grid_size=20;
25 QPrinter::PageSize ObjectsScene::paper_size=QPrinter::A4;
26 QPrinter::Orientation ObjectsScene::page_orientation=QPrinter::Landscape;
27 QRectF ObjectsScene::page_margins=QRectF(2,2,2,2);
28 QSizeF ObjectsScene::custom_paper_size=QSizeF(0,0);
29 QBrush ObjectsScene::grid;
30 bool ObjectsScene::corner_move=true;
31 bool ObjectsScene::invert_rangesel_trigger=false;
32 
ObjectsScene()33 ObjectsScene::ObjectsScene()
34 {
35 	layers.push_back(tr("Default layer"));
36 	active_layers.push_back(layers.at(0));
37 
38 	moving_objs=move_scene=false;
39 	enable_range_sel=true;
40 	this->setBackgroundBrush(grid);
41 
42 	sel_ini_pnt.setX(DNaN);
43 	sel_ini_pnt.setY(DNaN);
44 
45 	selection_rect=new QGraphicsPolygonItem;
46 	selection_rect->setVisible(false);
47 	selection_rect->setZValue(100);
48 
49 	rel_line=new QGraphicsLineItem;
50 	rel_line->setVisible(false);
51 	rel_line->setZValue(-1);
52 	rel_line->setPen(QColor(80,80,80));
53 
54 	this->addItem(selection_rect);
55 	this->addItem(rel_line);
56 
57 	scene_move_dx=scene_move_dy=0;
58 
59 	connect(&scene_move_timer, SIGNAL(timeout()), this, SLOT(moveObjectScene()));
60 	connect(&corner_hover_timer, SIGNAL(timeout()), this, SLOT(enableSceneMove()));
61 
62 	connect(&object_move_timer, &QTimer::timeout, [&](){
63 		//If the timer reaches its timeout we execute the procedures to finish the objects movement
64 		finishObjectsMove(itemsBoundingRect(true, true).center());
65 		object_move_timer.stop();
66 	});
67 
68 	scene_move_timer.setInterval(SceneMoveTimeout);
69 	corner_hover_timer.setInterval(SceneMoveTimeout * 10);
70 	object_move_timer.setInterval(SceneMoveTimeout * 10);
71 }
72 
~ObjectsScene()73 ObjectsScene::~ObjectsScene()
74 {
75 	QGraphicsItemGroup *item=nullptr;
76 	QList<QGraphicsItem *> items;
77 	vector<ObjectType> obj_types={ ObjectType::Relationship, ObjectType::Textbox, ObjectType::View,
78 																 ObjectType::Table, ObjectType::ForeignTable, ObjectType::Schema };
79 
80 	this->removeItem(selection_rect);
81 	this->removeItem(rel_line);
82 
83 	delete selection_rect;
84 	delete rel_line;
85 
86 	//Destroy the objects in the order defined on obj_types vector
87 	for(auto &type : obj_types)
88 	{
89 		items=this->items();
90 
91 		while(!items.isEmpty())
92 		{
93 			/* Try to convert the item to QGraphicsItemGroup because all the objects
94 			used to represent database object are derived from this class */
95 			item=dynamic_cast<QGraphicsItemGroup *>(items.front());
96 
97 			/* Case the object is converted to a item group and can be converted to database
98 			objects, indicates that the object can be removed from the scene */
99 			if(item && !item->parentItem() &&
100 					((dynamic_cast<RelationshipView *>(item) && type==ObjectType::Relationship) ||
101 					 (dynamic_cast<TextboxView *>(item) && type==ObjectType::Textbox) ||
102 					 (dynamic_cast<StyledTextboxView *>(item) && type==ObjectType::Textbox) ||
103 					 (dynamic_cast<GraphicalView *>(item) && type==ObjectType::View) ||
104 					 (dynamic_cast<TableView *>(item) && (type==ObjectType::Table || type==ObjectType::ForeignTable)) ||
105 					 (dynamic_cast<SchemaView *>(item) && type==ObjectType::Schema)))
106 
107 			{
108 				this->removeItem(item);
109 			}
110 
111 			items.pop_front();
112 		}
113 	}
114 
115 	//The graphical representation of db objects must be destroyed in a sorted way
116 	std::sort(removed_objs.begin(), removed_objs.end());
117 	while(!removed_objs.empty())
118 	{
119 		delete removed_objs.back();
120 		removed_objs.pop_back();
121 	}
122 }
123 
formatLayerName(const QString & name)124 QString ObjectsScene::formatLayerName(const QString &name)
125 {
126 	QString fmt_name;
127 	unsigned idx = 1;
128 
129 	//Removing invalid chars
130 	for(auto &chr : name)
131 	{
132 		if(chr.isLetterOrNumber() || chr == ' ' || chr == '_')
133 			fmt_name.append(chr);
134 		else
135 			fmt_name.append('_');
136 	}
137 
138 	//Doing the desambiguation (if needed)
139 	while(layers.contains(fmt_name))
140 		fmt_name = QString("%1 %2").arg(name).arg(QString::number(idx++));
141 
142 	return fmt_name;
143 }
144 
addLayer(const QString & name)145 QString ObjectsScene::addLayer(const QString &name)
146 {
147 	if(name.isEmpty())
148 		return "";
149 
150 	QString fmt_name = formatLayerName(name);
151 	layers.push_back(fmt_name);
152 
153 	emit s_layersChanged();
154 	return fmt_name;
155 }
156 
renameLayer(unsigned idx,const QString & name)157 QString ObjectsScene::renameLayer(unsigned idx, const QString &name)
158 {
159 	if(name.isEmpty() || idx >= static_cast<unsigned>(layers.size()))
160 		return "";
161 
162 	if(name != layers[idx])
163 		layers[idx] = formatLayerName(name);
164 
165 	emit s_layersChanged();
166 	return layers[idx];
167 }
168 
removeLayer(const QString & name)169 void ObjectsScene::removeLayer(const QString &name)
170 {
171 	int idx = layers.indexOf(name);
172 
173 	if(idx > 0)
174 	{
175 		moveObjectsToLayer(idx, DefaultLayer);
176 		layers.removeAll(name);
177 		active_layers.removeAll(name);
178 		emit s_layersChanged();
179 	}
180 }
181 
removeLayers()182 void ObjectsScene::removeLayers()
183 {
184 	BaseObjectView *obj_view = nullptr;
185 	QString def_layer = layers[DefaultLayer];
186 	bool is_active = active_layers.contains(def_layer);
187 
188 	layers.clear();
189 	active_layers.clear();
190 	layers.push_back(def_layer);
191 
192 	if(is_active)
193 		active_layers.push_back(def_layer);
194 
195 	for(auto &item : this->items())
196 	{
197 		obj_view = dynamic_cast<BaseObjectView *>(item);
198 
199 		if(obj_view && !obj_view->parentItem() && obj_view->getLayer() != DefaultLayer)
200 		{
201 			obj_view->setLayer(DefaultLayer);
202 			obj_view->setVisible(is_active);
203 		}
204 	}
205 
206 	emit s_layersChanged();
207 	updateActiveLayers();
208 }
209 
setActiveLayers(QStringList act_layers)210 void ObjectsScene::setActiveLayers(QStringList act_layers)
211 {
212 	QList<unsigned> layers_idxs;
213 	int idx = -1;
214 
215 	for(auto &layer : act_layers)
216 	{
217 		idx = layers.indexOf(layer);
218 
219 		if(idx >= 0)
220 			layers_idxs.push_back(idx);
221 	}
222 
223 	setActiveLayers(layers_idxs);
224 }
225 
setActiveLayers(QList<unsigned> layers_idxs)226 void ObjectsScene::setActiveLayers(QList<unsigned> layers_idxs)
227 {
228 	BaseObjectView *obj_view = nullptr;
229 	active_layers.clear();
230 
231 	if(!layers_idxs.isEmpty())
232 	{
233 		bool is_in_layer = false;
234 		unsigned layer_cnt = static_cast<unsigned>(layers.size());
235 		SchemaView *sch_view = nullptr;
236 
237 		for(auto &item : this->items())
238 		{
239 			obj_view = dynamic_cast<BaseObjectView *>(item);
240 
241 			if(obj_view && !obj_view->parentItem() && obj_view->getLayer() < layer_cnt)
242 			{
243 				sch_view = dynamic_cast<SchemaView *>(obj_view);
244 				is_in_layer = layers_idxs.contains(obj_view->getLayer());
245 
246 				if(!obj_view->isVisible() && is_in_layer)
247 				{
248 					if(!sch_view ||
249 						 (sch_view && dynamic_cast<Schema *>(sch_view->getUnderlyingObject())->isRectVisible()))
250 					 obj_view->setVisible(true);
251 				}
252 				else if(obj_view->isVisible() && !is_in_layer)
253 					obj_view->setVisible(false);
254 			}
255 		}
256 
257 		for(auto &idx : layers_idxs)
258 		{
259 			if(idx < layer_cnt)
260 				active_layers.push_back(layers[idx]);
261 		}
262 	}
263 	else
264 	{
265 		for(auto &item : this->items())
266 		{
267 			obj_view = dynamic_cast<BaseObjectView *>(item);
268 
269 			if(obj_view && !obj_view->parentItem())
270 				obj_view->setVisible(false);
271 		}
272 	}
273 
274 	emit s_activeLayersChanged();
275 }
276 
moveObjectsToLayer(unsigned old_layer,unsigned new_layer)277 void ObjectsScene::moveObjectsToLayer(unsigned old_layer, unsigned new_layer)
278 {
279 	BaseObjectView *obj_view = nullptr;
280 	unsigned total_layers = layers.size();
281 
282 	if(old_layer == new_layer || old_layer >= total_layers || new_layer >= total_layers)
283 		return;
284 
285 	for(auto &item : this->items())
286 	{
287 		obj_view = dynamic_cast<BaseObjectView *>(item);
288 
289 		if(obj_view && !obj_view->parentItem() && obj_view->getLayer() == old_layer)
290 		{
291 			obj_view->setLayer(new_layer);
292 			obj_view->setVisible(isLayerActive(layers[new_layer]));
293 		}
294 	}
295 
296 	emit s_objectsMovedLayer();
297 }
298 
isLayerActive(const QString & name)299 bool ObjectsScene::isLayerActive(const QString &name)
300 {
301 	return active_layers.contains(name);
302 }
303 
isLayerActive(unsigned layer_id)304 bool ObjectsScene::isLayerActive(unsigned layer_id)
305 {
306 	if(layer_id >= static_cast<unsigned>(layers.size()))
307 		return false;
308 
309 	return active_layers.contains(layers[layer_id]);
310 }
311 
getActiveLayers()312 QStringList ObjectsScene::getActiveLayers()
313 {
314 	return active_layers;
315 }
316 
getActiveLayersIds()317 QList<unsigned> ObjectsScene::getActiveLayersIds()
318 {
319 	QList<unsigned> list;
320 
321 	for(auto &layer : active_layers)
322 		list.push_back(layers.indexOf(layer));
323 
324 	return list;
325 }
326 
getLayers()327 QStringList ObjectsScene::getLayers()
328 {
329 	return layers;
330 }
331 
getLayerId(const QString & name)332 unsigned ObjectsScene::getLayerId(const QString &name)
333 {
334 	int idx = layers.contains(name);
335 	return idx < 0 ? InvalidLayer : static_cast<unsigned>(idx);
336 }
337 
updateActiveLayers()338 void ObjectsScene::updateActiveLayers()
339 {
340 	setActiveLayers(active_layers);
341 }
342 
setEnableCornerMove(bool enable)343 void ObjectsScene::setEnableCornerMove(bool enable)
344 {
345 	ObjectsScene::corner_move=enable;
346 }
347 
setInvertRangeSelectionTrigger(bool invert)348 void ObjectsScene::setInvertRangeSelectionTrigger(bool invert)
349 {
350 	ObjectsScene::invert_rangesel_trigger=invert;
351 }
352 
isCornerMoveEnabled()353 bool ObjectsScene::isCornerMoveEnabled()
354 {
355 	return ObjectsScene::corner_move;
356 }
357 
alignPointToGrid(const QPointF & pnt)358 QPointF ObjectsScene::alignPointToGrid(const QPointF &pnt)
359 {
360 	int px = static_cast<int>(round(pnt.x()/static_cast<double>(grid_size))) * grid_size,
361 			py = static_cast<int>(round(pnt.y()/static_cast<double>(grid_size))) * grid_size;
362 
363 	if(px < 0) px = 0;
364 	if(py < 0) py = 0;
365 
366 	return QPointF(px,	py);
367 }
368 
setSceneRect(const QRectF & rect)369 void ObjectsScene::setSceneRect(const QRectF &rect)
370 {
371 	QGraphicsScene::setSceneRect(0, 0, rect.width(), rect.height());
372 }
373 
itemsBoundingRect(bool seek_only_db_objs,bool selected_only)374 QRectF ObjectsScene::itemsBoundingRect(bool seek_only_db_objs, bool selected_only)
375 {
376 	if(!seek_only_db_objs)
377 		return QGraphicsScene::itemsBoundingRect();
378 	else
379 	{
380 		QRectF rect=QGraphicsScene::itemsBoundingRect();
381 		QList<QGraphicsItem *> items= (selected_only ? this->selectedItems() : this->items());
382 		double x=rect.width(), y=rect.height(), x2 = -10000, y2 = -10000;
383 		BaseObjectView *obj_view=nullptr;
384 		QPointF pnt;
385 		BaseGraphicObject *graph_obj=nullptr;
386 
387 		for(auto &item : items)
388 		{
389 			obj_view=dynamic_cast<BaseObjectView *>(item);
390 
391 			if(obj_view && obj_view->isVisible())
392 			{
393 				graph_obj=dynamic_cast<BaseGraphicObject *>(obj_view->getUnderlyingObject());
394 
395 				if(graph_obj)
396 				{
397 					if(graph_obj->getObjectType()!=ObjectType::Relationship &&
398 							graph_obj->getObjectType()!=ObjectType::BaseRelationship)
399 						pnt=graph_obj->getPosition();
400 					else
401 						pnt=dynamic_cast<RelationshipView *>(obj_view)->__boundingRect().topLeft();
402 
403 					if(pnt.x() < x)
404 						x=pnt.x();
405 
406 					if(pnt.y() < y)
407 						y=pnt.y();
408 
409 					if(selected_only)
410 					{
411 						if(graph_obj->getObjectType()!=ObjectType::Relationship &&
412 							 graph_obj->getObjectType()!=ObjectType::BaseRelationship)
413 							pnt = pnt + dynamic_cast<BaseObjectView *>(obj_view)->boundingRect().bottomRight();
414 						else
415 							pnt = pnt +  dynamic_cast<RelationshipView *>(obj_view)->__boundingRect().bottomRight();
416 
417 						if(pnt.x() > x2)
418 							x2 = pnt.x();
419 
420 						if(pnt.y() > y2)
421 							y2 = pnt.y();
422 					}
423 				}
424 			}
425 		}
426 
427 		if(selected_only)
428 			return QRectF(QPointF(x, y), QPointF(x2, y2));
429 		else
430 			return QRectF(QPointF(x, y), rect.bottomRight());
431 	}
432 }
433 
setGridSize(unsigned size)434 void ObjectsScene::setGridSize(unsigned size)
435 {
436 	if(size >= 20 || grid.style()==Qt::NoBrush)
437 	{
438 		QImage grid_img;
439 		double width, height, x, y;
440 		int img_w, img_h;
441 		QSizeF aux_size;
442 		QPrinter printer;
443 		QPainter painter;
444 		QPen pen;
445 
446 		configurePrinter(&printer);
447 		aux_size=printer.paperSize(QPrinter::Point);
448 		aux_size-=page_margins.size();
449 
450 		//Calculates where the extreme width and height where delimiter lines will be drawn
451 		width=aux_size.width()/static_cast<double>(size) * size;
452 		height=aux_size.height()/static_cast<double>(size) * size;
453 
454 		//Calculates the grid pixmpa size
455 		img_w=ceil(width/size) * size;
456 		img_h=ceil(height/size) * size;
457 
458 		grid_size=size;
459 		grid_img=QImage(img_w, img_h, QImage::Format_ARGB32);
460 		grid_img.fill(Qt::white);
461 		painter.begin(&grid_img);
462 
463 		if(show_grid)
464 		{
465 			pen.setColor(QColor(225, 225, 225));
466 			painter.setPen(pen);
467 
468 			//Draws the grid
469 			for(x=0; x < width; x+=size)
470 				for(y=0; y < height; y+=size)
471 					painter.drawRect(QRectF(QPointF(x,y),QPointF(x + size,y + size)));
472 		}
473 
474 		//Creates the page delimiter lines
475 		if(show_page_delim)
476 		{
477 			pen.setColor(QColor(75,115,195));
478 			pen.setStyle(Qt::DashLine);
479 			pen.setWidthF(1.0);
480 			painter.setPen(pen);
481 			painter.drawLine(width-1, 0,width-1,img_h-1);
482 			painter.drawLine(0, height-1,img_w-1,height-1);
483 		}
484 
485 		painter.end();
486 		grid.setTextureImage(grid_img);
487 	}
488 }
489 
showRelationshipLine(bool value,const QPointF & p_start)490 void ObjectsScene::showRelationshipLine(bool value, const QPointF &p_start)
491 {
492 	QList<QGraphicsItem *> items=this->items();
493 	QGraphicsItem::GraphicsItemFlags flags;
494 	BaseObjectView *object=nullptr;
495 	TableObjectView *tab_obj_view=nullptr;
496 	BaseGraphicObject *base_obj=nullptr;
497 
498 	if(!std::isnan(p_start.x()) && !std::isnan(p_start.y()))
499 		rel_line->setLine(QLineF(p_start,p_start));
500 
501 	rel_line->setVisible(value);
502 
503 	while(!items.isEmpty())
504 	{
505 		//When showing the relationship line all the objects cannot be moved
506 		flags=QGraphicsItem::ItemIsSelectable |
507 					QGraphicsItem::ItemSendsGeometryChanges;
508 
509 		object = dynamic_cast<BaseObjectView *>(items.front());
510 		tab_obj_view = dynamic_cast<TableObjectView *>(object);
511 
512 		// Discarding table objects views from checking since they can't be normally be selected by a single click
513 		if(object && !tab_obj_view && object->getUnderlyingObject())
514 		{
515 			BaseObject *ul_object = object->getUnderlyingObject();
516 			base_obj=dynamic_cast<BaseGraphicObject *>(ul_object);
517 
518 			if(!value && base_obj &&
519 					base_obj->getObjectType()!=ObjectType::Relationship &&
520 					base_obj->getObjectType()!=ObjectType::BaseRelationship &&
521 					!base_obj->isProtected())
522 				flags=QGraphicsItem::ItemIsMovable |
523 					  QGraphicsItem::ItemIsSelectable |
524 					  QGraphicsItem::ItemSendsGeometryChanges;
525 			else
526 				flags=QGraphicsItem::ItemIsSelectable |
527 					  QGraphicsItem::ItemSendsGeometryChanges;
528 		}
529 
530 		items.front()->setFlags(flags);
531 		items.pop_front();
532 	}
533 }
534 
setGridOptions(bool show_grd,bool align_objs_grd,bool show_pag_dlm)535 void ObjectsScene::setGridOptions(bool show_grd, bool align_objs_grd, bool show_pag_dlm)
536 {
537 	bool redef_grid=(ObjectsScene::show_grid!=show_grd ||
538 										ObjectsScene::show_page_delim!=show_pag_dlm ||
539 										grid.style()==Qt::NoBrush);
540 
541 	ObjectsScene::show_grid=show_grd;
542 	ObjectsScene::show_page_delim=show_pag_dlm;
543 	ObjectsScene::align_objs_grid=align_objs_grd;
544 
545 	if(redef_grid)
546 	{
547 		grid.setStyle(Qt::NoBrush);
548 		setGridSize(ObjectsScene::grid_size);
549 	}
550 }
551 
isAlignObjectsToGrid()552 bool ObjectsScene::isAlignObjectsToGrid()
553 {
554 	return align_objs_grid;
555 }
556 
isShowGrid()557 bool ObjectsScene::isShowGrid()
558 {
559 	return show_grid;
560 }
561 
isShowPageDelimiters()562 bool ObjectsScene::isShowPageDelimiters()
563 {
564 	return show_page_delim;
565 }
566 
setPaperConfiguration(QPrinter::PaperSize paper_sz,QPrinter::Orientation orient,QRectF margins,QSizeF custom_size)567 void ObjectsScene::setPaperConfiguration(QPrinter::PaperSize paper_sz, QPrinter::Orientation orient, QRectF margins, QSizeF custom_size)
568 {
569 	ObjectsScene::paper_size=paper_sz;
570 	ObjectsScene::page_orientation=orient;
571 	ObjectsScene::page_margins=margins;
572 	ObjectsScene::custom_paper_size=custom_size;
573 }
574 
getPaperConfiguration(QPrinter::PaperSize & paper_sz,QPrinter::Orientation & orient,QRectF & margins,QSizeF & custom_size)575 void ObjectsScene::getPaperConfiguration(QPrinter::PaperSize &paper_sz, QPrinter::Orientation &orient, QRectF &margins, QSizeF &custom_size)
576 {
577 	paper_sz=ObjectsScene::paper_size;
578 	orient=ObjectsScene::page_orientation;
579 	margins=ObjectsScene::page_margins;
580 	custom_size=ObjectsScene::custom_paper_size;
581 }
582 
configurePrinter(QPrinter * printer)583 void ObjectsScene::configurePrinter(QPrinter *printer)
584 {
585 	if(!printer)
586 		throw Exception(ErrorCode::OprNotAllocatedObject ,__PRETTY_FUNCTION__,__FILE__,__LINE__);
587 
588 	if(paper_size!=QPrinter::Custom)
589 		printer->setPaperSize(paper_size);
590 	else
591 	{
592 		QPageLayout pl;
593 		QPageSize ps;
594 		ps=QPageSize(QSizeF(custom_paper_size.width(), custom_paper_size.height()), QPageSize::Point, "", QPageSize::ExactMatch);
595 		pl.setPageSize(ps);
596 		pl.setOrientation(page_orientation==QPrinter::Landscape ? QPageLayout::Landscape : QPageLayout::Portrait);
597 		printer->setPageSize(pl.pageSize());
598 	}
599 
600 	if(paper_size==QPrinter::Custom)
601 	{
602 		if(custom_paper_size.width() > custom_paper_size.height())
603 			ObjectsScene::page_orientation=QPrinter::Landscape;
604 		else
605 			ObjectsScene::page_orientation=QPrinter::Portrait;
606 	}
607 	else
608 		printer->setOrientation(page_orientation);
609 
610 	printer->setPageMargins(page_margins.left(), page_margins.top(), page_margins.width(), page_margins.height(), QPrinter::Millimeter);
611 }
612 
configurePrinter(QPrinter * printer,const QSizeF & custom_size,QPrinter::Orientation orient)613 void ObjectsScene::configurePrinter(QPrinter *printer, const QSizeF &custom_size, QPrinter::Orientation orient)
614 {
615 	QPrinter::PaperSize orig_page_sz=paper_size;
616 	QPrinter::Orientation orig_orient=page_orientation;
617 	QSizeF orig_custom_sz=custom_paper_size;
618 
619 	paper_size=QPrinter::Custom;
620 	page_orientation=orient;
621 	custom_paper_size=custom_size;
622 
623 	configurePrinter(printer);
624 
625 	paper_size=orig_page_sz;
626 	page_orientation=orig_orient;
627 	custom_paper_size=orig_custom_sz;
628 }
629 
handlePopupMenuRequested(TableObject * child_obj)630 void ObjectsScene::handlePopupMenuRequested(TableObject *child_obj)
631 {
632 	emit s_popupMenuRequested(child_obj);
633 }
634 
handleObjectSelection(BaseGraphicObject * object,bool selected)635 void ObjectsScene::handleObjectSelection(BaseGraphicObject *object, bool selected)
636 {
637 	if(object)
638 		emit s_objectSelected(object, selected);
639 }
640 
handleChildrenSelectionChanged()641 void ObjectsScene::handleChildrenSelectionChanged()
642 {
643 	BaseTableView *tab_view = dynamic_cast<BaseTableView *>(sender());
644 
645 	if(!tab_view)
646 		return;
647 
648 	if(tab_view->getSelectedChidren().empty())
649 		tabs_sel_children.removeAll(tab_view);
650 	else if(!tabs_sel_children.contains(tab_view))
651 		tabs_sel_children.append(tab_view);
652 
653 	emit s_childrenSelectionChanged();
654 }
655 
addItem(QGraphicsItem * item)656 void ObjectsScene::addItem(QGraphicsItem *item)
657 {
658 	if(item)
659 	{
660 		RelationshipView *rel=dynamic_cast<RelationshipView *>(item);
661 		BaseTableView *tab=dynamic_cast<BaseTableView *>(item);
662 		BaseObjectView *obj=dynamic_cast<BaseObjectView *>(item);
663 
664 		if(rel)
665 			connect(rel, SIGNAL(s_relationshipModified(BaseGraphicObject*)), this, SIGNAL(s_objectModified(BaseGraphicObject*)));
666 		else if(tab)
667 		{
668 			connect(tab, SIGNAL(s_popupMenuRequested(TableObject*)), this, SLOT(handlePopupMenuRequested(TableObject*)));
669 			connect(tab, SIGNAL(s_childrenSelectionChanged()), this, SLOT(handleChildrenSelectionChanged()));
670 			connect(tab, SIGNAL(s_collapseModeChanged()), this, SIGNAL(s_collapseModeChanged()));
671 			connect(tab, SIGNAL(s_paginationToggled()), this, SIGNAL(s_paginationToggled()));
672 			connect(tab, SIGNAL(s_currentPageChanged()), this, SIGNAL(s_currentPageChanged()));
673 			connect(tab, SIGNAL(s_sceneClearRequested()), this, SLOT(clearSelection()));
674 		}
675 
676 		if(obj)
677 		{
678 			obj->setVisible(isLayerActive(obj->getLayer()));
679 
680 			// Relationships and schemas don't have their z value changed
681 			if(!rel && !dynamic_cast<SchemaView *>(item))
682 				obj->setZValue(dynamic_cast<BaseGraphicObject *>(obj->getUnderlyingObject())->getZValue());
683 
684 			connect(obj, SIGNAL(s_objectSelected(BaseGraphicObject*,bool)), this, SLOT(handleObjectSelection(BaseGraphicObject*,bool)));
685 		}
686 
687 		QGraphicsScene::addItem(item);
688 	}
689 }
690 
removeItem(QGraphicsItem * item)691 void ObjectsScene::removeItem(QGraphicsItem *item)
692 {
693 	if(item)
694 	{
695 		BaseObjectView *object=dynamic_cast<BaseObjectView *>(item);
696 		RelationshipView *rel=dynamic_cast<RelationshipView *>(item);
697 
698 		if(rel)
699 			rel->disconnectTables();
700 
701 		item->setVisible(false);
702 		item->setActive(false);
703 		QGraphicsScene::removeItem(item);
704 
705 		if(object)
706 		{
707 			disconnect(object, nullptr, this, nullptr);
708 			disconnect(object, nullptr, dynamic_cast<BaseGraphicObject*>(object->getUnderlyingObject()), nullptr);
709 			disconnect(dynamic_cast<BaseGraphicObject*>(object->getUnderlyingObject()), nullptr, object, nullptr);
710 			removed_objs.push_back(object);
711 		}
712 	}
713 }
714 
blockItemsSignals(bool block)715 void ObjectsScene::blockItemsSignals(bool block)
716 {
717 	BaseObjectView *obj_view = nullptr;
718 
719 	for(auto &item : this->items())
720 	{
721 		obj_view = dynamic_cast<BaseObjectView *>(item);
722 		if(obj_view)
723 			obj_view->blockSignals(block);
724 	}
725 }
726 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)727 void ObjectsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
728 {
729 	QGraphicsScene::mouseDoubleClickEvent(event);
730 
731 	if(this->selectedItems().size()==1 && event->buttons()==Qt::LeftButton && !rel_line->isVisible())
732 	{
733 		//Gets the selected graphical object
734 		BaseObjectView *obj=dynamic_cast<BaseObjectView *>(this->selectedItems().at(0));
735 
736 		if(obj)
737 			emit s_objectDoubleClicked(dynamic_cast<BaseGraphicObject *>(obj->getUnderlyingObject()));
738 	}
739 	else
740 		//Emit a signal indicating that no object was selected
741 		emit s_objectDoubleClicked(nullptr);
742 }
743 
mousePressEvent(QGraphicsSceneMouseEvent * event)744 void ObjectsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
745 {
746 	//Gets the item at mouse position
747 	QGraphicsItem* item=this->itemAt(event->scenePos(), QTransform());
748 	bool is_deselection = !this->selectedItems().isEmpty() && !item;
749 
750 	if(selectedItems().empty())
751 		emit s_objectsScenePressed(event->buttons());
752 
753 	/* If the relationship line is visible, indicates that the user is in the middle of
754 	 a relationship creation, thus is needed to inform to the scene to activate the
755 	 the multiselection to be able to select two tables and link them. By default,
756 	 the multiselection modifier is the Control key */
757 	if(rel_line->isVisible())
758 		event->setModifiers(Qt::ControlModifier);
759 
760 	if(is_deselection)
761 		this->blockItemsSignals(true);
762 
763 	/* If we're handling a deselection of the user selected another object whitout being holding Control
764 	 * we need to deselect the tables' children objects too */
765 	if(is_deselection || (event->buttons()==Qt::LeftButton && (event->modifiers() & Qt::ControlModifier) != Qt::ControlModifier))
766 		//Forcing the clear on all selected table children object
767 		clearTablesChildrenSelection();
768 
769 	QGraphicsScene::mousePressEvent(event);
770 
771 	if(is_deselection)
772 	{
773 		this->blockItemsSignals(false);
774 		emit s_objectSelected(nullptr, false);
775 	}
776 
777 	if(event->buttons()==Qt::LeftButton)
778 	{
779 		sel_ini_pnt=event->scenePos();
780 
781 		if((!invert_rangesel_trigger && event->modifiers()==Qt::ShiftModifier) ||
782 				(invert_rangesel_trigger && event->modifiers()==Qt::NoModifier))
783 		{
784 			if(enable_range_sel && this->selectedItems().isEmpty())
785 			{
786 				selection_rect->setVisible(true);
787 				emit s_objectSelected(nullptr,false);
788 			}
789 		}
790 		else
791 		{
792 			//Selects the object (without press control) if the user is creating a relationship
793 			if(item && item->isEnabled() && !item->isSelected() &&  rel_line->isVisible())
794 				item->setSelected(true);
795 		}
796 	}
797 	else if(event->buttons()==Qt::RightButton)
798 	{
799 		//Case there is no item at the mouse position clears the selection on the scene
800 		if(!item)
801 		{
802 			this->clearSelection();
803 			emit s_objectSelected(nullptr,false);
804 		}
805 
806 		emit s_popupMenuRequested();
807 	}
808 }
809 
mouseIsAtCorner()810 bool ObjectsScene::mouseIsAtCorner()
811 {
812 	QGraphicsView *view=getActiveViewport();
813 
814 	if(view)
815 	{
816 		QPoint pos=view->mapFromGlobal(QCursor::pos());
817 		QRect rect=view->rect();
818 
819 		if(rect.contains(pos))
820 		{
821 			if(pos.x() <= SceneMoveThreshold)
822 				scene_move_dx=-SceneMoveStep;
823 			else if(pos.x() >= (view->width() - view->verticalScrollBar()->width() - SceneMoveThreshold))
824 				scene_move_dx=SceneMoveStep;
825 			else
826 				scene_move_dx=0;
827 
828 			if(pos.y() <= SceneMoveThreshold)
829 				scene_move_dy=-SceneMoveStep;
830 			else if(pos.y() >= (view->height() - view->horizontalScrollBar()->height() - SceneMoveThreshold))
831 				scene_move_dy=SceneMoveStep;
832 			else
833 				scene_move_dy=0;
834 
835 			return scene_move_dx!=0 || scene_move_dy!=0;
836 		}
837 		else
838 			return false;
839 	}
840 	else
841 		return false;
842 }
843 
getActiveViewport()844 QGraphicsView *ObjectsScene::getActiveViewport()
845 {
846 	QGraphicsView *view_p=nullptr;
847 
848 	for(auto &view : this->views())
849 	{
850 		if(view && view->isActiveWindow())
851 		{
852 			view_p=view;
853 			break;
854 		}
855 	}
856 
857 	return view_p;
858 }
859 
moveObjectScene()860 void ObjectsScene::moveObjectScene()
861 {
862 	if(scene_move_dx!=0 || scene_move_dy!=0)
863 	{
864 		QGraphicsView *view=getActiveViewport();
865 
866 		if(view && mouseIsAtCorner())
867 		{
868 			view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() + scene_move_dx);
869 			view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() + scene_move_dy);
870 			move_scene=true;
871 		}
872 		else
873 		{
874 			move_scene=false;
875 			scene_move_timer.stop();
876 		}
877 	}
878 }
879 
enableSceneMove(bool value)880 void ObjectsScene::enableSceneMove(bool value)
881 {
882 	if(value)
883 	{
884 		scene_move_timer.start();
885 		corner_hover_timer.stop();
886 	}
887 	else
888 	{
889 		corner_hover_timer.stop();
890 		scene_move_timer.stop();
891 	}
892 
893 	move_scene=value;
894 }
895 
enableRangeSelection(bool value)896 void ObjectsScene::enableRangeSelection(bool value)
897 {
898 	enable_range_sel=value;
899 
900 	if(!value && selection_rect->isVisible())
901 		selection_rect->setVisible(value);
902 }
903 
adjustScenePositionOnKeyEvent(int key)904 void ObjectsScene::adjustScenePositionOnKeyEvent(int key)
905 {
906 	QGraphicsView *view = getActiveViewport();
907 
908 	if(view)
909 	{
910 		QRectF brect = itemsBoundingRect(true, true);
911 		QRectF view_rect = QRectF(view->mapToScene(view->rect().topLeft()),
912 															view->mapToScene(view->rect().bottomRight())),
913 				scene_rect = sceneRect();
914 
915 		if(view_rect.right() < brect.right() && key == Qt::Key_Right)
916 		{
917 			/* If the objects are being moved right and the scene width is lesser than the items bounding rect's width
918 			we need to resize the scene prior the position adjustment */
919 			scene_rect.setRight(brect.right());
920 			setSceneRect(scene_rect);
921 			view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() + ((brect.right() - view_rect.right()) * 2));
922 		}
923 		else if(view_rect.left() > brect.left()  && key == Qt::Key_Left)
924 			view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() - ((view_rect.left() - brect.left()) * 2));
925 
926 		if(view_rect.bottom() < brect.bottom() && key == Qt::Key_Down)
927 		{
928 			/* If the objects are being moved down and the scene hight is lesser than the items bounding rect's height
929 			we need to resize the scene prior the position adjustment */
930 			scene_rect.setBottom(brect.bottom());
931 			setSceneRect(scene_rect);
932 			view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() + ((brect.bottom() - view_rect.bottom()) * 2));
933 		}
934 		else if(view_rect.top() > brect.top()  && key == Qt::Key_Up)
935 			view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() - ((view_rect.top() - brect.top()) * 2));
936 	}
937 }
938 
keyPressEvent(QKeyEvent * event)939 void ObjectsScene::keyPressEvent(QKeyEvent *event)
940 {
941 	if((event->key() == Qt::Key_Up || event->key() == Qt::Key_Down ||
942 			event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) &&
943 		 !selectedItems().isEmpty())
944 	{
945 		double dx = 0, dy = 0;
946 		BaseObjectView *obj_view=nullptr;
947 		QRectF brect = itemsBoundingRect(true, true);
948 
949 		if(!moving_objs)
950 		{
951 			sel_ini_pnt = brect.center();
952 			moving_objs = true;
953 
954 			/* If the object move timer is not active we need to send the
955 			s_objectsMoved() signal in order to alert the classes like ModelWidget to
956 			save the current objects' position in the operation history */
957 			if(!object_move_timer.isActive())
958 				emit s_objectsMoved(false);
959 
960 			for(auto item : selectedItems())
961 			{
962 				obj_view=dynamic_cast<BaseObjectView *>(item);
963 
964 				if(obj_view && BaseObjectView::isPlaceholderEnabled())
965 					obj_view->togglePlaceholder(true);
966 			}
967 		}
968 
969 		if(event->key() == Qt::Key_Up)
970 			dy = -1;
971 		else if(event->key() == Qt::Key_Down)
972 			dy = 1;
973 
974 		if(event->key() == Qt::Key_Left)
975 			dx = -1;
976 		else if(event->key() == Qt::Key_Right)
977 			dx = 1;
978 
979 		if(event->modifiers() == Qt::ControlModifier)
980 		{
981 			dx *= 10;
982 			dy *= 10;
983 		}
984 		else if(event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier))
985 		{
986 			dx *= 100;
987 			dy *= 100;
988 		}
989 
990 		for(auto item : selectedItems())
991 		{
992 			obj_view=dynamic_cast<BaseObjectView *>(item);
993 
994 			if(obj_view && !dynamic_cast<RelationshipView *>(obj_view))
995 				obj_view->moveBy(dx, dy);
996 		}
997 
998 		adjustScenePositionOnKeyEvent(event->key());
999 	}
1000 	else
1001 		QGraphicsScene::keyPressEvent(event);
1002 }
1003 
keyReleaseEvent(QKeyEvent * event)1004 void ObjectsScene::keyReleaseEvent(QKeyEvent *event)
1005 {
1006 	if((event->key() == Qt::Key_Up || event->key() == Qt::Key_Down ||
1007 			event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) &&
1008 		 !event->isAutoRepeat() && !selectedItems().isEmpty())
1009 	{
1010 		if(moving_objs)
1011 		{
1012 			object_move_timer.start();
1013 			adjustScenePositionOnKeyEvent(event->key());
1014 		}
1015 	}
1016 	else
1017 		QGraphicsScene::keyReleaseEvent(event);
1018 }
1019 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)1020 void ObjectsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1021 {
1022 	if(event->buttons()==Qt::LeftButton || rel_line->isVisible())
1023 	{
1024 		if(corner_move)
1025 		{
1026 			if(mouseIsAtCorner())
1027 			{
1028 				if(move_scene)
1029 					scene_move_timer.start();
1030 				else
1031 					corner_hover_timer.start();
1032 			}
1033 			else
1034 				enableSceneMove(false);
1035 		}
1036 
1037 		if(!rel_line->isVisible())
1038 		{
1039 			int sel_items_count = this->selectedItems().size();
1040 
1041 			//Case the user starts a object moviment
1042 			if(sel_items_count != 0 && !moving_objs)
1043 			{
1044 				if(BaseObjectView::isPlaceholderEnabled())
1045 				{
1046 					QList<QGraphicsItem *> items=this->selectedItems();
1047 					BaseObjectView *obj_view=nullptr;
1048 
1049 					for(QGraphicsItem *item : items)
1050 					{
1051 						obj_view=dynamic_cast<BaseObjectView *>(item);
1052 						obj_view->togglePlaceholder(true);
1053 					}
1054 				}
1055 
1056 				emit s_objectsMoved(false);
1057 				moving_objs=true;
1058 			}
1059 
1060 			//If the alignment to grid is active, adjust the event scene position
1061 			if(align_objs_grid && !selection_rect->isVisible() && sel_items_count <= 1)
1062 				event->setScenePos(this->alignPointToGrid(event->scenePos()));
1063 			else if(selection_rect->isVisible())
1064 			{
1065 				QPolygonF pol;
1066 				pol.append(sel_ini_pnt);
1067 				pol.append(QPointF(event->scenePos().x(), sel_ini_pnt.y()));
1068 				pol.append(QPointF(event->scenePos().x(), event->scenePos().y()));
1069 				pol.append(QPointF(sel_ini_pnt.x(), event->scenePos().y()));
1070 				selection_rect->setPolygon(pol);
1071 				selection_rect->setBrush(BaseObjectView::getFillStyle(Attributes::ObjSelection));
1072 				selection_rect->setPen(BaseObjectView::getBorderStyle(Attributes::ObjSelection));
1073 			}
1074 		}
1075 	}
1076 
1077 	if(rel_line->isVisible())
1078 		rel_line->setLine(QLineF(rel_line->line().p1(), event->scenePos()));
1079 
1080 	QGraphicsScene::mouseMoveEvent(event);
1081 }
1082 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)1083 void ObjectsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1084 {
1085 	QGraphicsScene::mouseReleaseEvent(event);
1086 
1087 	if(event->button()==Qt::LeftButton && corner_move)
1088 		enableSceneMove(false);
1089 
1090 	//If there is selected object and the user ends the object moviment
1091 	if(!this->selectedItems().isEmpty() && moving_objs && event->button()==Qt::LeftButton/* && event->modifiers()==Qt::NoModifier */)
1092 	{
1093 		finishObjectsMove(event->scenePos());
1094 	}
1095 	else if(selection_rect->isVisible() && event->button()==Qt::LeftButton)
1096 	{
1097 		QPolygonF pol;
1098 		QPainterPath sel_area;
1099 
1100 		sel_area.addRect(selection_rect->polygon().boundingRect());
1101 
1102 		this->blockItemsSignals(true);
1103 		this->setSelectionArea(sel_area, Qt::IntersectsItemShape);
1104 		this->blockItemsSignals(false);
1105 
1106 		selection_rect->setVisible(false);
1107 		selection_rect->setPolygon(pol);
1108 		sel_ini_pnt.setX(DNaN);
1109 		sel_ini_pnt.setY(DNaN);
1110 
1111 		if(!this->selectedItems().isEmpty())
1112 			emit s_objectsSelectedInRange();
1113 	}
1114 }
1115 
finishObjectsMove(const QPointF & pnt_end)1116 void ObjectsScene::finishObjectsMove(const QPointF &pnt_end)
1117 {
1118 	QList<QGraphicsItem *> items=this->selectedItems(), rel_list;
1119 	double x1,y1,x2,y2, dx, dy;
1120 	QRectF rect;
1121 	SchemaView *sch_view=nullptr;
1122 	vector<QPointF> points;
1123 	vector<QPointF>::iterator itr;
1124 	vector<BaseObject *> rels, base_rels;
1125 	QSet<Schema *> schemas;
1126 	BaseRelationship *base_rel=nullptr;
1127 	RelationshipView *rel=nullptr;
1128 	BaseObjectView *obj_view=nullptr;
1129 	BaseTableView *tab_view=nullptr;
1130 	TableObjectView *tab_obj_view=nullptr;
1131 	QSet<BaseObjectView *> tables;
1132 
1133 	//Gathering the relationships inside the selected schemsa in order to move their points too
1134 	for(auto &item : items)
1135 	{
1136 		obj_view=dynamic_cast<BaseObjectView *>(item);
1137 		sch_view=dynamic_cast<SchemaView *>(item);
1138 		tab_view=dynamic_cast<BaseTableView *>(item);
1139 		tab_obj_view=dynamic_cast<TableObjectView *>(item);
1140 
1141 		// Ignoring table objects items
1142 		if(tab_obj_view)
1143 			continue;
1144 
1145 		if(obj_view)
1146 			obj_view->togglePlaceholder(false);
1147 
1148 		if(tab_view)
1149 			tables.insert(tab_view);
1150 		else if(sch_view)
1151 		{
1152 			//Get the schema object
1153 			Schema *schema=dynamic_cast<Schema *>(sch_view->getUnderlyingObject());
1154 
1155 			if(!schema->isProtected())
1156 			{
1157 				//Get the table-table and table-view relationships
1158 				rels=dynamic_cast<DatabaseModel *>(schema->getDatabase())->getObjects(ObjectType::Relationship, schema);
1159 				base_rels=dynamic_cast<DatabaseModel *>(schema->getDatabase())->getObjects(ObjectType::BaseRelationship, schema);
1160 				rels.insert(rels.end(), base_rels.begin(), base_rels.end());
1161 
1162 				for(auto &rel : rels)
1163 				{
1164 					base_rel=dynamic_cast<BaseRelationship *>(rel);
1165 
1166 					/* If the relationship contains points and it is not selected then it will be included on the list
1167 						 in order to move their custom line points */
1168 					if(!dynamic_cast<RelationshipView *>(base_rel->getOverlyingObject())->isSelected() &&
1169 							!base_rel->getPoints().empty())
1170 						rel_list.push_back(dynamic_cast<QGraphicsItem *>(base_rel->getOverlyingObject()));
1171 				}
1172 
1173 				#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
1174 					tables.unite(sch_view->getChildren().toSet());
1175 				#else
1176 					QList<BaseObjectView *> list = sch_view->getChildren();
1177 					tables.unite(QSet<BaseObjectView *>(list.begin(), list.end()));
1178 				#endif
1179 			}
1180 		}
1181 	}
1182 
1183 	items.append(rel_list);
1184 
1185 	/* Get the extreme points of the scene to check if some objects are out the area
1186 	forcing the scene to be resized */
1187 	x1=this->sceneRect().left();
1188 	y1=this->sceneRect().top();
1189 	x2=this->sceneRect().right();
1190 	y2=this->sceneRect().bottom();
1191 	dx=pnt_end.x() - sel_ini_pnt.x();
1192 	dy=pnt_end.y() - sel_ini_pnt.y();
1193 
1194 	for(auto &item : items)
1195 	{
1196 		// Ignoring table objects items
1197 		tab_obj_view=dynamic_cast<TableObjectView *>(item);
1198 		if(tab_obj_view) continue;
1199 
1200 		rel=dynamic_cast<RelationshipView *>(item);
1201 
1202 		if(!rel)
1203 		{
1204 			if(align_objs_grid)
1205 				item->setPos(alignPointToGrid(item->pos()));
1206 			else
1207 			{
1208 				QPointF p=item->pos();
1209 				if(p.x() < 0) p.setX(0);
1210 				if(p.y() < 0) p.setY(0);
1211 				item->setPos(p);
1212 			}
1213 
1214 			rect.setTopLeft(item->pos());
1215 			rect.setSize(item->boundingRect().size());
1216 		}
1217 		else
1218 		{
1219 			/* If the relationship has points added to the line is necessary to move the points
1220 				 too. Since relationships cannot be moved naturally (by user) this will be done
1221 				 by the scene. NOTE: this operation is done ONLY WHEN there is more than one object selected! */
1222 			points=rel->getUnderlyingObject()->getPoints();
1223 			if(items.size() > 1 && !points.empty())
1224 			{
1225 				itr=points.begin();
1226 				while(itr!=points.end())
1227 				{
1228 					//Translate the points
1229 					itr->setX(itr->x() + dx);
1230 					itr->setY(itr->y() + dy);
1231 
1232 					//Align to grid if the flag is set
1233 					if(align_objs_grid)
1234 						(*itr)=alignPointToGrid(*itr);
1235 
1236 					itr++;
1237 				}
1238 
1239 				//Assing the new points to relationship and reconfigure its line
1240 				rel->getUnderlyingObject()->setPoints(points);
1241 				rel->configureLine();
1242 			}
1243 
1244 			rect=rel->__boundingRect();
1245 		}
1246 
1247 		//Made the comparisson between the scene extremity and the object's bounding rect
1248 		if(rect.left() < x1) x1=rect.left();
1249 		if(rect.top() < y1) y1=rect.top();
1250 		if(rect.right() > x2) x2=rect.right();
1251 		if(rect.bottom() > y2) y2=rect.bottom();
1252 	}
1253 
1254 	//Reconfigures the rectangle with the most extreme points
1255 	rect.setCoords(x1, y1, x2, y2);
1256 
1257 	//If the new rect is greater than the scene bounding rect, this latter is resized
1258 	if(rect!=this->sceneRect())
1259 	{
1260 		rect=this->itemsBoundingRect();
1261 		rect.setTopLeft(QPointF(0,0));
1262 		rect.setWidth(rect.width() * 1.05);
1263 		rect.setHeight(rect.height() * 1.05);
1264 		this->setSceneRect(rect);
1265 	}
1266 
1267 	for(auto &obj : tables)
1268 	{
1269 		tab_view=dynamic_cast<BaseTableView *>(obj);
1270 
1271 		//Realign tables if the parent schema had the position adjusted too
1272 		if(align_objs_grid)
1273 		{
1274 			tab_view->setPos(alignPointToGrid(tab_view->pos()));
1275 			schemas.insert(dynamic_cast<Schema *>(tab_view->getUnderlyingObject()->getSchema()));
1276 		}
1277 
1278 		if(BaseObjectView::isPlaceholderEnabled())
1279 			tab_view->requestRelationshipsUpdate();
1280 	}
1281 
1282 	//Updating schemas bounding rects after moving objects
1283 	for(auto &obj : schemas)
1284 		obj->setModified(true);
1285 
1286 	emit s_objectsMoved(true);
1287 	moving_objs=false;
1288 	sel_ini_pnt.setX(DNaN);
1289 	sel_ini_pnt.setY(DNaN);
1290 }
1291 
alignObjectsToGrid()1292 void ObjectsScene::alignObjectsToGrid()
1293 {
1294 	QList<QGraphicsItem *> items=this->items();
1295 	RelationshipView *rel=nullptr;
1296 	BaseTableView *tab=nullptr;
1297 	TextboxView *lab=nullptr;
1298 	vector<QPointF> points;
1299 	vector<Schema *> schemas;
1300 	unsigned i, count, i1, count1;
1301 
1302 	count=items.size();
1303 	for(i=0; i < count; i++)
1304 	{
1305 		if(dynamic_cast<QGraphicsItemGroup *>(items[i]) && !items[i]->parentItem())
1306 		{
1307 			tab=dynamic_cast<BaseTableView *>(items[i]);
1308 			rel=dynamic_cast<RelationshipView *>(items[i]);
1309 
1310 			if(tab)
1311 				tab->setPos(this->alignPointToGrid(tab->pos()));
1312 			else if(rel)
1313 			{
1314 				//Align the relationship points
1315 				points=rel->getUnderlyingObject()->getPoints();
1316 				count1=points.size();
1317 				for(i1=0; i1 < count1; i1++)
1318 					points[i1]=this->alignPointToGrid(points[i1]);
1319 
1320 				if(count1 > 0)
1321 				{
1322 					rel->getUnderlyingObject()->setPoints(points);
1323 					rel->configureLine();
1324 				}
1325 
1326 				//Align the labels
1327 				for(i1=BaseRelationship::SrcCardLabel;
1328 					i1<=BaseRelationship::RelNameLabel; i1++)
1329 				{
1330 					lab=rel->getLabel(i1);
1331 					if(lab)
1332 						lab->setPos(this->alignPointToGrid(lab->pos()));
1333 				}
1334 			}
1335 			else if(!dynamic_cast<SchemaView *>(items[i]))
1336 				items[i]->setPos(this->alignPointToGrid(items[i]->pos()));
1337 			else
1338 				schemas.push_back(dynamic_cast<Schema *>(dynamic_cast<BaseObjectView *>(items[i])->getUnderlyingObject()));
1339 		}
1340 	}
1341 
1342 	//Updating schemas dimensions
1343 	while(!schemas.empty())
1344 	{
1345 		schemas.back()->setModified(true);
1346 		schemas.pop_back();
1347 	}
1348 }
1349 
update()1350 void ObjectsScene::update()
1351 {
1352 	this->setBackgroundBrush(grid);
1353 	QGraphicsScene::update(this->sceneRect());
1354 }
1355 
clearTablesChildrenSelection()1356 void ObjectsScene::clearTablesChildrenSelection()
1357 {
1358 	for(auto &tab_obj_view : tabs_sel_children)
1359 		tab_obj_view->clearChildrenSelection();
1360 
1361 	tabs_sel_children.clear();
1362 }
1363 
clearSelection()1364 void ObjectsScene::clearSelection()
1365 {
1366 	clearTablesChildrenSelection();
1367 	QGraphicsScene::clearSelection();
1368 }
1369 
getPagesForPrinting(const QSizeF & paper_size,const QSizeF & margin,unsigned & h_page_cnt,unsigned & v_page_cnt)1370 vector<QRectF> ObjectsScene::getPagesForPrinting(const QSizeF &paper_size, const QSizeF &margin, unsigned &h_page_cnt, unsigned &v_page_cnt)
1371 {
1372 	vector<QRectF> pages;
1373 	QRectF page_rect, max_rect;
1374 	double width, height, page_width, page_height;
1375 	unsigned h_page=0, v_page=0, start_h=99999, start_v=99999;
1376 	QList<QGraphicsItem *> list;
1377 
1378 	page_width=ceil(paper_size.width() - margin.width()-1);
1379 	page_height=ceil(paper_size.height() - margin.height()-1);
1380 
1381 	//Calculates the horizontal and vertical page count based upon the passed paper size
1382 	h_page_cnt=round(this->sceneRect().width()/page_width) + 1;
1383 	v_page_cnt=round(this->sceneRect().height()/page_height) + 1;
1384 
1385 	//Calculates the maximum count of horizontal and vertical pages
1386 	for(v_page=0; v_page < v_page_cnt; v_page++)
1387 	{
1388 		for(h_page=0; h_page < h_page_cnt; h_page++)
1389 		{
1390 			//Calculates the current page rectangle
1391 			page_rect=QRectF(QPointF(h_page * page_width, v_page * page_height), QSizeF(page_width, page_height));
1392 
1393 			//Case there is selected items recalculates the maximum page size
1394 			list=this->items(page_rect, Qt::IntersectsItemShape);
1395 			if(!list.isEmpty())
1396 			{
1397 				if(start_h > h_page) start_h=h_page;
1398 				if(start_v > v_page) start_v=v_page;
1399 
1400 				width=page_rect.left() + page_rect.width();
1401 				height=page_rect.top() + page_rect.height();
1402 
1403 				if(width > max_rect.width())
1404 					max_rect.setWidth(width);
1405 
1406 				if(height > max_rect.height())
1407 					max_rect.setHeight(height);
1408 			}
1409 		}
1410 	}
1411 
1412 	//Re calculates the maximum page count based upon the maximum page size
1413 	h_page_cnt=round(max_rect.width()/page_width);
1414 	v_page_cnt=round(max_rect.height()/page_height);
1415 
1416 	//Inserts the page rectangles on the list
1417 	for(v_page=static_cast<unsigned>(start_v); v_page < v_page_cnt; v_page++)
1418 		for(h_page=static_cast<unsigned>(start_h); h_page < h_page_cnt; h_page++)
1419 			pages.push_back(QRectF(QPointF(h_page * page_width, v_page * page_height), QSizeF(page_width, page_height)));
1420 
1421 	return pages;
1422 }
1423 
isRangeSelectionEnabled()1424 bool ObjectsScene::isRangeSelectionEnabled()
1425 {
1426 	return enable_range_sel;
1427 }
1428 
isRangeSelectionTriggerInverted()1429 bool ObjectsScene::isRangeSelectionTriggerInverted()
1430 {
1431 	return invert_rangesel_trigger;
1432 }
1433 
isRelationshipLineVisible()1434 bool ObjectsScene::isRelationshipLineVisible()
1435 {
1436 	return rel_line->isVisible();
1437 }
1438 
isMovingObjects()1439 bool ObjectsScene::isMovingObjects()
1440 {
1441 	return moving_objs;
1442 }
1443 
selectedItems() const1444 QList<QGraphicsItem *> ObjectsScene::selectedItems() const
1445 {
1446 	if(tabs_sel_children.empty())
1447 		return QGraphicsScene::selectedItems();
1448 
1449 	QList<QGraphicsItem *> items = QGraphicsScene::selectedItems();
1450 
1451 	for(auto &tab_view :tabs_sel_children)
1452 	{
1453 		for(auto &tab_obj : tab_view->getSelectedChidren())
1454 			items.append(tab_obj);
1455 	}
1456 
1457 	return items;
1458 }
1459 
hasOnlyTableChildrenSelection() const1460 bool ObjectsScene::hasOnlyTableChildrenSelection() const
1461 {
1462 	return QGraphicsScene::selectedItems().isEmpty() && !tabs_sel_children.isEmpty();
1463 }
1464