diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index 5721e4111..1bf68b48d 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -168,6 +168,9 @@ set(QET_SRC_FILES ${QET_DIR}/sources/borderproperties.h ${QET_DIR}/sources/bordertitleblock.cpp ${QET_DIR}/sources/bordertitleblock.h + ${QET_DIR}/sources/bordercelllabels.h + ${QET_DIR}/sources/cellruler.cpp + ${QET_DIR}/sources/cellruler.h ${QET_DIR}/sources/conductorautonumerotation.cpp ${QET_DIR}/sources/conductorautonumerotation.h ${QET_DIR}/sources/conductornumexport.cpp diff --git a/sources/bordercelllabels.h b/sources/bordercelllabels.h new file mode 100644 index 000000000..d5dbb6e29 --- /dev/null +++ b/sources/bordercelllabels.h @@ -0,0 +1,51 @@ +/* + Copyright 2006-2026 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 . +*/ +#ifndef BORDERCELLLABELS_H +#define BORDERCELLLABELS_H + +#include + +/// The labels the border of a folio writes on its rows and columns, shared +/// by BorderTitleBlock::draw() and the cell rulers of DiagramView so the +/// two cannot disagree. Header-only so it can be unit-tested directly -- +/// see tests/qttest/tst_bordercelllabels.cpp. +namespace BorderCellLabels { + + /// @return the label of row \a row, counted from 1: A..Z, then AA, AB... + /// (the sequence BorderTitleBlock::incrementLetters() walks through). + inline QString rowLabel(int row) + { + QString label; + while (row > 0) { + --row; + label.prepend(QChar('A' + row % 26)); + row /= 26; + } + return label; + } + + /// @return the label of column \a column, counted from 1. When + /// \a starts_at_zero (the "border-columns_0" setting) the first column + /// is labelled 0. + inline QString columnLabel(int column, bool starts_at_zero) + { + return QString::number(starts_at_zero ? column - 1 : column); + } +} + +#endif // BORDERCELLLABELS_H diff --git a/sources/bordertitleblock.cpp b/sources/bordertitleblock.cpp index c6913ba46..7878d1a9f 100644 --- a/sources/bordertitleblock.cpp +++ b/sources/bordertitleblock.cpp @@ -17,6 +17,7 @@ */ #include "bordertitleblock.h" +#include "bordercelllabels.h" #include "createdxf.h" #include "diagram.h" #include "diagramposition.h" @@ -534,6 +535,8 @@ void BorderTitleBlock::draw(QPainter *painter) //Draw the nums of columns if (display_border_ && display_columns_) { + const bool columns_start_at_zero = + settings.value("border-columns_0", true).toBool(); for (int i = 1 ; i <= columns_count_ ; ++ i) { QRectF numbered_rectangle = QRectF( diagram_rect_.topLeft().x() @@ -544,23 +547,15 @@ void BorderTitleBlock::draw(QPainter *painter) columns_header_height_ ); painter -> drawRect(numbered_rectangle); - if (settings.value("border-columns_0", true).toBool()){ painter -> drawText(numbered_rectangle, Qt::AlignVCenter | Qt::AlignCenter, - QString("%1").arg(i - 1)); - }else{ - painter -> drawText(numbered_rectangle, - Qt::AlignVCenter - | Qt::AlignCenter, - QString("%1").arg(i)); - } + BorderCellLabels::columnLabel(i, columns_start_at_zero)); } } //Draw the nums of rows if (display_border_ && display_rows_) { - QString row_string("A"); for (int i = 1 ; i <= rows_count_ ; ++ i) { QRectF lettered_rectangle = QRectF( diagram_rect_.topLeft().x(), @@ -576,8 +571,7 @@ void BorderTitleBlock::draw(QPainter *painter) painter -> drawText(lettered_rectangle, Qt::AlignVCenter | Qt::AlignCenter, - row_string); - row_string = incrementLetters(row_string); + BorderCellLabels::rowLabel(i)); } } diff --git a/sources/cellruler.cpp b/sources/cellruler.cpp new file mode 100644 index 000000000..299d70eb8 --- /dev/null +++ b/sources/cellruler.cpp @@ -0,0 +1,170 @@ +/* + Copyright 2006-2026 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 "cellruler.h" + +#include "bordercelllabels.h" +#include "diagram.h" +#include "diagramview.h" + +#include +#include + +/** + @brief CellRuler::CellRuler + @param orientation : Qt::Horizontal for the column numbers along the + top, Qt::Vertical for the row letters along the left + @param view : the view this ruler is placed on +*/ +CellRuler::CellRuler(Qt::Orientation orientation, DiagramView *view) : + QWidget(view), + m_orientation(orientation), + m_view(view) +{ + setAttribute(Qt::WA_OpaquePaintEvent); + hide(); +} + +/** + @brief CellRuler::thickness + @return the height of the top ruler, which is also the width of the + side ruler, so the corner they share is square. Constant whatever the + zoom. +*/ +int CellRuler::thickness() const +{ + const QFontMetrics metrics = fontMetrics(); + return qMax(metrics.height(), metrics.horizontalAdvance(QStringLiteral("WW"))) + 6; +} + +/** + @brief CellRuler::setLeadingSpace + @param space : pixels left empty before the first pixel of the + viewport, along this ruler +*/ +void CellRuler::setLeadingSpace(int space) +{ + m_leading_space = space; + update(); +} + +/** + @brief CellRuler::paintEvent + Draw a cell for each column (or row) of the folio border, at the + position the view shows it. When the cells get too small for their + labels, only one label every 2, 5, 10... cells is written. +*/ +void CellRuler::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + painter.fillRect(rect(), palette().color(QPalette::Button)); + + const bool horizontal = m_orientation == Qt::Horizontal; + const int length = horizontal ? width() : height(); + const int depth = horizontal ? height() : width(); + + //Separate the ruler from the drawing + painter.setPen(palette().color(QPalette::Dark)); + if (horizontal) { + painter.drawLine(0, depth - 1, length, depth - 1); + } else { + painter.drawLine(depth - 1, 0, depth - 1, length); + } + + Diagram *diagram = m_view->diagram(); + if (!diagram) { + return; + } + const BorderTitleBlock &border = diagram->border_and_titleblock; + const QRectF inside = border.insideBorderRect(); + const QTransform transform = m_view->viewportTransform(); + + const int count = horizontal ? border.columnsCount() : border.rowsCount(); + const qreal cell_size = horizontal ? border.columnsWidth() : border.rowsHeight(); + const qreal first = horizontal ? inside.left() : inside.top(); + const qreal scale = horizontal ? transform.m11() : transform.m22(); + const qreal offset = (horizontal ? transform.dx() : transform.dy()) + m_leading_space; + const qreal cell_pixels = cell_size * scale; + if (count < 1 || cell_pixels <= 0) { + return; + } + + const bool columns_start_at_zero = + QSettings().value("border-columns_0", true).toBool(); + auto label = [&](int index) { + return horizontal ? BorderCellLabels::columnLabel(index, columns_start_at_zero) + : BorderCellLabels::rowLabel(index); + }; + + //Room one label needs along the ruler, and the smallest step + //between written labels that gives it that room + const QFontMetrics metrics = fontMetrics(); + const int label_room = horizontal + ? metrics.horizontalAdvance(label(count)) + 6 + : metrics.height() + 2; + int step = 1; + for (int candidate : {1, 2, 5, 10, 20, 50, 100, 200, 500}) { + step = candidate; + if (candidate * cell_pixels >= label_room) { + break; + } + } + + for (int i = 1 ; i <= count ; ++i) { + const qreal start = offset + (first + (i - 1) * cell_size) * scale; + const qreal end = start + cell_pixels; + if (end < m_leading_space || start > length) { + continue; + } + + //Cell edges, drawn only when the cells are wide enough + //for them to read as cells rather than as a hatching + painter.setPen(palette().color(QPalette::Dark)); + if (cell_pixels >= 4) { + if (horizontal) { + painter.drawLine(QPointF(start, 0), QPointF(start, depth - 1)); + if (i == count) painter.drawLine(QPointF(end, 0), QPointF(end, depth - 1)); + } else { + painter.drawLine(QPointF(0, start), QPointF(depth - 1, start)); + if (i == count) painter.drawLine(QPointF(0, end), QPointF(depth - 1, end)); + } + } + + //Written labels are the ones a multiple of step: 0, 5, 10... + //for the columns, A, F, K... for the rows + const int position = (horizontal && !columns_start_at_zero) ? i : i - 1; + if (position % step != 0) { + continue; + } + + painter.setPen(palette().color(QPalette::ButtonText)); + const qreal centre = (start + end) / 2; + const QRectF text_rect = horizontal + ? QRectF(centre - label_room / 2.0, 0, label_room, depth - 1) + : QRectF(0, centre - label_room / 2.0, depth - 1, label_room); + painter.drawText(text_rect, Qt::AlignCenter | Qt::TextDontClip, label(i)); + } + + //Keep the corner empty: the other ruler's labels do not belong there + if (m_leading_space > 0) { + painter.fillRect(horizontal ? QRect(0, 0, m_leading_space, depth - 1) + : QRect(0, 0, depth - 1, m_leading_space), + palette().color(QPalette::Button)); + } +} diff --git a/sources/cellruler.h b/sources/cellruler.h new file mode 100644 index 000000000..619b2d52e --- /dev/null +++ b/sources/cellruler.h @@ -0,0 +1,54 @@ +/* + Copyright 2006-2026 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 . +*/ +#ifndef CELLRULER_H +#define CELLRULER_H + +#include + +class DiagramView; + +/** + @brief The CellRuler class + A bar along the top or the left edge of a DiagramView that repeats the + column numbers or the row letters of the folio border, aligned with the + cells at any zoom, so they stay in sight however far the view is + scrolled. It sits in the view's margins, outside of the scene: printing + and exporting are unaffected. +*/ +class CellRuler : public QWidget +{ + Q_OBJECT + + public: + CellRuler(Qt::Orientation orientation, DiagramView *view); + + int thickness() const; + void setLeadingSpace(int space); + + protected: + void paintEvent(QPaintEvent *event) override; + + private: + Qt::Orientation m_orientation; + DiagramView *m_view; + /// Pixels before the viewport starts, left empty: the corner the + /// side ruler fills when both rulers are shown. + int m_leading_space = 0; +}; + +#endif // CELLRULER_H diff --git a/sources/diagramview.cpp b/sources/diagramview.cpp index bef745a9c..918b6bbbf 100644 --- a/sources/diagramview.cpp +++ b/sources/diagramview.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "diagramview.h" +#include "cellruler.h" #include "lastusedstyle.h" #include "qetproject.h" #include "QPropertyUndoCommand/qpropertyundocommand.h" @@ -108,6 +109,13 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) : connect(&(m_diagram -> border_and_titleblock), &BorderTitleBlock::informationChanged, this, &DiagramView::updateWindowTitle); connect(diagram, &Diagram::findElementRequired, this, &DiagramView::findElementRequired); + m_top_ruler = new CellRuler(Qt::Horizontal, this); + m_side_ruler = new CellRuler(Qt::Vertical, this); + m_cell_rulers_shown = QSettings().value("diagrameditor/cell_rulers", false).toBool(); + connect(&m_diagram->border_and_titleblock, &BorderTitleBlock::borderChanged, this, &DiagramView::updateCellRulers); + connect(&m_diagram->border_and_titleblock, &BorderTitleBlock::displayChanged, this, &DiagramView::updateCellRulers); + updateCellRulers(); + QShortcut *edit_conductor_color_shortcut = new QShortcut(QKeySequence(Qt::Key_F2), this); connect(edit_conductor_color_shortcut, &QShortcut::activated, [this]() { @@ -1188,6 +1196,82 @@ void DiagramView::paintingInverted(bool inverted) m_diagram->setInvertedLightness(inverted); } +/** + @brief DiagramView::setCellRulersShown + Show or hide the rulers that keep the column numbers and the row + letters of the folio border in sight along the edges of this view. + @param shown +*/ +void DiagramView::setCellRulersShown(bool shown) +{ + m_cell_rulers_shown = shown; + updateCellRulers(); +} + +/** + @brief DiagramView::updateCellRulers + Show each ruler when the rulers are wanted and the folio shows the + matching header, and give it room in the margins of the view. The + part of the folio in sight stays in sight when the viewport resizes. +*/ +void DiagramView::updateCellRulers() +{ + const BorderTitleBlock &border = m_diagram->border_and_titleblock; + const bool top = m_cell_rulers_shown + && border.borderIsDisplayed() && border.columnsAreDisplayed(); + const bool side = m_cell_rulers_shown + && border.borderIsDisplayed() && border.rowsAreDisplayed(); + const int thickness = m_top_ruler->thickness(); + + m_top_ruler->setVisible(top); + m_side_ruler->setVisible(side); + m_side_ruler->setLeadingSpace(top ? thickness : 0); + + const QMargins margins(side ? thickness : 0, top ? thickness : 0, 0, 0); + if (margins != viewportMargins()) { + const QPointF centre = mapToScene(viewport()->rect().center()); + setViewportMargins(margins); + centerOn(centre); + } + placeCellRulers(); + m_top_ruler->update(); + m_side_ruler->update(); +} + +/** + @brief DiagramView::placeCellRulers + Lay the rulers along the top and the left edges of the viewport, the + side ruler covering the corner too when both are shown. +*/ +void DiagramView::placeCellRulers() +{ + if (!m_top_ruler) { + return; + } + const QRect viewport_rect = viewport()->geometry(); + const int thickness = m_top_ruler->thickness(); + const int corner = m_top_ruler->isHidden() ? 0 : thickness; + m_top_ruler->setGeometry(viewport_rect.left(), viewport_rect.top() - thickness, + viewport_rect.width(), thickness); + m_side_ruler->setGeometry(viewport_rect.left() - thickness, viewport_rect.top() - corner, + thickness, viewport_rect.height() + corner); +} + +/** + @brief DiagramView::viewportEvent + Keep the rulers along the viewport when it resizes, which it also does + without the view resizing, when the scroll bars come and go. + @param event + @return what QGraphicsView::viewportEvent() returns +*/ +bool DiagramView::viewportEvent(QEvent *event) +{ + if (event->type() == QEvent::Resize) { + placeCellRulers(); + } + return PaletteGraphicsView::viewportEvent(event); +} + /** @brief DiagramView::paintEvent Reimplemented from QGraphicsView @@ -1197,6 +1281,13 @@ void DiagramView::paintEvent(QPaintEvent *event) { PaletteGraphicsView::paintEvent(event); + //Scrolling and zooming both repaint the viewport: follow them + if (viewportTransform() != m_rulers_transform) { + m_rulers_transform = viewportTransform(); + m_top_ruler->update(); + m_side_ruler->update(); + } + if (m_free_rubberbanding && m_free_rubberband.count() >= 3) { QPainter painter(viewport()); diff --git a/sources/diagramview.h b/sources/diagramview.h index a03b49ebd..9550e4898 100644 --- a/sources/diagramview.h +++ b/sources/diagramview.h @@ -24,6 +24,7 @@ #include #include "palettegraphicsview.h" +class CellRuler; class Conductor; class Diagram; class QETDiagramEditor; @@ -62,6 +63,11 @@ class DiagramView : public PaletteGraphicsView QList m_separators; QPolygonF m_free_rubberband; bool m_free_rubberbanding = false; + CellRuler *m_top_ruler = nullptr; + CellRuler *m_side_ruler = nullptr; + bool m_cell_rulers_shown = false; + /// Last viewport transform the rulers were painted for + QTransform m_rulers_transform; public: @@ -78,6 +84,7 @@ class DiagramView : public PaletteGraphicsView /// cursor query (QCursor::pos()/setPos() are silently ignored by /// several window managers and compositors, Wayland included). QPoint lastMousePos() const { return m_last_mouse_pos; } + void setCellRulersShown(bool shown); protected: void mouseDoubleClickEvent(QMouseEvent *) override; @@ -91,6 +98,7 @@ class DiagramView : public PaletteGraphicsView ///Set for one call only, by the Escape handler, to let focus leave the view. bool m_releasing_focus = false; void paintEvent(QPaintEvent *event) override; + bool viewportEvent(QEvent *event) override; void paintingInverted(bool inverted) override; void mousePressEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; @@ -113,6 +121,8 @@ class DiagramView : public PaletteGraphicsView QRectF viewedSceneRect() const; bool mustIntegrateTitleBlockTemplate(const TitleBlockTemplateLocation &) const; bool gestures() const; + void updateCellRulers(); + void placeCellRulers(); /// Lowest and highest allowed value of the view transform scale (m11). /// Prevents wheel-zoom from driving the transform to overflow, which diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 7c5f2ddb2..c97613ba7 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -522,6 +522,18 @@ void QETDiagramEditor::setUpActions() } }); + //Keep the column numbers and row letters of the folio in sight + m_cell_rulers = new QAction(tr("Garder les en-têtes visibles"), this); + m_cell_rulers->setStatusTip(tr("Garde les numéros de colonne et les lettres de ligne du folio visibles au bord de la vue")); + m_cell_rulers->setCheckable(true); + m_cell_rulers->setChecked(settings.value("diagrameditor/cell_rulers", false).toBool()); + connect(m_cell_rulers, &QAction::triggered, [this](bool checked) { + QSettings().setValue("diagrameditor/cell_rulers", checked); + foreach (ProjectView *prjv, this->openedProjects()) + foreach (DiagramView *dv, prjv->diagram_views()) + dv->setCellRulersShown(checked); + }); + //Edit current diagram properties m_edit_diagram_properties = new QAction(QET::Icons::DialogInformation, tr("Propriétés du folio"), this); ShortcutManager::instance().registerAction(m_edit_diagram_properties, "diagrameditor.edit_diagram_properties", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_L); @@ -1147,6 +1159,7 @@ void QETDiagramEditor::setUpMenu() menu_affichage -> addAction(m_draw_grid); menu_affichage -> addMenu(m_text_grid_menu); menu_affichage -> addAction(m_draw_guides); + menu_affichage -> addAction(m_cell_rulers); menu_affichage -> addMenu(m_background_color_button->menu()); menu_affichage -> addSeparator(); menu_affichage -> addActions(m_zoom_actions_group.actions()); @@ -1978,6 +1991,7 @@ void QETDiagramEditor::slot_updateActions() m_background_color_button-> setEnabled(opened_diagram); m_draw_grid-> setEnabled(opened_diagram); m_draw_guides-> setEnabled(opened_diagram); + m_cell_rulers-> setEnabled(opened_diagram); //Project menu m_project_edit_properties -> setEnabled(opened_project); diff --git a/sources/qetdiagrameditor.h b/sources/qetdiagrameditor.h index fdcc6a88c..c0bece505 100644 --- a/sources/qetdiagrameditor.h +++ b/sources/qetdiagrameditor.h @@ -212,6 +212,7 @@ class QETDiagramEditor : public QETMainWindow *m_auto_break_conductor, ///< Enable/Disable the use of auto break conductor *m_draw_grid, ///< Switch the background grid display or not *m_draw_guides = nullptr, ///< Switch the custom guides display or not + *m_cell_rulers = nullptr, ///< Keep the folio column/row headers in sight or not *m_project_edit_properties, ///< Edit the properties of the current project. *m_project_add_diagram, ///< Add a diagram to the current project. *m_remove_diagram_from_project, ///< Delete a diagram from the current project diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 63766e0a1..3296bc016 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -84,6 +84,12 @@ add_test(NAME tst_diagramsortkeys COMMAND tst_diagramsortkeys) target_include_directories(tst_diagramsortkeys PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_diagramsortkeys PRIVATE Qt::Test) +# bordercelllabels.h is header-only too. +add_executable(tst_bordercelllabels tst_bordercelllabels.cpp) +add_test(NAME tst_bordercelllabels COMMAND tst_bordercelllabels) +target_include_directories(tst_bordercelllabels PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_bordercelllabels PRIVATE Qt::Test) + # contactusage.h is a header-only helper holding the contact counting # rules, so this test builds independently of the rest of the QET sources. add_executable(tst_contactusage tst_contactusage.cpp) diff --git a/tests/qttest/tst_bordercelllabels.cpp b/tests/qttest/tst_bordercelllabels.cpp new file mode 100644 index 000000000..7c4ca074b --- /dev/null +++ b/tests/qttest/tst_bordercelllabels.cpp @@ -0,0 +1,55 @@ +#include + +#include "bordercelllabels.h" + +class tst_bordercelllabels : public QObject +{ + Q_OBJECT + + // A copy of BorderTitleBlock::incrementLetters(), the walk the folio + // border used to draw its row labels with, as the reference rowLabel() + // must reproduce. + static QString incrementLetters(const QString &string) + { + if (string.isEmpty()) + return QStringLiteral("A"); + const QString first_digits(string.left(string.length() - 1)); + const QChar last_digit(string.at(string.length() - 1)); + if (last_digit != QLatin1Char('Z')) + return first_digits + QChar(last_digit.unicode() + 1); + return incrementLetters(first_digits) + QLatin1Char('A'); + } + +private slots: + void rowLabelsFollowTheBorderSequence() + { + QString expected(QStringLiteral("A")); + for (int row = 1; row <= 1000; ++row) { + QCOMPARE(BorderCellLabels::rowLabel(row), expected); + expected = incrementLetters(expected); + } + } + + void rowLabelSamples() + { + QCOMPARE(BorderCellLabels::rowLabel(1), QStringLiteral("A")); + QCOMPARE(BorderCellLabels::rowLabel(26), QStringLiteral("Z")); + QCOMPARE(BorderCellLabels::rowLabel(27), QStringLiteral("AA")); + QCOMPARE(BorderCellLabels::rowLabel(52), QStringLiteral("AZ")); + QCOMPARE(BorderCellLabels::rowLabel(53), QStringLiteral("BA")); + QCOMPARE(BorderCellLabels::rowLabel(702), QStringLiteral("ZZ")); + QCOMPARE(BorderCellLabels::rowLabel(703), QStringLiteral("AAA")); + } + + void columnLabels() + { + QCOMPARE(BorderCellLabels::columnLabel(1, true), QStringLiteral("0")); + QCOMPARE(BorderCellLabels::columnLabel(1, false), QStringLiteral("1")); + QCOMPARE(BorderCellLabels::columnLabel(17, true), QStringLiteral("16")); + QCOMPARE(BorderCellLabels::columnLabel(17, false), QStringLiteral("17")); + } +}; + +QTEST_APPLESS_MAIN(tst_bordercelllabels) + +#include "tst_bordercelllabels.moc"