/* Copyright 2006-2025 The QElectroTech Team This file is part of QElectroTech. QElectroTech is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. QElectroTech is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QElectroTech. If not, see . */ #include "templatecellsset.h" #include "../titleblockcell.h" #include "templateview.h" #include "templatevisualcell.h" #include /** Constructor @param parent_view View this set of cells are rattached to */ TitleBlockTemplateCellsSet::TitleBlockTemplateCellsSet(const TitleBlockTemplateView *parent_view) : parent_view_(parent_view) { } /** Destructor */ TitleBlockTemplateCellsSet::~TitleBlockTemplateCellsSet() { } /** @return a QPainterPath composed of the rectangles from cells within this set */ QPainterPath TitleBlockTemplateCellsSet::painterPath() const { QPainterPath cells_path; foreach (TitleBlockTemplateVisualCell *cell, *this) { cells_path.addRect(cell -> geometry()); } return(cells_path); } /** @return true if the cells within this set are composing a rectangle shape, false otherwise. */ bool TitleBlockTemplateCellsSet::isRectangle() const { if (!count()) return(false); if (count() == 1) return(true); QPolygonF points = painterPath().simplified().toFillPolygon(); if (points.isClosed()) points.pop_back(); return(points.count() == 4 || points.count() == 5); } /** @return true if all cells within this set are selected */ bool TitleBlockTemplateCellsSet::allCellsAreSelected() const { foreach (TitleBlockTemplateVisualCell *cell, *this) { if (!cell -> isSelected()) { return(false); } } return(true); } /** @return true if this set includes at least one cell which is spanned by a cell not present in this set, false otherwise. */ bool TitleBlockTemplateCellsSet::hasExternalSpan() const { // fetches all cells concerned by this set QSet all_cells = cells(true); // look for cells spanned by cells that do not belong to this set foreach (TitleBlockCell *cell, all_cells) { if (cell -> spanner_cell && !all_cells.contains(cell -> spanner_cell)) { return(true); } } return(false); } /** @return the top left cell within this set, or 0 if this set is empty */ TitleBlockTemplateVisualCell *TitleBlockTemplateCellsSet::topLeftCell() const { if (empty()) return(nullptr); if (count() == 1) return(first()); // look for cells at the top QMultiMap top_cells; foreach (TitleBlockTemplateVisualCell *cell_view, *this) { if (TitleBlockCell *cell = cell_view -> cell()) { top_cells.insert(cell -> num_row, cell_view); } } QList candidates = top_cells.values(top_cells.keys().first()); if (candidates.count() == 1) return(candidates.first()); // look for the cell at the left int lowest_num_col = 100000; TitleBlockTemplateVisualCell *candidate = nullptr; foreach (TitleBlockTemplateVisualCell *cell_view, candidates) { if (TitleBlockCell *cell = cell_view -> cell()) { if (cell -> num_col < lowest_num_col) { lowest_num_col = cell -> num_col; candidate = cell_view; } } } return(candidate); } /** @return the bottom right cell within this set, or 0 if this set is empty */ TitleBlockTemplateVisualCell *TitleBlockTemplateCellsSet::bottomRightCell() const { if (empty()) return(nullptr); if (count() == 1) return(first()); // look for cells at the bottom QMultiMap bottom_cells; foreach (TitleBlockTemplateVisualCell *cell_view, *this) { bottom_cells.insert(cell_view -> geometry().bottom(), cell_view); } QList candidates = bottom_cells.values(bottom_cells.keys().last()); if (candidates.count() == 1) return(candidates.first()); // look for the cell at the right qreal highest_right = -100000; TitleBlockTemplateVisualCell *candidate = nullptr; foreach (TitleBlockTemplateVisualCell *cell_view, candidates) { qreal right = cell_view -> geometry().right(); if (right > highest_right) { highest_right = right; candidate = cell_view; } } return(candidate); } /** @return the merge area, i.e. the rectangle delimited by the top left cell and the bottom right cell within this cells set. */ QRectF TitleBlockTemplateCellsSet::mergeAreaRect() const { QRectF merge_area; if (!parent_view_) return(merge_area); TitleBlockTemplateVisualCell *top_left_cell = topLeftCell(); if (!top_left_cell) return(merge_area); TitleBlockTemplateVisualCell *bottom_right_cell = bottomRightCell(); if (!bottom_right_cell) return(merge_area); merge_area.setTopLeft(top_left_cell -> geometry().topLeft()); merge_area.setBottomRight(bottom_right_cell -> geometry().bottomRight()); return(merge_area); } /** @param rect (Optional) The merge area to be considered; if a null QRectF is provided, this method will use mergeAreaRect(). @return the cells contained in the merge area of this cells set */ TitleBlockTemplateCellsSet TitleBlockTemplateCellsSet::mergeArea(const QRectF &rect) const { TitleBlockTemplateCellsSet merge_area(parent_view_); if (!parent_view_) return(merge_area); QRectF merge_area_rect = rect.isNull() ? mergeAreaRect() : rect; merge_area = parent_view_ -> cells(merge_area_rect); return(merge_area); } /** @return the list of cells rendered by the current selection @param include_spanned whether to include spanned cells or not */ QSet TitleBlockTemplateCellsSet::cells(bool include_spanned) const { QSet set; foreach (TitleBlockTemplateVisualCell *cell_view, *this) { if (TitleBlockCell *cell = cell_view -> cell()) { if (include_spanned) { foreach (TitleBlockCell *cell, cell_view -> cells()) { set << cell; } } else { set << cell; } } } return(set); }