From 2752e6705c3e5901ca63f42d975ee6ef9ead5b10 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Wed, 7 Oct 2026 17:36:22 +1300 Subject: [PATCH] Folio properties: paper size presets A0-A5, landscape or portrait A "Format" choice in the folio dimensions box sets the columns and rows so the whole folio, title block included, has the size of an ISO A sheet at the 96 dpi the PDF export uses. A4 landscape keeps 60 px columns and 80 px rows and comes out as an A4 page in the PDF. Nothing new is stored: the choice only fills in the columns and rows. A folio whose size already matches a paper shows that paper; any other shows "Custom". Changing the title block template after picking a paper fits the folio again; a template too tall for the paper falls back to "Custom". The same box is in Folio properties and in the new-folio defaults of the project and application settings. Co-Authored-By: Claude Opus 5.5 --- sources/ui/borderpropertieswidget.cpp | 224 ++++++++++++++++++++++ sources/ui/borderpropertieswidget.h | 18 +- sources/ui/borderpropertieswidget.ui | 19 ++ sources/ui/configpage/configpages.cpp | 4 + sources/ui/diagrampropertiesdialog.cpp | 4 + sources/ui/titleblockpropertieswidget.cpp | 33 ++++ sources/ui/titleblockpropertieswidget.h | 3 + 7 files changed, 303 insertions(+), 2 deletions(-) diff --git a/sources/ui/borderpropertieswidget.cpp b/sources/ui/borderpropertieswidget.cpp index f59ddc9e6..7c29e1182 100644 --- a/sources/ui/borderpropertieswidget.cpp +++ b/sources/ui/borderpropertieswidget.cpp @@ -18,8 +18,85 @@ #include "borderpropertieswidget.h" #include "../diagram.h" +#include "../qetapp.h" +#include "../titleblocktemplate.h" #include "ui_borderpropertieswidget.h" +#include +#include + +namespace { + /// ISO 216 A sizes in mm, landscape + struct PaperSize { const char *name; int width_mm; int height_mm; }; + const PaperSize paper_sizes[] = { + {"A0", 1189, 841}, + {"A1", 841, 594}, + {"A2", 594, 420}, + {"A3", 420, 297}, + {"A4", 297, 210}, + {"A5", 210, 148} + }; + + /// The PDF export draws one scene unit as one pixel at 96 dpi + int mmToPx(int mm) { return qRound(mm * 96.0 / 25.4); } + + /// The PDF export rounds a page within 3 pt of a paper size to that + /// size, and 4 px left over already rounds to 3.0 pt and misses + const int exact_fit = 3; + + /// Smallest column width or row height a paper size may give + const int min_cell = 10; + + /** + @brief fitCells + Split @p available pixels into cells close to @p current pixels. + First choice: cells within 15 %, then 30 %, of the current size + that leave at most exact_fit pixels, the closest to the current + size. Failing that, cells within 15 % that leave the fewest pixels. + @return the number of cells and their size + */ + QPair fitCells(int available, int current, int min_size, int max_count) + { + current = qMax(current, min_size); + auto size_for = [&](int count, double spread) { + const int size = available / count; + return (size < min_size || qAbs(size - current) > current * spread) + ? 0 : size; + }; + + for (double spread : {0.15, 0.30}) + { + QPair best(0, 0); + for (int count = 1 ; count <= max_count ; ++count) + { + const int size = size_for(count, spread); + if (size && available - count * size <= exact_fit && + (!best.first || qAbs(size - current) < qAbs(best.second - current))) + best = qMakePair(count, size); + } + if (best.first) return best; + } + + QPair best(0, 0); + std::tuple best_score(available, available); + for (int count = 1 ; count <= max_count ; ++count) + { + const int size = size_for(count, 0.15); + const std::tuple score(available - count * size, + qAbs(size - current)); + if (size && score < best_score) { + best_score = score; + best = qMakePair(count, size); + } + } + if (best.first) return best; + + //No size near the current one when the cell count is capped + const int count = qBound(1, qRound(double(available) / current), max_count); + return qMakePair(count, available / count); + } +} + /** @brief BorderPropertiesWidget::BorderPropertiesWidget default constructor @@ -31,6 +108,30 @@ BorderPropertiesWidget::BorderPropertiesWidget(const BorderProperties &bp, QWidg ui(new Ui::BorderPropertiesWidget) { ui->setupUi(this); + + ui->m_paper_cb->addItem(tr("Personnalisé")); + for (const PaperSize &paper : paper_sizes) + ui->m_paper_cb->addItem(QString::fromLatin1(paper.name)); + ui->m_orientation_cb->addItem(tr("Paysage")); + ui->m_orientation_cb->addItem(tr("Portrait")); + + if (TitleBlockTemplate *tbt = QETApp::defaultTitleBlockTemplate()) + m_titleblock_height = tbt->height(); + + //activated() is only emitted for a choice the user makes + auto paper_picked = [this]() { + m_paper_picked = ui->m_paper_cb->currentIndex() > 0; + applyPaperSize(); + }; + connect(ui->m_paper_cb, qOverload(&QComboBox::activated), + this, paper_picked); + connect(ui->m_orientation_cb, qOverload(&QComboBox::activated), + this, paper_picked); + for (QSpinBox *sb : {ui->m_colums_count_sp, ui->m_columns_width_sp, + ui->m_rows_count_sp, ui->m_rows_height_sp}) + connect(sb, qOverload(&QSpinBox::valueChanged), + this, &BorderPropertiesWidget::showMatchingPaperSize); + setProperties(bp); } @@ -57,6 +158,7 @@ void BorderPropertiesWidget::setProperties(const BorderProperties &bp) ui -> m_rows_count_sp ->setValue (m_properties.rows_count); ui -> m_rows_height_sp ->setValue (m_properties.rows_height); ui -> m_display_rows_cb ->setChecked (m_properties.display_rows); + showMatchingPaperSize(); } /** @@ -83,3 +185,125 @@ void BorderPropertiesWidget::setReadOnly(const bool &ro) { ui->border_gb->setDisabled(ro); } + +/** + @brief BorderPropertiesWidget::setTitleBlockSize + Tell the widget the title block the folio uses: its height counts in + the size of the page. If the user picked a paper size in this widget, + the columns and rows are fitted again so the folio keeps that size; + otherwise they are left alone. + @param height : height of the title block template + @param edge : edge of the folio the title block sits on +*/ +void BorderPropertiesWidget::setTitleBlockSize(int height, Qt::Edge edge) +{ + m_titleblock_height = height; + m_titleblock_edge = edge; + if (m_paper_picked) + applyPaperSize(); + else + showMatchingPaperSize(); +} + +/** + @brief BorderPropertiesWidget::applyPaperSize + Set the columns and rows so the folio, title block included, has the + size of the chosen paper. The column width and row height stay close + to the current ones. +*/ +void BorderPropertiesWidget::applyPaperSize() +{ + const int index = ui->m_paper_cb->currentIndex() - 1; + ui->m_orientation_cb->setEnabled(index >= 0); + if (index < 0) return; + + int columns, column_width, rows, row_height; + if (!fitPaperSize(index, ui->m_orientation_cb->currentIndex() == 0, + columns, column_width, rows, row_height)) + { + //The title block leaves no room on this paper + showMatchingPaperSize(); + return; + } + + m_updating = true; + ui->m_colums_count_sp->setValue(columns); + ui->m_columns_width_sp->setValue(column_width); + ui->m_rows_count_sp->setValue(rows); + ui->m_rows_height_sp->setValue(row_height); + m_updating = false; +} + +/** + @brief BorderPropertiesWidget::fitPaperSize + Compute the columns and rows that give the folio, title block + included, the size of a paper, with cells close to the current ones. + @param index : index of the paper in paper_sizes + @param landscape : true for landscape, false for portrait + @return false if the paper is too small for the title block +*/ +bool BorderPropertiesWidget::fitPaperSize(int index, bool landscape, + int &columns, int &column_width, + int &rows, int &row_height) const +{ + const PaperSize &paper = paper_sizes[index]; + const int page_width = mmToPx(landscape ? paper.width_mm : paper.height_mm); + const int page_height = mmToPx(landscape ? paper.height_mm : paper.width_mm); + + //The page is one pixel larger than the border, for its line + int width = page_width - 1 - qRound(m_properties.rows_header_width); + int height = page_height - 1 - qRound(m_properties.columns_header_height); + if (m_titleblock_edge == Qt::BottomEdge) + height -= m_titleblock_height; + else + width -= m_titleblock_height; + if (width < min_cell || height < min_cell) return false; + + const QPair c = fitCells(width, + ui->m_columns_width_sp->value(), + min_cell, + ui->m_colums_count_sp->maximum()); + const QPair r = fitCells(height, + ui->m_rows_height_sp->value(), + min_cell, + ui->m_rows_count_sp->maximum()); + columns = c.first; + column_width = c.second; + rows = r.first; + row_height = r.second; + return true; +} + +/** + @brief BorderPropertiesWidget::showMatchingPaperSize + Show the paper size the folio has, or "Custom" if it has none: the + folio has a paper size when choosing it would change nothing. +*/ +void BorderPropertiesWidget::showMatchingPaperSize() +{ + if (m_updating) return; + m_paper_picked = false; + + int found = 0; + int orientation = ui->m_orientation_cb->currentIndex(); + for (int i = 0 ; i < int(std::size(paper_sizes)) && !found ; ++i) + { + for (int o = 0 ; o < 2 ; ++o) + { + int columns, column_width, rows, row_height; + if (fitPaperSize(i, o == 0, columns, column_width, rows, row_height) && + columns == ui->m_colums_count_sp->value() && + column_width == ui->m_columns_width_sp->value() && + rows == ui->m_rows_count_sp->value() && + row_height == ui->m_rows_height_sp->value()) + { + found = i + 1; + orientation = o; + break; + } + } + } + ui->m_paper_cb->setCurrentIndex(found); + ui->m_orientation_cb->setCurrentIndex(orientation); + ui->m_orientation_cb->setEnabled(found > 0); +} diff --git a/sources/ui/borderpropertieswidget.h b/sources/ui/borderpropertieswidget.h index 01669c761..1042e23c4 100644 --- a/sources/ui/borderpropertieswidget.h +++ b/sources/ui/borderpropertieswidget.h @@ -41,12 +41,26 @@ class BorderPropertiesWidget : public QWidget void setProperties(const BorderProperties &bp); const BorderProperties &properties(); void setReadOnly (const bool &ro); - - private slots: + void setTitleBlockSize(int height, Qt::Edge edge); private: + void applyPaperSize(); + void showMatchingPaperSize(); + bool fitPaperSize(int index, bool landscape, + int &columns, int &column_width, + int &rows, int &row_height) const; + Ui::BorderPropertiesWidget *ui; BorderProperties m_properties; + /// height of the title block the folio is drawn with, and + /// the edge it sits on: it counts in the size of the page + int m_titleblock_height = 0; + Qt::Edge m_titleblock_edge = Qt::BottomEdge; + /// true while the widget itself changes the spin boxes + bool m_updating = false; + /// true once the user picked a paper size, until the columns or + /// rows are edited by hand + bool m_paper_picked = false; }; #endif // BORDERPROPERTIESWIDGET_H diff --git a/sources/ui/borderpropertieswidget.ui b/sources/ui/borderpropertieswidget.ui index 7fcee9bfd..63021ab12 100644 --- a/sources/ui/borderpropertieswidget.ui +++ b/sources/ui/borderpropertieswidget.ui @@ -80,7 +80,24 @@ + + + + Format : + + + + + + Règle les colonnes et les lignes pour que le folio, cartouche compris, ait la taille de ce format de papier, à 96 points par pouce comme l'export PDF. + + + + + + + Qt::Vertical @@ -105,6 +122,8 @@ m_rows_height_sp m_display_columns_cb m_display_rows_cb + m_paper_cb + m_orientation_cb diff --git a/sources/ui/configpage/configpages.cpp b/sources/ui/configpage/configpages.cpp index b9ac8562c..fb3ffc687 100644 --- a/sources/ui/configpage/configpages.cpp +++ b/sources/ui/configpage/configpages.cpp @@ -139,6 +139,10 @@ NewDiagramPage::NewDiagramPage(QETProject *project, } connect(ipw, &TitleBlockPropertiesWidget::openAutoNumFolioEditor, this, &NewDiagramPage::changeToAutoFolioTab); + bpw -> setTitleBlockSize(ipw -> currentTitleBlockHeight(), + ipw -> currentTitleBlockEdge()); + connect(ipw, &TitleBlockPropertiesWidget::titleBlockSizeChanged, + bpw, &BorderPropertiesWidget::setTitleBlockSize); // main tab widget QTabWidget *tab_widget = new QTabWidget(this); diff --git a/sources/ui/diagrampropertiesdialog.cpp b/sources/ui/diagrampropertiesdialog.cpp index cc3046b9c..15386bbfe 100644 --- a/sources/ui/diagrampropertiesdialog.cpp +++ b/sources/ui/diagrampropertiesdialog.cpp @@ -65,6 +65,10 @@ DiagramPropertiesDialog::DiagramPropertiesDialog(Diagram *diagram, QWidget *pare titleblock_infos -> setReadOnly(diagram_is_read_only); connect(titleblock_infos, &TitleBlockPropertiesWidget::openAutoNumFolioEditor, this, &DiagramPropertiesDialog::editAutoFolioNum); + border_infos -> setTitleBlockSize(titleblock_infos -> currentTitleBlockHeight(), + titleblock_infos -> currentTitleBlockEdge()); + connect(titleblock_infos, &TitleBlockPropertiesWidget::titleBlockSizeChanged, + border_infos, &BorderPropertiesWidget::setTitleBlockSize); //titleblock_infos->setMinimumSize(590,480); //Minimum Size needed for correct display //Conductor widget diff --git a/sources/ui/titleblockpropertieswidget.cpp b/sources/ui/titleblockpropertieswidget.cpp index 88bdd4842..7b83c9155 100644 --- a/sources/ui/titleblockpropertieswidget.cpp +++ b/sources/ui/titleblockpropertieswidget.cpp @@ -336,6 +336,30 @@ QString TitleBlockPropertiesWidget::currentTitleBlockTemplateName() const return QString(); } +/** + @brief TitleBlockPropertiesWidget::currentTitleBlockHeight + @return the height of the current title block template +*/ +int TitleBlockPropertiesWidget::currentTitleBlockHeight() const +{ + const TitleBlockTemplate *tbt = nullptr; + if (!currentTitleBlockTemplateName().isEmpty()) + tbt = templateForIndex(ui -> m_tbt_cb -> currentIndex()); + if (!tbt) + tbt = QETApp::defaultTitleBlockTemplate(); + return tbt ? tbt -> height() : 0; +} + +/** + @brief TitleBlockPropertiesWidget::currentTitleBlockEdge + @return the edge of the folio the title block is displayed at +*/ +Qt::Edge TitleBlockPropertiesWidget::currentTitleBlockEdge() const +{ + return ui -> m_display_at_cb -> currentIndex() == 0 ? Qt::BottomEdge + : Qt::RightEdge; +} + /** @brief TitleBlockPropertiesWidget::addCollection add a collection of title block available in the combo box @@ -388,6 +412,15 @@ void TitleBlockPropertiesWidget::initDialog( this, &TitleBlockPropertiesWidget::changeCurrentTitleBlockTemplate); + auto size_changed = [this]() { + emit titleBlockSizeChanged(currentTitleBlockHeight(), + currentTitleBlockEdge()); + }; + connect(ui->m_tbt_cb, qOverload(&QComboBox::currentIndexChanged), + this, size_changed); + connect(ui->m_display_at_cb, qOverload(&QComboBox::currentIndexChanged), + this, size_changed); + if (project!= nullptr){ keys_2 = project -> folioAutoNum().keys(); foreach (QString str, keys_2) { ui -> auto_page_cb -> addItem(str); } diff --git a/sources/ui/titleblockpropertieswidget.h b/sources/ui/titleblockpropertieswidget.h index 127f04c9c..4804f9837 100644 --- a/sources/ui/titleblockpropertieswidget.h +++ b/sources/ui/titleblockpropertieswidget.h @@ -70,6 +70,8 @@ class TitleBlockPropertiesWidget : public QWidget void setTitleBlockTemplatesVisible(const bool &visible); void setReadOnly (const bool &ro); + int currentTitleBlockHeight() const; + Qt::Edge currentTitleBlockEdge() const; static void addTemplateVariables( DiagramContext &context, @@ -96,6 +98,7 @@ class TitleBlockPropertiesWidget : public QWidget signals: void set_auto_page_num() const; void openAutoNumFolioEditor (QString); + void titleBlockSizeChanged(int height, Qt::Edge edge); private: Ui::TitleBlockPropertiesWidget *ui;