mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-07 20:34:14 +02:00
Merge pull request #1362 from ispyisail/feature/folio-paper-size
Add paper sizes A0–A5 to the folio dimensions
This commit is contained in:
@@ -18,8 +18,89 @@
|
||||
#include "borderpropertieswidget.h"
|
||||
|
||||
#include "../diagram.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../titleblocktemplate.h"
|
||||
#include "ui_borderpropertieswidget.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
|
||||
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;
|
||||
|
||||
/// Fewest columns or rows a folio may have: BorderTitleBlock raises
|
||||
/// any lower count to 3 (MIN_COLUMN_COUNT, MIN_ROW_COUNT)
|
||||
const int min_count = 3;
|
||||
|
||||
/**
|
||||
@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<int, int> 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<int, int> best(0, 0);
|
||||
for (int count = min_count ; 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<int, int> best(0, 0);
|
||||
std::tuple<int, int> best_score(available, available);
|
||||
for (int count = min_count ; count <= max_count ; ++count)
|
||||
{
|
||||
const int size = size_for(count, 0.15);
|
||||
const std::tuple<int, int> 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(min_count, qRound(double(available) / current), max_count);
|
||||
return qMakePair(count, available / count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief BorderPropertiesWidget::BorderPropertiesWidget
|
||||
default constructor
|
||||
@@ -31,6 +112,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<int>(&QComboBox::activated),
|
||||
this, paper_picked);
|
||||
connect(ui->m_orientation_cb, qOverload<int>(&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<int>(&QSpinBox::valueChanged),
|
||||
this, &BorderPropertiesWidget::showMatchingPaperSize);
|
||||
|
||||
setProperties(bp);
|
||||
}
|
||||
|
||||
@@ -57,6 +162,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 +189,126 @@ 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 * min_count || height < min_cell * min_count)
|
||||
return false;
|
||||
|
||||
const QPair<int, int> c = fitCells(width,
|
||||
ui->m_columns_width_sp->value(),
|
||||
min_cell,
|
||||
ui->m_colums_count_sp->maximum());
|
||||
const QPair<int, int> 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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -80,7 +80,24 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="m_paper_label">
|
||||
<property name="text">
|
||||
<string>Format :</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="m_paper_cb">
|
||||
<property name="toolTip">
|
||||
<string>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.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QComboBox" name="m_orientation_cb"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@@ -105,6 +122,8 @@
|
||||
<tabstop>m_rows_height_sp</tabstop>
|
||||
<tabstop>m_display_columns_cb</tabstop>
|
||||
<tabstop>m_display_rows_cb</tabstop>
|
||||
<tabstop>m_paper_cb</tabstop>
|
||||
<tabstop>m_orientation_cb</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<int>(&QComboBox::currentIndexChanged),
|
||||
this, size_changed);
|
||||
connect(ui->m_display_at_cb, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this, size_changed);
|
||||
|
||||
if (project!= nullptr){
|
||||
keys_2 = project -> folioAutoNum().keys();
|
||||
foreach (QString str, keys_2) { ui -> auto_page_cb -> addItem(str); }
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user