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