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 "tableview.h"
20 
TableView(PhysicalTable * table)21 TableView::TableView(PhysicalTable *table) : BaseTableView(table)
22 {
23 	connect(table, SIGNAL(s_objectModified()), this, SLOT(configureObject()));
24 	this->configureObject();
25 }
26 
configureObject()27 void TableView::configureObject()
28 {
29 	/* If the table isn't visible we abort the current configuration
30 	 * and mark its geometry update as pending so in the next call to
31 	 * setVisible(true) the geometry can be updated (see BaseObjectView::itemChange()) */
32 	if(!this->isVisible())
33 	{
34 		pending_geom_update = true;
35 		return;
36 	}
37 
38 	PhysicalTable *table=dynamic_cast<PhysicalTable *>(this->getUnderlyingObject());
39 	int i, count, obj_idx;
40 	double width=0, px=0, cy=0, old_width=0, old_height=0;
41 	unsigned start_col = 0, end_col = 0, start_ext = 0, end_ext = 0;
42 	QPen pen;
43 	TableObjectView *col_item=nullptr;
44 	QList<QGraphicsItem *> subitems;
45 	QList<TableObjectView *> col_items;
46 	TableObject *tab_obj=nullptr;
47 	QGraphicsItemGroup *groups[]={ columns, ext_attribs };
48 	RoundedRectItem *bodies[]={ body, ext_attribs_body };
49 	vector<TableObject *> tab_objs, columns, ext_tab_objs;
50 	QStringList atribs, tag_attribs = { Attributes::TableBody, Attributes::TableExtBody };
51 	Tag *tag=table->getTag();
52 	CollapseMode collapse_mode = table->getCollapseMode();
53 	vector<ObjectType> ext_types = BaseObject::getChildObjectTypes(table->getObjectType());
54 	bool has_col_pag = false, has_ext_pag = false;
55 
56 	if(table->getObjectType() == ObjectType::Table)
57 		atribs.append({ Attributes::TableBody, Attributes::TableExtBody });
58 	else
59 		atribs.append({ Attributes::ForeignTableBody, Attributes::ForeignTableExtBody });
60 
61 	// Clear the selected children objects vector since we'll (re)configure the whole table
62 	sel_child_objs.clear();
63 
64 	//Configures the table title
65 	title->configureObject(table);
66 
67 	// We store the columns in a separated vector in order to paginate them (if enabled)
68 	columns.assign(table->getObjectList(ObjectType::Column)->begin(),
69 								 table->getObjectList(ObjectType::Column)->end());
70 
71 	// We store the extended attributes in a separated vector in order to paginate them (if enabled)
72 	for(auto &type : ext_types)
73 	{
74 		if(type == ObjectType::Column)
75 			continue;
76 
77 		ext_tab_objs.insert(ext_tab_objs.end(),
78 												table->getObjectList(type)->begin(),
79 												table->getObjectList(type)->end());
80 	}
81 
82 	has_col_pag = configurePaginationParams(BaseTable::AttribsSection, columns.size(), start_col, end_col);
83 
84 	has_ext_pag = configurePaginationParams(BaseTable::ExtAttribsSection,
85 																						collapse_mode != CollapseMode::ExtAttribsCollapsed ? ext_tab_objs.size() : 0,
86 																						start_ext, end_ext);
87 
88 	attribs_toggler->setHasExtAttributes(!hide_ext_attribs && !ext_tab_objs.empty());
89 
90 	px=0;
91 	old_width=this->bounding_rect.width();
92 	old_height=this->bounding_rect.height();
93 
94 	for(obj_idx=0; obj_idx < 2; obj_idx++)
95 	{
96 		tab_objs.clear();
97 
98 		if(obj_idx==0)
99 		{
100 			if(collapse_mode != CollapseMode::AllAttribsCollapsed)
101 			{
102 				if(table->isPaginationEnabled() && has_col_pag)
103 					tab_objs.assign(columns.begin() + start_col, columns.begin() + end_col);
104 				else
105 					tab_objs.assign(columns.begin(), columns.end());
106 			}
107 		}
108 		else
109 		{
110 			if(!hide_ext_attribs && collapse_mode == CollapseMode::NotCollapsed)
111 			{
112 				if(table->isPaginationEnabled() && has_ext_pag)
113 					tab_objs.assign(ext_tab_objs.begin() + start_ext, ext_tab_objs.begin() + end_ext);
114 				else
115 					tab_objs.assign(ext_tab_objs.begin(), ext_tab_objs.end());
116 			}
117 		}
118 
119 		//Gets the subitems of the current group
120 		subitems=groups[obj_idx]->childItems();
121 		groups[obj_idx]->moveBy(-groups[obj_idx]->scenePos().x(),
122 														-groups[obj_idx]->scenePos().y());
123 		count=tab_objs.size();
124 		groups[obj_idx]->setVisible(count > 0);
125 		bodies[obj_idx]->setVisible(count > 0);
126 
127 		for(i=0; i < count; i++)
128 		{
129 			tab_obj=tab_objs.at(i);
130 
131 			//Reusing the subitem if it was allocated before
132 			if(!subitems.isEmpty() && i < subitems.size())
133 			{
134 				col_item=dynamic_cast<TableObjectView *>(subitems[i]);
135 				col_item->setSourceObject(tab_obj);
136 				col_item->configureObject();
137 				col_item->moveBy(-col_item->scenePos().x(),-col_item->scenePos().y());
138 			}
139 			else
140 				col_item=new TableObjectView(tab_obj);
141 
142 			//Configures the item and set its position
143 			col_item->configureObject();
144 			col_item->moveBy(HorizSpacing, (i * col_item->boundingRect().height()) + VertSpacing);
145 
146 			/* Calculates the width of the name + type of the object. This is used to align all
147 			the constraint labels on table */
148 			width=col_item->getChildObject(TableObjectView::ObjDescriptor)->boundingRect().width() +
149 						col_item->getChildObject(TableObjectView::NameLabel)->boundingRect().width() + (5 * HorizSpacing);
150 
151 			if(px < width)
152 				px=width;
153 
154 			col_items.push_back(col_item);
155 		}
156 
157 		//Destroy the unused items
158 		i=subitems.size()-1;
159 		while(i > count-1)
160 		{
161 			col_item=dynamic_cast<TableObjectView *>(subitems[i]);
162 			groups[obj_idx]->removeFromGroup(col_item);
163 			delete col_item;
164 			i--;
165 		}
166 
167 		//Set all items position
168 		while(!col_items.isEmpty())
169 		{
170 			col_item=dynamic_cast<TableObjectView *>(col_items.front());
171 			groups[obj_idx]->removeFromGroup(col_item);
172 			col_items.pop_front();
173 
174 			//Positioning the type label
175 			col_item->setChildObjectXPos(TableObjectView::TypeLabel, px);
176 
177 			//Positioning the constraints label
178 			col_item->setChildObjectXPos(TableObjectView::ConstrAliasLabel,
179 																	 px + (col_item->getChildObject(TableObjectView::TypeLabel)->boundingRect().width() * 1.05));
180 
181 			groups[obj_idx]->addToGroup(col_item);
182 		}
183 	}
184 
185 	width = calculateWidth();
186 
187 	//Resizes the title using the new width
188 	title->resizeTitle(width, title->boundingRect().height());
189 
190 	//Resizes the columns/extended attributes using the new width
191 	for(obj_idx=0; obj_idx < 2; obj_idx++)
192 	{
193 		bodies[obj_idx]->setRect(QRectF(0,0, width, groups[obj_idx]->boundingRect().height() + (2 * VertSpacing)));
194 		pen=this->getBorderStyle(atribs[obj_idx]);
195 
196 		if(table->isPartition())
197 		  pen.setStyle(Qt::DashLine);
198 
199 		if(!tag)
200 			bodies[obj_idx]->setBrush(this->getFillStyle(atribs[obj_idx]));
201 		else
202 		{
203 			pen.setColor(tag->getElementColor(tag_attribs[obj_idx], Tag::BorderColor));
204 			bodies[obj_idx]->setBrush(tag->getFillStyle(tag_attribs[obj_idx]));
205 		}
206 
207 		bodies[obj_idx]->setPen(pen);
208 
209 		if(obj_idx==0)
210 			bodies[obj_idx]->setPos(title->pos().x(), title->boundingRect().height()-1);
211 		else
212 		{
213 			if(bodies[0]->isVisible())
214 				bodies[obj_idx]->setPos(title->pos().x(),
215 										title->boundingRect().height() +
216 										bodies[0]->boundingRect().height() - 2);
217 			else
218 				bodies[obj_idx]->setPos(title->pos().x(), title->boundingRect().height()-1);
219 		}
220 
221 		groups[obj_idx]->setPos(bodies[obj_idx]->pos());
222 
223 		subitems=groups[obj_idx]->childItems();
224 		while(!subitems.isEmpty())
225 		{
226 			col_item=dynamic_cast<TableObjectView *>(subitems.front());
227 			subitems.pop_front();
228 			col_item->setChildObjectXPos(TableObjectView::ConstrAliasLabel,
229 																	 width - col_item->getChildObject(TableObjectView::ConstrAliasLabel)->boundingRect().width() - (2 * HorizSpacing) - 1);
230 
231 			//Generating the connection points of the columns
232 			if(obj_idx==0)
233 			{
234 				tab_obj=dynamic_cast<TableObject *>(col_item->getUnderlyingObject());
235 				cy=title->boundingRect().height() + col_item->pos().y() + (col_item->boundingRect().height()/2);
236 				conn_points[tab_obj].resize(2);
237 				conn_points[tab_obj][LeftConnPoint]=QPointF(col_item->pos().x() - 1.5, cy);
238 				conn_points[tab_obj][RightConnPoint]=QPointF(col_item->pos().x() + width - 1.5  , cy);
239 			}
240 		}
241 	}
242 
243 	BaseTableView::__configureObject(width);
244 
245 	if(table->isPartitioned())
246 		table_tooltip += QString("\n%1 (%2)").arg(tr("Partitioned")).arg(~table->getPartitioningType());
247 
248 	if(table->isPartition())
249 		table_tooltip += QString("\n%1 of %2").arg(tr("Partition")).arg(table->getPartitionedTable()->getSignature(true));
250 
251 	if(!table->getAlias().isEmpty())
252 		table_tooltip += QString("\nAlias: %1").arg(table->getAlias());
253 
254 	if(!table->getComment().isEmpty())
255 		table_tooltip += QString("\n---\n%1").arg(table->getComment());
256 
257 	BaseObjectView::__configureObject();
258 	configureTag();
259 	configureSQLDisabledInfo();
260 
261 	if((old_width!=0 && this->bounding_rect.width()!=old_width) ||
262 			(old_height!=0 && this->bounding_rect.height()!=old_height))
263 		emit s_objectDimensionChanged();
264 	else
265 		requestRelationshipsUpdate();
266 }
267 
getConnectionPoints(TableObject * tab_obj,unsigned pnt_type)268 QPointF TableView::getConnectionPoints(TableObject *tab_obj, unsigned pnt_type)
269 {
270 	if(!tab_obj)
271 		throw Exception(ErrorCode::OprNotAllocatedObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
272 	else if(pnt_type > RightConnPoint)
273 		throw Exception(ErrorCode::RefElementInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
274 	else if(conn_points.count(tab_obj)==0)
275 		//Returns the center point in case of the connection point of the table object wasn't calculated already
276 		return this->getCenter();
277 
278 	return conn_points[tab_obj][pnt_type];
279 }
280