// SPDX-License-Identifier: GPL-2.0-or-later
/*
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 "qetapptest.h"
#include "TerminalStrip/physicalterminal.h"
#include "TerminalStrip/realterminal.h"
#include "TerminalStrip/terminalstrip.h"
#include "TerminalStrip/ui/terminalstripeditor.h"
#include "TerminalStrip/ui/terminalstripmodel.h"
#include "cable/cable.h"
#include "qetgraphicsitem/conductor.h"
#include "qetgraphicsitem/element.h"
#include "qetgraphicsitem/terminal.h"
#include "qetinformation.h"
#include "qetproject.h"
#include
#include
#include
#include
#include
#include
#include
/**
The Cable and Color / wire number columns of the terminal strip editor
are worked out from the cables of the project: a terminal with a cable
wired to it shows that cable and its core, the cell is read only, and
nothing is written into the element -- so a cable renamed or removed
cannot leave a stale value behind. A terminal no cable is wired to
keeps the value typed in by hand, cell and all.
The Target column follows the same rule: it names where the cable
lands past this strip (Installation, Location and Label), the very
three fields the strip drawing writes in its box. Worked out from a
cable it is read only -- only the drawing decides it -- and with no
cable landing anywhere it is there to be typed in, just like the
Cable cell, the text then stored in the element and kept until a
cable works a target out again. A text typed in is written one line
per field, Installation, Location, Label, the way the column shows
them: only a line break breaks it into fields, so a "/" written
inside a field (a name like "ASP/01") is part of that field and the
box draws one line containing it.
fixtures/every_feature.qet has the strip X1 whose two terminals are
wired to conductors on the folio, and no cable at all.
*/
class tst_terminalcableauto : public QObject
{
Q_OBJECT
// The wired terminal of strip X1: its element carries a conductor.
static QSharedPointer wiredTerminal(QETProject &project)
{
const QVector strips = project.terminalStrip();
if (strips.isEmpty()) return {};
for (const QSharedPointer &real_t
: strips.first()->realTerminals())
{
Element *element = real_t ? real_t->element() : nullptr;
if (!element) continue;
for (Terminal *terminal : element->terminals())
if (terminal && !terminal->conductors().isEmpty())
return real_t;
}
return {};
}
static QList conductorsOf(const QSharedPointer &real_t)
{
QList conductors;
Element *element = real_t ? real_t->element() : nullptr;
if (!element) return conductors;
for (Terminal *terminal : element->terminals()) {
if (!terminal) continue;
for (Conductor *conductor : terminal->conductors()) {
if (conductor && !conductors.contains(conductor))
conductors.append(conductor);
}
}
return conductors;
}
static Conductor *conductorOf(const QSharedPointer &real_t)
{
const QList conductors = conductorsOf(real_t);
return conductors.isEmpty() ? nullptr : conductors.first();
}
// The wire which leaves the strip: one of its ends belongs to a piece
// outside the strip, while the wire joining two terminals of this very
// strip never leaves it -- and never has a target to name.
static Conductor *outgoingConductor(const QSharedPointer &real_t)
{
TerminalStrip *strip = real_t ? real_t->parentStrip() : nullptr;
if (!strip) return nullptr;
QSet own;
for (const QSharedPointer &physical
: strip->physicalTerminal())
{
if (!physical) continue;
for (const QSharedPointer &real : physical->realTerminals())
if (real && real->element())
own.insert(real->element()->uuid());
}
for (Conductor *conductor : conductorsOf(real_t))
{
if (!conductor) continue;
Element *ends[2] = {
conductor->terminal1 ? conductor->terminal1->parentElement() : nullptr,
conductor->terminal2 ? conductor->terminal2->parentElement() : nullptr };
for (Element *end : ends)
if (end && !own.contains(end->uuid()))
return conductor;
}
return nullptr;
}
// The cable the create dialog hands over: owned by the project, four
// cores, one core wired to the terminal's conductor (or to @p on).
static Cable *wireCable(QETProject &project,
const QSharedPointer &real_t,
int core = 1,
const QString &designation = QStringLiteral("15W1"),
Conductor *on = nullptr)
{
if (!on) on = conductorOf(real_t);
Cable *cable = project.newCable();
cable->setDesignation(designation);
cable->setCoreCount(4);
cable->setCoreColors({QStringLiteral("bn"), QStringLiteral("bl"),
QStringLiteral("gn/ye"), QStringLiteral("gy")});
CableCore assignment;
assignment.core = core;
assignment.conductor = on->uuid();
cable->setCore(assignment);
return cable;
}
private slots:
void wiredCableIsWorkedOutAndReadsOnly()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QVERIFY2(!fixture.isEmpty(), "fixture project not found");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY2(!real_t.isNull(), "the fixture has a wired terminal");
Element *element = real_t->element();
//Without a cable, the cells show nothing and may be typed in
QVERIFY(!real_t->cableIsAutomatic());
TerminalStripModel model_before(project.terminalStrip().first());
QVERIFY(model_before.rowCount() > 0);
QVERIFY(model_before.flags(model_before.index(0, TerminalStripModel::Cable))
& Qt::ItemIsEditable);
wireCable(project, real_t);
//The terminal itself reads the cable and its core
QCOMPARE(real_t->cable(), QStringLiteral("15W1"));
QVERIFY(real_t->cableIsAutomatic());
QCOMPARE(real_t->cableWire(), QStringLiteral("bl"));
//The automatic value is never written into the element: nothing
//stays behind once the cable is renamed or removed
QVERIFY(element);
QVERIFY(element->elementData().m_informations
.value(RealTerminal::cableInfoKey()).toString().isEmpty());
//The model shows both cells and keeps them out of the edits
TerminalStripModel model(project.terminalStrip().first());
QCOMPARE(model.rowCount(), model_before.rowCount());
const QModelIndex cable_cell = model.index(0, TerminalStripModel::Cable);
const QModelIndex wire_cell = model.index(0, TerminalStripModel::CableWire);
QCOMPARE(model.data(cable_cell).toString(), QStringLiteral("15W1"));
QCOMPARE(model.data(wire_cell).toString(), QStringLiteral("bl"));
QVERIFY(!(model.flags(cable_cell) & Qt::ItemIsEditable));
QVERIFY(!(model.flags(wire_cell) & Qt::ItemIsEditable));
//The other cells go on being editable
QVERIFY(model.flags(model.index(0, TerminalStripModel::Label))
& Qt::ItemIsEditable);
}
void coreWithoutColorIsShownAsItsNumber()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY(!real_t.isNull());
Cable *cable = project.newCable();
cable->setDesignation(QStringLiteral("15W1"));
cable->setCoreCount(4);
//A type file which gives no colours: the number of the core is shown
CableCore assignment;
assignment.core = 1;
assignment.conductor = conductorOf(real_t)->uuid();
cable->setCore(assignment);
QCOMPARE(real_t->cable(), QStringLiteral("15W1"));
QCOMPARE(real_t->cableWire(), QStringLiteral("2"));
QVERIFY(real_t->cableIsAutomatic());
}
void typedValueStaysWhenNoCableIsThere()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY(!real_t.isNull());
Element *element = real_t->element();
QVERIFY(element);
//A cable designation typed in by hand in the editor
ElementData typed = element->elementData();
typed.m_informations.addValue(RealTerminal::cableInfoKey(),
QStringLiteral("K9"));
element->setElementData(typed);
QCOMPARE(real_t->cable(), QStringLiteral("K9"));
QVERIFY(!real_t->cableIsAutomatic());
//Wired to a cable: the cable is shown, the typed value is set aside
Cable *cable = wireCable(project, real_t);
QCOMPARE(real_t->cable(), QStringLiteral("15W1"));
QVERIFY(real_t->cableIsAutomatic());
QCOMPARE(real_t->cableWire(), QStringLiteral("bl"));
//Cable removed: the typed value is back, no stale 15W1 anywhere
cable->removeCore(1);
QCOMPARE(real_t->cable(), QStringLiteral("K9"));
QVERIFY(!real_t->cableIsAutomatic());
QVERIFY(element->elementData().m_informations
.value(RealTerminal::cableInfoKey())
.toString() == QStringLiteral("K9"));
TerminalStripModel model(project.terminalStrip().first());
QVERIFY(model.flags(model.index(0, TerminalStripModel::Cable))
& Qt::ItemIsEditable);
}
// The window stays open while the user wires the drawing: the cells
// follow a cable wired, renamed or taken away without any reload by
// hand.
void openEditorFollowsCableChanges()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY(!real_t.isNull());
TerminalStripEditor editor(&project);
editor.setCurrentStrip(project.terminalStrip().first());
const QAbstractItemView *table = editor.findChild(
QStringLiteral("m_table_widget"));
QVERIFY2(table && table->model(), "the editor has its table");
const QAbstractItemModel *model = table->model();
const QModelIndex cable_cell = model->index(0, TerminalStripModel::Cable);
QVERIFY(model->data(cable_cell).toString().isEmpty());
//A cable wired to the terminal fills the cell in
Cable *cable = wireCable(project, real_t);
QCOMPARE(model->data(cable_cell).toString(), QStringLiteral("15W1"));
//Renaming it follows at once, the drawing is not stale behind
cable->setDesignation(QStringLiteral("16W1"));
QCOMPARE(model->data(cable_cell).toString(), QStringLiteral("16W1"));
//Taking it away empties the cell: nothing stale stays behind
QVERIFY(project.removeCable(cable));
QVERIFY(model->data(cable_cell).toString().isEmpty());
}
// A terminal wired to two cables shows them both, comma separated.
void twoCablesOnOneTerminalAreBothShown()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY(!real_t.isNull());
const QList conductors = conductorsOf(real_t);
QVERIFY2(conductors.size() >= 2,
"the fixture terminal carries two wires");
wireCable(project, real_t, 1, QStringLiteral("15W1"), conductors.at(0));
wireCable(project, real_t, 1, QStringLiteral("16W1"), conductors.at(1));
QCOMPARE(real_t->cable(), QStringLiteral("15W1, 16W1"));
QCOMPARE(real_t->cableWire(), QStringLiteral("bl, bl"));
QVERIFY(real_t->cableIsAutomatic());
}
// The Target column names where the cable lands, past this strip: the
// three fields the strip drawing writes in the box under the cable,
// joined in one line. Worked out from a cable it is read only, there
// is nothing to name without one, and the cell empties again once the
// cable goes.
void targetColumnNamesWhereTheCableLands()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY(!real_t.isNull());
Conductor *outgoing = outgoingConductor(real_t);
QVERIFY2(outgoing, "the fixture terminal has a wire leaving the strip");
Element *target = (outgoing->terminal1
&& outgoing->terminal1->parentElement() == real_t->element())
? (outgoing->terminal2 ? outgoing->terminal2->parentElement() : nullptr)
: (outgoing->terminal1 ? outgoing->terminal1->parentElement() : nullptr);
QVERIFY(target);
//The piece the cable lands on carries an Installation, a Location
//and a Label, the way an element of a real drawing does
ElementData named = target->elementData();
named.m_informations.addValue(QETInformation::ELMT_PLANT,
QStringLiteral("Plant 7"));
named.m_informations.addValue(QETInformation::ELMT_LOCATION,
QStringLiteral("Cabinet 7"));
named.m_informations.addValue(QETInformation::ELMT_LABEL,
QStringLiteral("-S7"));
target->setElementData(named);
//No cable yet : nothing to name, so the cell is there to be
//filled in by hand -- the very rule of the Cable cell
TerminalStripModel model_before(project.terminalStrip().first());
const QModelIndex before_cell = model_before.index(
0, TerminalStripModel::CableTarget);
QVERIFY(model_before.data(before_cell).toString().isEmpty());
QVERIFY(model_before.flags(before_cell) & Qt::ItemIsEditable);
Cable *cable = wireCable(project, real_t, 1, QStringLiteral("15W1"),
outgoing);
//Row zero is the terminal the cable is wired to
TerminalStripModel model(project.terminalStrip().first());
QCOMPARE(model.data(model.index(0, TerminalStripModel::Cable))
.toString(), QStringLiteral("15W1"));
//The three fields, one on its own line as the box draws them
const QModelIndex target_cell = model.index(0,
TerminalStripModel::CableTarget);
QCOMPARE(model.data(target_cell).toString(),
QStringLiteral("Plant 7\nCabinet 7\n-S7"));
QVERIFY(!(model.flags(target_cell) & Qt::ItemIsEditable));
QCOMPARE(model.headerData(TerminalStripModel::CableTarget,
Qt::Horizontal).toString(),
QStringLiteral("Target"));
//The cable taken away : the cell is empty again, nothing stale
//behind, and it takes an edit once more
QVERIFY(cable);
cable->removeCore(1);
TerminalStripModel reloaded(project.terminalStrip().first());
const QModelIndex reloaded_cell = reloaded.index(
0, TerminalStripModel::CableTarget);
QVERIFY(reloaded.data(reloaded_cell).toString().isEmpty());
QVERIFY(reloaded.flags(reloaded_cell) & Qt::ItemIsEditable);
}
// A target the drawing cannot work out is typed in by hand, exactly
// like a cable designation: the cell takes the edit, Apply stores the
// text in the element, a fresh model shows it again. A cable landing
// on a named piece then gives the read only target of the drawing and
// the text typed in by hand waits, untouched, until that cable goes.
void targetTypedByHandIsStoredAndKept()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY(!real_t.isNull());
Element *element = real_t->element();
QVERIFY(element);
Conductor *outgoing = outgoingConductor(real_t);
QVERIFY2(outgoing, "the fixture terminal has a wire leaving the strip");
//The editor takes a target typed in by hand : no cable is wired,
//so there is nothing the drawing could name and the cell is open
TerminalStripEditor editor(&project);
editor.setCurrentStrip(project.terminalStrip().first());
QAbstractItemView *table = editor.findChild(
QStringLiteral("m_table_widget"));
QVERIFY2(table && table->model(), "the editor has its table");
QAbstractItemModel *model = table->model();
const QModelIndex cell = model->index(0, TerminalStripModel::CableTarget);
QVERIFY(model->data(cell).toString().isEmpty());
QVERIFY(model->flags(cell) & Qt::ItemIsEditable);
const QString typed{QStringLiteral("Boiler room\nCabinet 3\n-K3")};
QVERIFY(model->setData(cell, typed, Qt::EditRole));
QCOMPARE(model->data(cell, Qt::EditRole).toString(), typed);
QCOMPARE(model->data(cell).toString(), typed);
//Apply writes it in the element, and nowhere else
editor.apply();
QCOMPARE(element->elementData().m_informations
.value(RealTerminal::cableTargetInfoKey()).toString(), typed);
//Three lines, each field on its own: the box draws them one under
//the other, without any separator
QCOMPARE(real_t->cableTarget(),
(QVector{QStringLiteral("Boiler room"),
QStringLiteral("Cabinet 3"),
QStringLiteral("-K3")}));
QVERIFY(!real_t->cableTargetIsAutomatic());
//A fresh model shows the text and still lets it be edited
TerminalStripModel model_after(project.terminalStrip().first());
const QModelIndex after_cell = model_after.index(
0, TerminalStripModel::CableTarget);
QCOMPARE(model_after.data(after_cell).toString(), typed);
QVERIFY(model_after.flags(after_cell) & Qt::ItemIsEditable);
//The piece the cable lands on is named now : once the cable is
//wired the drawing gives the target of its own, and that one is
//read only while the text typed in by hand stays in the element
Element *target = (outgoing->terminal1
&& outgoing->terminal1->parentElement() == real_t->element())
? (outgoing->terminal2 ? outgoing->terminal2->parentElement() : nullptr)
: (outgoing->terminal1 ? outgoing->terminal1->parentElement() : nullptr);
QVERIFY(target);
ElementData named = target->elementData();
named.m_informations.addValue(QETInformation::ELMT_PLANT,
QStringLiteral("Plant 9"));
named.m_informations.addValue(QETInformation::ELMT_LOCATION,
QStringLiteral("Cabinet 9"));
named.m_informations.addValue(QETInformation::ELMT_LABEL,
QStringLiteral("-S9"));
target->setElementData(named);
Cable *cable = wireCable(project, real_t, 1, QStringLiteral("15W1"),
outgoing);
QVERIFY(cable);
QVERIFY(real_t->cableTargetIsAutomatic());
QCOMPARE(real_t->cableTarget(),
(QVector{QStringLiteral("Plant 9"),
QStringLiteral("Cabinet 9"),
QStringLiteral("-S9")}));
QCOMPARE(element->elementData().m_informations
.value(RealTerminal::cableTargetInfoKey()).toString(), typed);
TerminalStripModel model_cable(project.terminalStrip().first());
const QModelIndex cable_cell = model_cable.index(
0, TerminalStripModel::CableTarget);
QCOMPARE(model_cable.data(cable_cell).toString(),
QStringLiteral("Plant 9\nCabinet 9\n-S9"));
QVERIFY(!(model_cable.flags(cable_cell) & Qt::ItemIsEditable));
//And the automatic target was never written in the element
QCOMPARE(element->elementData().m_informations
.value(RealTerminal::cableTargetInfoKey()).toString(), typed);
//The cable taken away : the text typed in by hand is back, cell
//and all, no stale Plant 9 anywhere
cable->removeCore(1);
QVERIFY(!real_t->cableTargetIsAutomatic());
QCOMPARE(real_t->cableTarget(),
(QVector{QStringLiteral("Boiler room"),
QStringLiteral("Cabinet 3"),
QStringLiteral("-K3")}));
TerminalStripModel model_back(project.terminalStrip().first());
const QModelIndex back_cell = model_back.index(
0, TerminalStripModel::CableTarget);
QCOMPARE(model_back.data(back_cell).toString(), typed);
QVERIFY(model_back.flags(back_cell) & Qt::ItemIsEditable);
}
// A text typed in by hand is written one line per field, and only a
// line break breaks it into fields: the box under the cable draws
// one line per field, exactly like a target worked out from the
// project, while a "/" written inside a field is part of it and
// gives one single line holding it.
void typedTargetGivesOneLinePerField()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
const auto real_t = wiredTerminal(project);
QVERIFY(!real_t.isNull());
Element *element = real_t->element();
QVERIFY(element);
//One field only: one single line in the box
ElementData typed = element->elementData();
typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(),
QStringLiteral("Boiler room"));
element->setElementData(typed);
QCOMPARE(real_t->cableTarget(),
QVector{QStringLiteral("Boiler room")});
//Three fields, written one per line: three lines, and no
//separator anywhere in them
typed = element->elementData();
typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(),
QStringLiteral("Plant 9\nCabinet 9\n-S9"));
element->setElementData(typed);
QCOMPARE(real_t->cableTarget(),
(QVector{QStringLiteral("Plant 9"),
QStringLiteral("Cabinet 9"),
QStringLiteral("-S9")}));
//A typed text is never a target of the drawing
QVERIFY(!real_t->cableTargetIsAutomatic());
//The column shows the text back the way it was written
TerminalStripModel model(project.terminalStrip().first());
QCOMPARE(model.data(model.index(0, TerminalStripModel::CableTarget))
.toString(), QStringLiteral("Plant 9\nCabinet 9\n-S9"));
//A slash inside a field, and one typed between two words of a
//field kept in one line: both stay part of the field, the cell
//shows them untouched and the box holds one single line each
typed = element->elementData();
typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(),
QStringLiteral("Betrieb 1/2"));
element->setElementData(typed);
QCOMPARE(real_t->cableTarget(),
QVector{QStringLiteral("Betrieb 1/2")});
typed = element->elementData();
typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(),
QStringLiteral("Betrieb 1/2 / Keller"));
element->setElementData(typed);
QCOMPARE(real_t->cableTarget(),
QVector{QStringLiteral("Betrieb 1/2 / Keller")});
TerminalStripModel slashed(project.terminalStrip().first());
QCOMPARE(slashed.data(slashed.index(0, TerminalStripModel::CableTarget))
.toString(), QStringLiteral("Betrieb 1/2 / Keller"));
//The line breaks written by hand are the fields again, whatever
//the fields themselves contain
typed = element->elementData();
typed.m_informations.addValue(RealTerminal::cableTargetInfoKey(),
QStringLiteral("Betrieb 1/2\nKeller\n-K4"));
element->setElementData(typed);
QCOMPARE(real_t->cableTarget(),
(QVector{QStringLiteral("Betrieb 1/2"),
QStringLiteral("Keller"),
QStringLiteral("-K4")}));
}
// The Target cell is edited line by line: its editor holds one line
// per field and hands the text back exactly as the cell holds it.
// Return writes a new line (the cell is multi line), Ctrl+Return
// gives the cell back without waiting for the focus to go somewhere
// else, and the row of the table is as tall as the lines the cell
// shows, so none of them is cut off.
void targetCellIsEditedLineByLine()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
TerminalStripEditor editor(&project);
editor.setCurrentStrip(project.terminalStrip().first());
QTableView *table = editor.findChild(
QStringLiteral("m_table_widget"));
QVERIFY2(table && table->model(), "the editor has its table");
QAbstractItemModel *model = table->model();
QVERIFY(model->rowCount() > 1);
const QModelIndex cell = model->index(0, TerminalStripModel::CableTarget);
QVERIFY(model->flags(cell) & Qt::ItemIsEditable);
const QString typed{QStringLiteral("Betrieb 1/2\nKeller\n-K4")};
QVERIFY(model->setData(cell, typed, Qt::EditRole));
QCOMPARE(model->data(cell).toString(), typed);
//The editor of the table is the one taking a cell of several
//lines, and one of its own does the very same thing for the
//keys, without a view in between
QVERIFY(qobject_cast(
table->itemDelegate()));
TerminalStripModelDelegate delegate;
QStyleOptionViewItem option;
option.rect = QRect(0, 0, qMax(
50, table->columnWidth(TerminalStripModel::CableTarget)), 400);
option.font = table->font();
option.fontMetrics = QFontMetrics(option.font);
QWidget *cell_editor = delegate.createEditor(table, option, cell);
QVERIFY(cell_editor);
auto *text_edit = qobject_cast(cell_editor);
QVERIFY2(text_edit, "the Target cell takes a multi line editor");
QCOMPARE(text_edit->objectName(),
QStringLiteral("terminal_cable_target"));
//The editor gives the lines back exactly as the cell holds
//them: nothing joined, nothing split again
delegate.setEditorData(cell_editor, cell);
QCOMPARE(text_edit->toPlainText(), typed);
delegate.setModelData(cell_editor, model, cell);
QCOMPARE(model->data(cell).toString(), typed);
//Return writes a new line in the cell, and hands nothing over
//yet: Ctrl+Return is the key giving the cell back
QSignalSpy committed(&delegate, &TerminalStripModelDelegate::commitData);
QSignalSpy closed(&delegate, &TerminalStripModelDelegate::closeEditor);
text_edit->moveCursor(QTextCursor::End);
QKeyEvent return_key{QEvent::KeyPress, Qt::Key_Return,
Qt::NoModifier};
QCoreApplication::sendEvent(cell_editor, &return_key);
QCOMPARE(text_edit->toPlainText(), typed + QLatin1Char('\n'));
QCOMPARE(committed.count(), 0);
QCOMPARE(closed.count(), 0);
QKeyEvent ctrl_return_key{QEvent::KeyPress, Qt::Key_Return,
Qt::ControlModifier};
QCoreApplication::sendEvent(cell_editor, &ctrl_return_key);
QCOMPARE(committed.count(), 1);
QCOMPARE(closed.count(), 1);
delete cell_editor;
}
// The row of the table follows the lines its Target cell holds: the
// cell is never cut off, however many lines it takes, the row grows
// to hold them all and shrinks back afterwards, while the rows
// around keep the height they have always had.
void targetRowGrowsWithTheLinesOfItsCell()
{
const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet");
QETProject project(fixture);
QCOMPARE(project.state(), QETProject::Ok);
TerminalStripEditor editor(&project);
editor.setCurrentStrip(project.terminalStrip().first());
QTableView *table = editor.findChild(
QStringLiteral("m_table_widget"));
QVERIFY2(table && table->model(), "the editor has its table");
QAbstractItemModel *model = table->model();
QVERIFY(model->rowCount() > 1);
const QModelIndex cell = model->index(0, TerminalStripModel::CableTarget);
QVERIFY(model->flags(cell) & Qt::ItemIsEditable);
QVERIFY2(model->data(cell).toString().isEmpty(),
"no target to start with, the row is the usual one");
const QModelIndex other_cell = model->index(
1, TerminalStripModel::CableTarget);
const int base_row = table->rowHeight(cell.row());
const int base_other_row = table->rowHeight(other_cell.row());
QVERIFY(base_row > 0);
//Three lines in the cell: the row holds them all, at the
//font of the table -- the row was already taller than that
//for what else the terminal draws, so it may stay as it is
const QString typed{QStringLiteral("Betrieb 1/2\nKeller\n-K4")};
QVERIFY(model->setData(cell, typed, Qt::EditRole));
QCOMPARE(model->data(cell).toString(), typed);
const auto heightOfLines = [table](int lines_) {
return lines_ * table->fontMetrics().height();
};
const int three_line_row = table->rowHeight(cell.row());
QVERIFY2(three_line_row >= qMax(base_row, heightOfLines(3)),
"the row must be as tall as both the lines the cell "
"shows and what it held before, or something is cut off");
QVERIFY2(table->rowHeight(other_cell.row()) >= base_other_row,
"the rows around are not pushed down");
//From here on the rows have been worked out once: what the
//other rows hold is the height they keep
const int other_row = table->rowHeight(other_cell.row());
//Many more lines than anything else in the row: the row grows
//to hold them, and the rows around stay where they are
const QString many{QStringLiteral(
"Plant 6\nCabinet 6\n-K6\nZone 6\nLevel 6\nEnd 6")};
QVERIFY(model->setData(cell, many, Qt::EditRole));
const int six_line_row = table->rowHeight(cell.row());
QVERIFY2(six_line_row >= heightOfLines(6),
"the row must be as tall as the lines the cell shows, "
"or the fields below the first one are cut off");
QVERIFY2(six_line_row > qMax(base_row, three_line_row),
"a cell which took six lines must take a taller row "
"than the one it had before");
QVERIFY2(table->rowHeight(other_cell.row()) == other_row,
"the rows around keep their own height");
//Back to one single line: the row follows again
QVERIFY(model->setData(cell, QStringLiteral("Keller"), Qt::EditRole));
QVERIFY2(table->rowHeight(cell.row()) < six_line_row,
"the row follows the cell back to a single line");
QVERIFY2(table->rowHeight(cell.row())
<= base_row + table->fontMetrics().height(),
"the row is back to the height it had before");
QVERIFY2(table->rowHeight(cell.row()) >= table->fontMetrics().height(),
"the single line left is still shown");
}
};
QET_APP_TEST_MAIN(tst_terminalcableauto)
#include "tst_terminalcableauto.moc"