1 /*
2 	Copyright 2006-2019 The QElectroTech Team
3 	This file is part of QElectroTech.
4 
5 	QElectroTech is free software: you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation, either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	QElectroTech 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 	You should have received a copy of the GNU General Public License
16 	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "templatecellsset.h"
19 #include "templatevisualcell.h"
20 #include "templateview.h"
21 #include "titleblockcell.h"
22 
23 /**
24 	Constructor
25 	@param parent_view View this set of cells are rattached to
26 */
TitleBlockTemplateCellsSet(const TitleBlockTemplateView * parent_view)27 TitleBlockTemplateCellsSet::TitleBlockTemplateCellsSet(const TitleBlockTemplateView *parent_view) :
28 	parent_view_(parent_view)
29 {
30 }
31 
32 /**
33 	Copy constructor
34 	@param copy TitleBlockTemplateCellsSet object to copy
35 */
TitleBlockTemplateCellsSet(const TitleBlockTemplateCellsSet & copy)36 TitleBlockTemplateCellsSet::TitleBlockTemplateCellsSet(const TitleBlockTemplateCellsSet &copy) :
37 	QList<TitleBlockTemplateVisualCell *>(copy),
38 	parent_view_(copy.parent_view_)
39 {
40 }
41 
42 /**
43 	Destructor
44 */
~TitleBlockTemplateCellsSet()45 TitleBlockTemplateCellsSet::~TitleBlockTemplateCellsSet() {
46 }
47 
48 /**
49 	@return a QPainterPath composed of the rectangles from cells within this set
50 */
painterPath() const51 QPainterPath TitleBlockTemplateCellsSet::painterPath() const {
52 	QPainterPath cells_path;
53 	foreach (TitleBlockTemplateVisualCell *cell, *this) {
54 		cells_path.addRect(cell -> geometry());
55 	}
56 	return(cells_path);
57 }
58 
59 /**
60 	@return true if the cells within this set are composing a rectangle shape,
61 	false otherwise.
62 */
isRectangle() const63 bool TitleBlockTemplateCellsSet::isRectangle() const {
64 	if (!count()) return(false);
65 	if (count() == 1) return(true);
66 
67 	QPolygonF points = painterPath().simplified().toFillPolygon();
68 	if (points.isClosed()) points.pop_back();
69 
70 	return(points.count() == 4 || points.count() == 5);
71 }
72 
73 /**
74 	@return true if all cells within this set are selected
75 */
allCellsAreSelected() const76 bool TitleBlockTemplateCellsSet::allCellsAreSelected() const {
77 	foreach (TitleBlockTemplateVisualCell *cell, *this) {
78 		if (!cell -> isSelected()) {
79 			return(false);
80 		}
81 	}
82 	return(true);
83 }
84 
85 /**
86 	@return true if this set includes at least one cell which is spanned by a
87 	cell not present in this set, false otherwise.
88 */
hasExternalSpan() const89 bool TitleBlockTemplateCellsSet::hasExternalSpan() const {
90 	// fetches all cells concerned by this set
91 	QSet<TitleBlockCell *> all_cells = cells(true);
92 
93 	// look for cells spanned by cells that do not belong to this set
94 	foreach (TitleBlockCell *cell, all_cells) {
95 		if (cell -> spanner_cell && !all_cells.contains(cell -> spanner_cell)) {
96 			return(true);
97 		}
98 	}
99 	return(false);
100 }
101 
102 /**
103 	@return the top left cell within this set, or 0 if this set is empty
104 */
topLeftCell() const105 TitleBlockTemplateVisualCell *TitleBlockTemplateCellsSet::topLeftCell() const {
106 	if (empty()) return(nullptr);
107 	if (count() == 1) return(first());
108 
109 	// look for cells at the top
110 	QMultiMap<int, TitleBlockTemplateVisualCell *> top_cells;
111 	foreach (TitleBlockTemplateVisualCell *cell_view, *this) {
112 		if (TitleBlockCell *cell = cell_view -> cell()) {
113 			top_cells.insertMulti(cell -> num_row, cell_view);
114 		}
115 	}
116 	QList<TitleBlockTemplateVisualCell *> candidates = top_cells.values(top_cells.keys().first());
117 	if (candidates.count() == 1) return(candidates.first());
118 
119 	// look for the cell at the left
120 	int lowest_num_col = 100000;
121 	TitleBlockTemplateVisualCell *candidate = nullptr;
122 	foreach (TitleBlockTemplateVisualCell *cell_view, candidates) {
123 		if (TitleBlockCell *cell = cell_view -> cell()) {
124 			if (cell -> num_col < lowest_num_col) {
125 				lowest_num_col = cell -> num_col;
126 				candidate = cell_view;
127 			}
128 		}
129 	}
130 	return(candidate);
131 }
132 
133 /**
134 	@return the bottom right cell within this set, or 0 if this set is empty
135 */
bottomRightCell() const136 TitleBlockTemplateVisualCell *TitleBlockTemplateCellsSet::bottomRightCell() const {
137 	if (empty()) return(nullptr);
138 	if (count() == 1) return(first());
139 
140 	// look for cells at the bottom
141 	QMultiMap<qreal, TitleBlockTemplateVisualCell *> bottom_cells;
142 	foreach (TitleBlockTemplateVisualCell *cell_view, *this) {
143 		bottom_cells.insertMulti(cell_view -> geometry().bottom(), cell_view);
144 	}
145 	QList<TitleBlockTemplateVisualCell *> candidates = bottom_cells.values(bottom_cells.keys().last());
146 	if (candidates.count() == 1) return(candidates.first());
147 
148 	// look for the cell at the right
149 	qreal highest_right = -100000;
150 	TitleBlockTemplateVisualCell *candidate = nullptr;
151 	foreach (TitleBlockTemplateVisualCell *cell_view, candidates) {
152 		qreal right = cell_view -> geometry().right();
153 		if (right > highest_right) {
154 			highest_right = right;
155 			candidate = cell_view;
156 		}
157 	}
158 	return(candidate);
159 }
160 
161 /**
162 	@return the merge area, i.e. the rectangle delimited by the top left cell
163 	and the bottom right cell within this cells set.
164 */
mergeAreaRect() const165 QRectF TitleBlockTemplateCellsSet::mergeAreaRect() const {
166 	QRectF merge_area;
167 	if (!parent_view_) return(merge_area);
168 
169 	TitleBlockTemplateVisualCell *top_left_cell = topLeftCell();
170 	if (!top_left_cell) return(merge_area);
171 	TitleBlockTemplateVisualCell *bottom_right_cell = bottomRightCell();
172 	if (!bottom_right_cell) return(merge_area);
173 
174 	merge_area.setTopLeft(top_left_cell -> geometry().topLeft());
175 	merge_area.setBottomRight(bottom_right_cell -> geometry().bottomRight());
176 	return(merge_area);
177 }
178 
179 /**
180 	@param rect (Optional) The merge area to be considered; if a null QRectF is
181 	provided, this method will use mergeAreaRect().
182 	@return the cells contained in the merge area of this cells set
183 */
mergeArea(const QRectF & rect) const184 TitleBlockTemplateCellsSet TitleBlockTemplateCellsSet::mergeArea(const QRectF &rect) const {
185 	TitleBlockTemplateCellsSet merge_area(parent_view_);
186 	if (!parent_view_) return(merge_area);
187 
188 	QRectF merge_area_rect = rect.isNull() ? mergeAreaRect() : rect;
189 
190 	merge_area = parent_view_ -> cells(merge_area_rect);
191 	return(merge_area);
192 }
193 
194 /**
195 	@return the list of cells rendered by the current selection
196 	@param include_spanned whether to include spanned cells or not
197 */
cells(bool include_spanned) const198 QSet<TitleBlockCell *> TitleBlockTemplateCellsSet::cells(bool include_spanned) const {
199 	QSet<TitleBlockCell *> set;
200 	foreach (TitleBlockTemplateVisualCell *cell_view, *this) {
201 		if (TitleBlockCell *cell = cell_view -> cell()) {
202 			if (include_spanned) {
203 				foreach (TitleBlockCell *cell, cell_view -> cells()) {
204 					set << cell;
205 				}
206 			} else {
207 				set << cell;
208 			}
209 		}
210 	}
211 	return(set);
212 }
213