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 "graphicalview.h"
20 
GraphicalView(View * view)21 GraphicalView::GraphicalView(View *view) : BaseTableView(view)
22 {
23 	connect(view, SIGNAL(s_objectModified()), this, SLOT(configureObject()));
24 
25 	columns=new QGraphicsItemGroup;
26 	columns->setZValue(1);
27 	this->addToGroup(columns);
28 	configurePlaceholder();
29 	this->configureObject();
30 }
31 
configureObject()32 void GraphicalView::configureObject()
33 {
34 	/* If the table isn't visible we abort the current configuration
35 	 * and mark its geometry update as pending so in the next call to
36 	 * setVisible(true) the geometry can be updated (see BaseObjectView::itemChange()) */
37 	if(!this->isVisible())
38 	{
39 		pending_geom_update = true;
40 		return;
41 	}
42 
43 	View *view=dynamic_cast<View *>(this->getUnderlyingObject());
44 	int i = 0, count = 0;
45 	unsigned start_col = 0, end_col = 0, start_ext = 0, end_ext = 0;
46 	QPen pen;
47 	TableObjectView *graph_ref=nullptr;
48 	QList<QGraphicsItem *> subitems;
49 	vector<TableObject *> tab_objs, ext_tab_objs;
50 	vector<SimpleColumn> view_cols;
51 	QGraphicsItemGroup *groups[]={ columns, ext_attribs };
52 	RoundedRectItem *bodies[]={ body, ext_attribs_body };
53 	QString attribs[]={ Attributes::ViewBody, Attributes::ViewExtBody },
54 			tag_attribs[]={ Attributes::TableBody, Attributes::TableExtBody };
55 	double width, type_width=0, px=0;
56 	TableObjectView *col_item=nullptr;
57 	QList<TableObjectView *> col_items;
58 	TableObject *tab_obj=nullptr;
59 	Tag *tag=view->getTag();
60 	CollapseMode collapse_mode = view->getCollapseMode();
61 	bool has_col_pag = false, has_ext_pag = false;
62 
63 	// Clear the selected children objects vector since we'll (re)configure the whole view
64 	sel_child_objs.clear();
65 
66 	//Configures the view's title
67 	title->configureObject(view);
68 
69 	for(auto &obj : view->getObjects())
70 		ext_tab_objs.push_back(dynamic_cast<TableObject *>(obj));
71 
72 	attribs_toggler->setHasExtAttributes(!hide_ext_attribs && !ext_tab_objs.empty());
73 
74 	view_cols = view->getColumns();
75 	has_col_pag = configurePaginationParams(BaseTable::AttribsSection, view_cols.size(), start_col, end_col);
76 	has_ext_pag = configurePaginationParams(BaseTable::ExtAttribsSection,
77 																						collapse_mode != CollapseMode::ExtAttribsCollapsed ? ext_tab_objs.size() : 0,
78 																						start_ext, end_ext);
79 
80 	//Moves the references group to the origin to be moved latter
81 	columns->moveBy(-columns->scenePos().x(),	-columns->scenePos().y());
82 	columns->setVisible(view->getCollapseMode() != CollapseMode::AllAttribsCollapsed && start_col < static_cast<unsigned>(view_cols.size()));
83 	body->setVisible(columns->isVisible());
84 
85 	if(!columns->isVisible())
86 	{
87 		for(auto &item : columns->childItems())
88 		{
89 			columns->removeFromGroup(item);
90 			delete item;
91 		}
92 	}
93 	else
94 	{
95 		vector<SimpleColumn> aux_view_cols;
96 
97 		if(has_col_pag)
98 			aux_view_cols.assign(view_cols.begin() + start_col, view_cols.begin() + end_col);
99 		else
100 			aux_view_cols = view_cols;
101 
102 		count = aux_view_cols.size();
103 		subitems=columns->childItems();
104 
105 		for(i=0; i < count; i++)
106 		{
107 			//Reuses the subitem if it was allocated before
108 			if(!subitems.isEmpty() && i < subitems.size())
109 			{
110 				graph_ref=dynamic_cast<TableObjectView *>(subitems[i]);
111 
112 				//Moves the reference to the origin to be moved latter
113 				graph_ref->moveBy(-graph_ref->scenePos().x(),
114 													-graph_ref->scenePos().y());
115 			}
116 			else
117 				graph_ref=new TableObjectView;
118 
119 			graph_ref->configureObject(aux_view_cols[i]);
120 			graph_ref->moveBy(HorizSpacing, (i * graph_ref->boundingRect().height()) + VertSpacing);
121 
122 			/* Calculates the width of the name + type of the object. This is used to align all
123 			the constraint labels on table */
124 			width=graph_ref->getChildObject(TableObjectView::ObjDescriptor)->boundingRect().width() +
125 						graph_ref->getChildObject(TableObjectView::NameLabel)->boundingRect().width() + (8 * HorizSpacing);
126 			if(px < width)  px=width;
127 
128 			//Gets the maximum width of the column type label to align all at same horizontal position
129 			if(type_width < graph_ref->getChildObject(TableObjectView::TypeLabel)->boundingRect().width())
130 				type_width=graph_ref->getChildObject(TableObjectView::TypeLabel)->boundingRect().width() + (3 * HorizSpacing);
131 
132 			col_items.push_back(graph_ref);
133 		}
134 
135 		//Destroy the graphical references not used
136 		i=subitems.size()-1;
137 		while(i > count-1)
138 		{
139 			graph_ref=dynamic_cast<TableObjectView *>(subitems[i]);
140 			columns->removeFromGroup(graph_ref);
141 			delete graph_ref;
142 			i--;
143 		}
144 
145 		//Set all items position
146 		while(!col_items.isEmpty())
147 		{
148 			col_item=dynamic_cast<TableObjectView *>(col_items.front());
149 			columns->removeFromGroup(col_item);
150 			col_items.pop_front();
151 
152 			//Positioning the type label
153 			col_item->setChildObjectXPos(TableObjectView::TypeLabel, px);
154 
155 			//Positioning the constraints label
156 			col_item->setChildObjectXPos(TableObjectView::ConstrAliasLabel, px + type_width);
157 			columns->addToGroup(col_item);
158 		}
159 	}
160 
161 	if(!hide_ext_attribs && view->getCollapseMode() == CollapseMode::NotCollapsed)
162 	{
163 		if(view->isPaginationEnabled() && has_ext_pag)
164 			tab_objs.assign(ext_tab_objs.begin() + start_ext, ext_tab_objs.begin() + end_ext);
165 		else
166 			tab_objs.assign(ext_tab_objs.begin(), ext_tab_objs.end());
167 	}
168 
169 	ext_attribs->setVisible(!tab_objs.empty() && view->getCollapseMode() == CollapseMode::NotCollapsed);
170 	ext_attribs_body->setVisible(ext_attribs->isVisible());
171 
172 	if(tab_objs.empty())
173 	{
174 		for(auto &item : ext_attribs->childItems())
175 		{
176 			ext_attribs->removeFromGroup(item);
177 			delete item;
178 		}
179 	}
180 	else
181 	{
182 		count=tab_objs.size();
183 
184 		//Gets the subitems of the current group
185 		subitems=ext_attribs->childItems();
186 		ext_attribs->moveBy(-ext_attribs->scenePos().x(), -ext_attribs->scenePos().y());
187 		for(i=0; i < count; i++)
188 		{
189 			tab_obj=tab_objs.at(i);
190 
191 			//Reusing the subitem if it was allocated before
192 			if(!subitems.isEmpty() && i < subitems.size())
193 			{
194 				col_item=dynamic_cast<TableObjectView *>(subitems[i]);
195 				col_item->setSourceObject(tab_obj);
196 				col_item->configureObject();
197 				col_item->moveBy(-col_item->scenePos().x(),
198 												 -col_item->scenePos().y());
199 			}
200 			else
201 				col_item=new TableObjectView(tab_obj);
202 
203 			//Configures the item and set its position
204 			col_item->configureObject();
205 			col_item->moveBy(HorizSpacing, (i * col_item->boundingRect().height()) + VertSpacing);
206 
207 			/* Calculates the width of the name + type of the object. This is used to align all
208 			the constraint labels on table */
209 			width=col_item->getChildObject(TableObjectView::ObjDescriptor)->boundingRect().width() +
210 						col_item->getChildObject(TableObjectView::NameLabel)->boundingRect().width() + (3 * HorizSpacing);
211 			if(px < width)  px=width;
212 
213 			//Gets the maximum width of the column type label to align all at same horizontal position
214 			if(type_width < col_item->getChildObject(TableObjectView::TypeLabel)->boundingRect().width())
215 				type_width=col_item->getChildObject(TableObjectView::TypeLabel)->boundingRect().width() + (3 * HorizSpacing);
216 
217 			col_items.push_back(col_item);
218 		}
219 
220 		//Destroy the unused items
221 		i=subitems.size()-1;
222 		while(i > count-1)
223 		{
224 			col_item=dynamic_cast<TableObjectView *>(subitems[i]);
225 			ext_attribs->removeFromGroup(col_item);
226 			delete col_item;
227 			i--;
228 		}
229 
230 		//Set all items position
231 		while(!col_items.isEmpty())
232 		{
233 			col_item=dynamic_cast<TableObjectView *>(col_items.front());
234 			ext_attribs->removeFromGroup(col_item);
235 			col_items.pop_front();
236 
237 			//Positioning the type label
238 			col_item->setChildObjectXPos(TableObjectView::TypeLabel, px);
239 
240 			//Positioning the constraints label
241 			col_item->setChildObjectXPos(TableObjectView::ConstrAliasLabel, px + type_width);
242 			ext_attribs->addToGroup(col_item);
243 		}
244 	}
245 
246 	width = calculateWidth();
247 
248 	//Resizes the title using the new width
249 	title->resizeTitle(width, title->boundingRect().height());
250 
251 	//Resizes the columns/extended attributes using the new width
252 	for(int idx=0; idx < 2; idx++)
253 	{
254 		/* Configuring the brush and pen of the bodies even if they aren't visible
255 		 * the attributes toggler at the bottom of the view uses the color of the attributes body
256 		 * this will avoid the creation of a transparent toggler */
257 		if(!tag)
258 		{
259 			bodies[idx]->setBrush(this->getFillStyle(attribs[idx]));
260 			pen=this->getBorderStyle(attribs[idx]);
261 		}
262 		else
263 		{
264 			bodies[idx]->setBrush(tag->getFillStyle(tag_attribs[idx]));
265 			pen.setColor(tag->getElementColor(tag_attribs[idx], Tag::BorderColor));
266 		}
267 
268 		pen.setStyle(Qt::DashLine);
269 		bodies[idx]->setPen(pen);
270 
271 		// We avoid the construction of the rect related to the current body item if the related group isn't visible
272 		if(!groups[idx]->isVisible())
273 			continue;
274 
275 		bodies[idx]->setRect(QRectF(0,0, width, groups[idx]->boundingRect().height() + (2 * VertSpacing)));
276 
277 		if(idx==0)
278 			bodies[idx]->setPos(title->pos().x(), title->boundingRect().height() - 1);
279 		else
280 		{
281 			if(bodies[0]->isVisible())
282 				bodies[idx]->setPos(title->pos().x(),
283 														title->boundingRect().height() +
284 														bodies[0]->boundingRect().height() - 2);
285 			else
286 				bodies[idx]->setPos(title->pos().x(), title->boundingRect().height()-1);
287 		}
288 
289 		groups[idx]->setPos(bodies[idx]->pos());
290 
291 		subitems=groups[idx]->childItems();
292 		while(!subitems.isEmpty())
293 		{
294 			col_item=dynamic_cast<TableObjectView *>(subitems.front());
295 			subitems.pop_front();
296 			col_item->setChildObjectXPos(TableObjectView::ConstrAliasLabel,
297 																	 width - col_item->getChildObject(3)->boundingRect().width() - (2 * HorizSpacing));
298 		}
299 	}
300 
301 	this->bounding_rect.setTopLeft(title->boundingRect().topLeft());
302 	this->bounding_rect.setWidth(title->boundingRect().width());
303 
304 	BaseTableView::__configureObject(width);
305 
306 	if(!view->getAlias().isEmpty())
307 		table_tooltip += QString("\nAlias: %1").arg(view->getAlias());
308 
309 	if(!view->getComment().isEmpty())
310 		table_tooltip += QString("\n---\n%1").arg(view->getComment());
311 
312 	BaseObjectView::__configureObject();
313 	BaseObjectView::configureObjectShadow();
314 	BaseObjectView::configureObjectSelection();
315 	configureTag();
316 	configureSQLDisabledInfo();
317 	requestRelationshipsUpdate();
318 }
319 
320