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 "baseobjectview.h"
20 #include "textboxview.h"
21 #include "roundedrectitem.h"
22 #include "objectsscene.h"
23 
24 map<QString, QTextCharFormat> BaseObjectView::font_config;
25 map<QString, vector<QColor>> BaseObjectView::color_config;
26 unsigned BaseObjectView::global_sel_order=1;
27 bool BaseObjectView::use_placeholder=true;
28 bool BaseObjectView::compact_view=false;
29 
BaseObjectView(BaseObject * object)30 BaseObjectView::BaseObjectView(BaseObject *object)
31 {
32 	sel_order=0;
33 	protected_icon=nullptr;
34 	obj_shadow=nullptr;
35 	obj_selection=nullptr;
36 	pos_info_item=nullptr;
37 	sql_disabled_item=nullptr;
38 	placeholder=nullptr;
39 	setSourceObject(object);
40 }
41 
~BaseObjectView()42 BaseObjectView::~BaseObjectView()
43 {
44 	setSourceObject(nullptr);
45 }
46 
mousePressEvent(QGraphicsSceneMouseEvent * event)47 void BaseObjectView::mousePressEvent(QGraphicsSceneMouseEvent *event)
48 {
49 	if(event->button()==Qt::RightButton && !this->isSelected())
50 	{
51 		//Faking an left-click in order to force the object selection using the right button.
52 		QGraphicsSceneMouseEvent *m_event=new QGraphicsSceneMouseEvent;
53 		m_event->setPos(event->pos());
54 		m_event->setScenePos(event->scenePos());
55 		m_event->setScreenPos(event->screenPos());
56 		m_event->setButton(Qt::LeftButton);
57 
58 		QGraphicsItemGroup::mousePressEvent(m_event);
59 		event->ignore();
60 	}
61 	else if(event->button()==Qt::LeftButton)
62 		QGraphicsItemGroup::mousePressEvent(event);
63 }
64 
setSourceObject(BaseObject * object)65 void BaseObjectView::setSourceObject(BaseObject *object)
66 {
67 	BaseGraphicObject *graph_obj=dynamic_cast<BaseGraphicObject *>(object);
68 
69 	//Stores the reference to the source object as the data of graphical object
70 	this->setData(0, QVariant::fromValue<void *>(object));
71 
72 	if(!graph_obj)
73 	{
74 		if(obj_shadow)
75 		{
76 			this->removeFromGroup(obj_shadow);
77 			delete obj_shadow;
78 			obj_shadow=nullptr;
79 		}
80 
81 		if(protected_icon)
82 		{
83 			this->removeFromGroup(protected_icon);
84 			delete protected_icon;
85 			protected_icon=nullptr;
86 		}
87 
88 		if(pos_info_item)
89 		{
90 			this->removeFromGroup(pos_info_item);
91 			delete pos_info_item;
92 			pos_info_item=nullptr;
93 		}
94 
95 		if(sql_disabled_item)
96 		{
97 			this->removeFromGroup(sql_disabled_item);
98 			delete sql_disabled_item;
99 			sql_disabled_item=nullptr;
100 		}
101 
102 		if(placeholder)
103 		{
104 			delete placeholder;
105 			placeholder=nullptr;
106 		}
107 	}
108 	else
109 	{
110 		QGraphicsPolygonItem *pol_item=nullptr;
111 
112 		graph_obj->disconnect();
113 		graph_obj->setReceiverObject(this);
114 		connect(graph_obj, SIGNAL(s_objectProtected(bool)), this, SLOT(toggleProtectionIcon(bool)));
115 
116 		//By default the item can be selected and send geometry changes to the scene
117 		this->setFlags(QGraphicsItem::ItemIsSelectable |
118 					   QGraphicsItem::ItemSendsGeometryChanges);
119 		//The object is only movable if is not protected
120 		this->setFlag(QGraphicsItem::ItemIsMovable, !graph_obj->isProtected());
121 
122 		if(!protected_icon)
123 		{
124 			protected_icon=new QGraphicsItemGroup;
125 			protected_icon->setVisible(graph_obj->isProtected());
126 			protected_icon->setZValue(3);
127 
128 			pol_item=new QGraphicsPolygonItem;
129 			protected_icon->addToGroup(pol_item);
130 
131 			pol_item=new QGraphicsPolygonItem;
132 			protected_icon->addToGroup(pol_item);
133 
134 			this->addToGroup(protected_icon);
135 		}
136 
137 		if(!pos_info_item)
138 		{
139 			pos_info_item=new TextPolygonItem;
140 			pos_info_item->setZValue(10);
141 			this->addToGroup(pos_info_item);
142 		}
143 
144 		if(!sql_disabled_item && object->getObjectType()!=ObjectType::Textbox)
145 		{
146 			sql_disabled_item=new TextPolygonItem;
147 			sql_disabled_item->setZValue(100);
148 			this->addToGroup(sql_disabled_item);
149 		}
150 	}
151 }
152 
getUnderlyingObject()153 BaseObject *BaseObjectView::getUnderlyingObject()
154 {
155 	return reinterpret_cast<BaseObject *>(this->data(0).value<void *>());
156 }
157 
loadObjectsStyle()158 void BaseObjectView::loadObjectsStyle()
159 {
160 	QTextCharFormat font_fmt;
161 	QFont font;
162 	attribs_map attribs;
163 	map<QString, QTextCharFormat>::iterator itr;
164 	QStringList list;
165 	QString elem,
166 			config_file=GlobalAttributes::getConfigurationFilePath(GlobalAttributes::ObjectsStyleConf);
167 	XmlParser xmlparser;
168 
169 	try
170 	{
171 		xmlparser.restartParser();
172 
173 		xmlparser.setDTDFile(GlobalAttributes::getTmplConfigurationFilePath(GlobalAttributes::ObjectDTDDir,
174 																																				GlobalAttributes::ObjectsStyleConf +
175 																																				GlobalAttributes::ObjectDTDExt),
176 												 GlobalAttributes::ObjectsStyleConf);
177 
178 		xmlparser.loadXMLFile(config_file);
179 
180 		if(xmlparser.accessElement(XmlParser::ChildElement))
181 		{
182 			do
183 			{
184 				if(xmlparser.getElementType()==XML_ELEMENT_NODE)
185 				{
186 					xmlparser.getElementAttributes(attribs);
187 					elem=xmlparser.getElementName();
188 
189 					if(elem==Attributes::Global)
190 					{
191 						font.setFamily(attribs[Attributes::Font]);
192 						font.setPointSizeF(attribs[Attributes::Size].toDouble());
193 						font.setBold(attribs[Attributes::Bold]==Attributes::True);
194 						font.setItalic(attribs[Attributes::Italic]==Attributes::True);
195 						font.setUnderline(attribs[Attributes::Underline]==Attributes::True);
196 						font_fmt.setFont(font);
197 						font_config[Attributes::Global]=font_fmt;
198 					}
199 					else if(elem==Attributes::Font)
200 					{
201 						font_config[attribs[Attributes::Id]]=font_fmt;
202 						itr=font_config.find(attribs[Attributes::Id]);
203 						font=font_fmt.font();
204 						font.setBold(attribs[Attributes::Bold]==Attributes::True);
205 						font.setItalic(attribs[Attributes::Italic]==Attributes::True);
206 						font.setUnderline(attribs[Attributes::Underline]==Attributes::True);
207 						(itr->second).setFont(font);
208 						(itr->second).setForeground(QColor(attribs[Attributes::Color]));
209 					}
210 					else if(elem==Attributes::Object)
211 					{
212 						list=attribs[Attributes::FillColor].split(',');
213 
214 						vector<QColor> colors;
215 						colors.push_back(!list.isEmpty() ? QColor(list[0]) : QColor(0,0,0));
216 						colors.push_back(list.size()==2 ? QColor(list[1]) : colors[0]);
217 						colors.push_back(QColor(attribs[Attributes::BorderColor]));
218 
219 						color_config[attribs[Attributes::Id]]=colors;
220 					}
221 				}
222 			}
223 			while(xmlparser.accessElement(XmlParser::NextElement));
224 		}
225 	}
226 	catch(Exception &e)
227 	{
228 		throw Exception(e.getErrorMessage(), e.getErrorCode(), __PRETTY_FUNCTION__, __FILE__, __LINE__, &e, config_file);
229 	}
230 }
231 
setFontStyle(const QString & id,QTextCharFormat font_fmt)232 void BaseObjectView::setFontStyle(const QString &id, QTextCharFormat font_fmt)
233 {
234 	QFont font;
235 
236 	if(id!=Attributes::Global)
237 	{
238 		font=font_config[Attributes::Global].font();
239 		font.setItalic(font_fmt.font().italic());
240 		font.setBold(font_fmt.font().bold());
241 		font.setUnderline(font_fmt.font().underline());
242 		font_fmt.setFont(font);
243 	}
244 	else
245 	{
246 		map<QString, QTextCharFormat>::iterator itr, itr_end;
247 
248 		itr=font_config.begin();
249 		itr_end=font_config.end();
250 		font=font_fmt.font();
251 
252 		while(itr!=itr_end)
253 		{
254 			font.setItalic((itr->second).font().italic());
255 			font.setBold((itr->second).font().bold());
256 			font.setUnderline((itr->second).font().underline());
257 			(itr->second).setFont(font);
258 			itr++;
259 		}
260 	}
261 
262 	if(font_config.count(id))
263 		font_config[id]=font_fmt;
264 }
265 
setElementColor(const QString & id,QColor color,unsigned color_id)266 void BaseObjectView::setElementColor(const QString &id, QColor color, unsigned color_id)
267 {
268 	if(color_id < 3 && color_config.count(id))
269 		color_config[id][color_id]=color;
270 }
271 
getElementColor(const QString & id,unsigned color_id)272 QColor BaseObjectView::getElementColor(const QString &id, unsigned color_id)
273 {
274 	if(color_config.count(id) > 0 && color_id < 3)
275 		return color_config[id][color_id];
276 	else
277 		return QColor(0,0,0);
278 }
279 
getFillStyle(const QString & id,QColor & color1,QColor & color2)280 void BaseObjectView::getFillStyle(const QString &id, QColor &color1, QColor &color2)
281 {
282 	if(color_config.count(id) > 0)
283 	{
284 		color1=color_config[id][0];
285 		color2=color_config[id][1];
286 	}
287 }
288 
getFillStyle(const QString & id)289 QLinearGradient BaseObjectView::getFillStyle(const QString &id)
290 {
291 	vector<QColor> colors;
292 	QLinearGradient grad(QPointF(0,0),QPointF(0,1));
293 
294 	if(color_config.count(id) > 0)
295 	{
296 		colors=color_config[id];
297 
298 		if(!colors.empty())
299 		{
300 			if(id==Attributes::ObjSelection || id==Attributes::Placeholder)
301 			{
302 				colors[0].setAlpha(ObjectAlphaChannel);
303 				colors[1].setAlpha(ObjectAlphaChannel);
304 			}
305 
306 			grad.setCoordinateMode(QGradient::ObjectBoundingMode);
307 			grad.setColorAt(0, colors[0]);
308 			grad.setColorAt(1, colors[1]);
309 		}
310 	}
311 
312 	return grad;
313 }
314 
getBorderStyle(const QString & id)315 QPen BaseObjectView::getBorderStyle(const QString &id)
316 {
317 	QPen pen;
318 	vector<QColor> colors;
319 
320 	if(color_config.count(id) > 0)
321 	{
322 		colors=color_config[id];
323 
324 		if(!colors.empty())
325 		{
326 			if(id==Attributes::ObjSelection)
327 				colors[2].setAlpha(ObjectAlphaChannel);
328 
329 			pen.setWidthF(ObjectBorderWidth);
330 			pen.setColor(colors[2]);
331 		}
332 	}
333 
334 	return pen;
335 }
336 
getFontStyle(const QString & id)337 QTextCharFormat BaseObjectView::getFontStyle(const QString &id)
338 {
339 	if(font_config.count(id))
340 		return font_config[id];
341 	else
342 		return QTextCharFormat();
343 }
344 
setPlaceholderEnabled(bool value)345 void BaseObjectView::setPlaceholderEnabled(bool value)
346 {
347 	use_placeholder=value;
348 }
349 
isPlaceholderEnabled()350 bool BaseObjectView::isPlaceholderEnabled()
351 {
352 	return use_placeholder;
353 }
354 
setCompactViewEnabled(bool value)355 void BaseObjectView::setCompactViewEnabled(bool value)
356 {
357 	compact_view = value;
358 }
359 
isCompactViewEnabled()360 bool BaseObjectView::isCompactViewEnabled()
361 {
362 	return compact_view;
363 }
364 
itemChange(GraphicsItemChange change,const QVariant & value)365 QVariant BaseObjectView::itemChange(GraphicsItemChange change, const QVariant &value)
366 {
367 	if(change==ItemPositionHasChanged)
368 	{
369 		BaseGraphicObject *graph_obj=dynamic_cast<BaseGraphicObject *>(this->getUnderlyingObject());
370 
371 		if(graph_obj && !graph_obj->isProtected())
372 		{
373 			if(ObjectsScene::isAlignObjectsToGrid())
374 				this->setPos(ObjectsScene::alignPointToGrid(this->scenePos()));
375 
376 			graph_obj->setPosition(this->scenePos());
377 			this->configurePositionInfo(this->pos());
378 		}
379 	}
380 	else if(change == ItemSelectedHasChanged && obj_selection)
381 	{
382 		this->setSelectionOrder(value.toBool());
383 		pos_info_item->setVisible(value.toBool());
384 		obj_selection->setVisible(value.toBool());
385 
386 		this->configurePositionInfo(this->pos());
387 		emit s_objectSelected(dynamic_cast<BaseGraphicObject *>(this->getUnderlyingObject()), value.toBool());
388 	}
389 
390 	return value;
391 }
392 
setSelectionOrder(bool selected)393 void BaseObjectView::setSelectionOrder(bool selected)
394 {
395 	if(this->sel_order==0 && selected)
396 		this->sel_order=++BaseObjectView::global_sel_order;
397 	else if(!selected)
398 		this->sel_order=0;
399 }
400 
boundingRect() const401 QRectF BaseObjectView::boundingRect() const
402 {
403 	return bounding_rect;
404 }
405 
toggleProtectionIcon(bool value)406 void BaseObjectView::toggleProtectionIcon(bool value)
407 {
408 	BaseGraphicObject *obj_graf=dynamic_cast<BaseGraphicObject *>(this->getUnderlyingObject());
409 
410 	protected_icon->setVisible(value);
411 	this->setFlag(QGraphicsItem::ItemIsMovable, !value);
412 
413 	if(obj_graf)
414 		obj_graf->setModified(true);
415 }
416 
configureObjectSelection()417 void BaseObjectView::configureObjectSelection()
418 {
419 	RoundedRectItem *rect_item=dynamic_cast<RoundedRectItem *>(obj_selection);
420 
421 	if(rect_item)
422 	{
423 		rect_item->setRect(this->boundingRect());
424 		rect_item->setPos(0, 0);
425 		rect_item->setBorderRadius(5);
426 		rect_item->setBrush(this->getFillStyle(Attributes::ObjSelection));
427 		rect_item->setPen(this->getBorderStyle(Attributes::ObjSelection));
428 	}
429 }
430 
configurePositionInfo(QPointF pos)431 void BaseObjectView::configurePositionInfo(QPointF pos)
432 {
433 	if(this->isSelected())
434 	{
435 		QFont fnt=font_config[Attributes::PositionInfo].font();
436 
437 		pos_info_item->setBrush(BaseObjectView::getFillStyle(Attributes::PositionInfo));
438 		pos_info_item->setPen(BaseObjectView::getBorderStyle(Attributes::PositionInfo));
439 
440 		fnt.setPointSizeF(fnt.pointSizeF() * 0.95);
441 		pos_info_item->setFont(fnt);
442 		pos_info_item->setTextBrush(font_config[Attributes::PositionInfo].foreground());
443 
444 		pos_info_item->setText(QString(" x:%1 y:%2 ").arg(round(pos.x())).arg(round(pos.y())));
445 		pos_info_item->setPolygon(QPolygonF(pos_info_item->getTextBoundingRect()));
446 
447 		pos_info_item->setPos(-0.5, -pos_info_item->boundingRect().height()/2);
448 	}
449 }
450 
configureSQLDisabledInfo()451 void BaseObjectView::configureSQLDisabledInfo()
452 {
453 	if(sql_disabled_item)
454 	{
455 		double px=0, py=0;
456 
457 		sql_disabled_item->setVisible(this->getUnderlyingObject()->isSQLDisabled());
458 
459 		if(this->getUnderlyingObject()->isSQLDisabled())
460 		{
461 			QTextCharFormat char_fmt;
462 			char_fmt=BaseObjectView::getFontStyle(Attributes::PositionInfo);
463 			char_fmt.setFontPointSize(char_fmt.font().pointSizeF() * 0.80);
464 
465 			sql_disabled_item->setFont(char_fmt.font());
466 			sql_disabled_item->setText(tr("SQL off"));
467 			sql_disabled_item->setTextBrush(char_fmt.foreground());
468 
469 			sql_disabled_item->setPolygon(QRectF(QPointF(0,0), sql_disabled_item->getTextBoundingRect().size() + QSizeF(1.5 * HorizSpacing, 1.5 * VertSpacing)));
470 			sql_disabled_item->setPen(BaseObjectView::getBorderStyle(Attributes::PositionInfo));
471 			sql_disabled_item->setBrush(BaseObjectView::getFillStyle(Attributes::PositionInfo));
472 
473 			px=bounding_rect.width() - sql_disabled_item->boundingRect().width() + (1.5 * HorizSpacing);
474 			py=-(sql_disabled_item->boundingRect().height()/2);
475 
476 			sql_disabled_item->setPos(px, py);
477 			sql_disabled_item->setTextPos(HorizSpacing * 0.75, VertSpacing * 0.75);
478 		}
479 	}
480 }
481 
configureProtectedIcon()482 void BaseObjectView::configureProtectedIcon()
483 {
484 	if(protected_icon)
485 	{
486 		QGraphicsPolygonItem *pol_item=nullptr;
487 		QPolygonF pol;
488 		double factor;
489 
490 		//Calculates the factor used to resize the protection icon accordding the font size
491 		factor=font_config[Attributes::Global].font().pointSizeF()/DefaultFontSize;
492 
493 		pol.append(QPointF(2,5)); pol.append(QPointF(2,2));
494 		pol.append(QPointF(3,1)); pol.append(QPointF(4,0));
495 		pol.append(QPointF(7,0)); pol.append(QPointF(8,1));
496 		pol.append(QPointF(9,2)); pol.append(QPointF(9,5));
497 		pol.append(QPointF(7,5)); pol.append(QPointF(7,3));
498 		pol.append(QPointF(6,2)); pol.append(QPointF(5,2));
499 		pol.append(QPointF(4,3)); pol.append(QPointF(4,5));
500 
501 		if(factor!=1.0)
502 			TextPolygonItem::resizePolygon(pol, pol.boundingRect().width() * factor,
503 																					pol.boundingRect().height() * factor);
504 
505 		pol_item=dynamic_cast<QGraphicsPolygonItem *>(protected_icon->childItems().at(0));
506 		pol_item->setPolygon(pol);
507 		pol_item->setBrush(this->getFillStyle(Attributes::LockerArc));
508 		pol_item->setPen(this->getBorderStyle(Attributes::LockerArc));
509 
510 		pol.clear();
511 		pol.append(QPointF(1,5));  pol.append(QPointF(10,5));
512 		pol.append(QPointF(11,6)); pol.append(QPointF(11,9));
513 		pol.append(QPointF(10,10)); pol.append(QPointF(1,10));
514 		pol.append(QPointF(0,9)); pol.append(QPointF(0,6));
515 
516 		if(factor!=1.0)
517 			TextPolygonItem::resizePolygon(pol, pol.boundingRect().width() * factor,
518 																					pol.boundingRect().height() * factor);
519 
520 		pol_item=dynamic_cast<QGraphicsPolygonItem *>(protected_icon->childItems().at(1));
521 		pol_item->setPolygon(pol);
522 		pol_item->setBrush(this->getFillStyle(Attributes::LockerBody));
523 		pol_item->setPen(this->getBorderStyle(Attributes::LockerBody));
524 	}
525 }
526 
configurePlaceholder()527 void BaseObjectView::configurePlaceholder()
528 {
529 	if(!placeholder)
530 	{
531 		placeholder=new RoundedRectItem();
532 		placeholder->setVisible(false);
533 		placeholder->setZValue(-1);
534 		placeholder->setFlag(QGraphicsItem::ItemIsMovable, false);
535 		placeholder->setFlag(QGraphicsItem::ItemIsSelectable, false);
536 	}
537 }
538 
__configureObject()539 void BaseObjectView::__configureObject()
540 {
541 	BaseGraphicObject *graph_obj=dynamic_cast<BaseGraphicObject *>(this->getUnderlyingObject());
542 
543 	if(graph_obj)
544 	{
545 		this->setPos(graph_obj->getPosition());
546 		this->setToolTip(graph_obj->getName(true) +
547 						 QString(" (") + graph_obj->getTypeName() +
548 						 QString(") ") + QString("\nId: %1").arg(graph_obj->getObjectId()));
549 		this->configurePositionInfo(graph_obj->getPosition());
550 		this->configureProtectedIcon();
551 	}
552 }
553 
getSelectionOrder()554 unsigned BaseObjectView::getSelectionOrder()
555 {
556 	return sel_order;
557 }
558 
getCenter()559 QPointF BaseObjectView::getCenter()
560 {
561 	return QPointF(this->pos().x() + this->boundingRect().width()/2.0,
562 								 this->pos().y() + this->boundingRect().height()/2.0);
563 }
564 
togglePlaceholder(bool visible)565 void BaseObjectView::togglePlaceholder(bool visible)
566 {
567 	if(use_placeholder && placeholder && this->scene())
568 	{
569 		if(!placeholder->scene())
570 			this->scene()->addItem(placeholder);
571 
572 		if(visible)
573 		{
574 			QPen pen=BaseObjectView::getBorderStyle(Attributes::Placeholder);
575 			pen.setStyle(Qt::DashLine);
576 
577 			placeholder->setZValue(this->zValue() - 1);
578 			placeholder->setBrush(BaseObjectView::getFillStyle(Attributes::Placeholder));
579 			placeholder->setPen(pen);
580 			placeholder->setRect(QRectF(QPointF(0,0),this->bounding_rect.size()));
581 			placeholder->setPos(this->mapToScene(this->bounding_rect.topLeft()));
582 		}
583 
584 		placeholder->setVisible(visible);
585 	}
586 }
587 
getFontFactor()588 double BaseObjectView::getFontFactor()
589 {
590 	return font_config[Attributes::Global].font().pointSizeF()/DefaultFontSize;
591 }
592 
setLayer(unsigned layer)593 void BaseObjectView::setLayer(unsigned layer)
594 {
595 	BaseGraphicObject *graph_obj = dynamic_cast<BaseGraphicObject *>(this->getUnderlyingObject());
596 
597 	if(graph_obj)
598 		graph_obj->setLayer(layer);
599 }
600 
getLayer()601 unsigned BaseObjectView::getLayer()
602 {
603 	BaseGraphicObject *graph_obj = dynamic_cast<BaseGraphicObject *>(this->getUnderlyingObject());
604 
605 	if(graph_obj)
606 		return graph_obj->getLayer();
607 
608 	return 0;
609 }
610 
getScreenDpiFactor()611 double BaseObjectView::getScreenDpiFactor()
612 {
613 	QScreen *screen = qApp->screens().at(qApp->desktop()->screenNumber(qApp->activeWindow()));
614 	double factor = screen->logicalDotsPerInch() / 96.0;
615 	double pixel_ratio = screen->devicePixelRatio();
616 
617 	if(factor < 1)
618 		return 1;
619 
620 	return factor * pixel_ratio;
621 }
622