mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-01 15:24:13 +02:00
66ad129c20
In the terminal strip manager, free terminals could only be moved into a strip with the "move to" list and its button. Dragging one onto a strip, which is what users try first (#409), did nothing. Free terminals can now be dragged onto a strip in the tree on the left, from either place they are listed: - the free terminal table: every selected row is moved; - the tree itself: the dragged free terminal is moved. Dropping on a strip, or on a terminal already in that strip, moves them with the same undo command as the button, so one undo puts them back. The strip is then selected, showing the terminals just added. A drop anywhere else is refused. A drop is also refused while the free terminal table has edits that are not applied yet: moving reloads that table and would lose them. The button is disabled in the same case. The drag carries the uuids of the terminal elements under its own mime type, and the tree only moves terminals that are still free in the project when the drop happens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
469 lines
13 KiB
C++
469 lines
13 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <QComboBox>
|
|
#include <QMimeData>
|
|
|
|
#include "freeterminalmodel.h"
|
|
#include "../../elementprovider.h"
|
|
#include "../../utils/qetutils.h"
|
|
#include "../../qetgraphicsitem/terminalelement.h"
|
|
#include "../realterminal.h"
|
|
#include "../../qetinformation.h"
|
|
|
|
const int LABEL_CELL = 0;
|
|
const int XREF_CELL = 1;
|
|
const int TYPE_CELL = 2;
|
|
const int FUNCTION_CELL = 3;
|
|
const int LED_CELL = 4;
|
|
|
|
const int COLUMN_COUNT = 5;
|
|
|
|
static QVector<bool> UNMODIFIED_CELL_VECTOR{false, false, false, false, false};
|
|
|
|
FreeTerminalModel::Column FreeTerminalModel::columnTypeForIndex(const QModelIndex &index)
|
|
{
|
|
if (index.isValid())
|
|
{
|
|
switch (index.column()) {
|
|
case 0 : return Label;
|
|
case 1 : return XRef;
|
|
case 2 : return Type;
|
|
case 3 : return Function;
|
|
case 4 : return Led;
|
|
default : return Invalid;
|
|
}
|
|
}
|
|
return Invalid;
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::FreeTerminalModel
|
|
* @param project
|
|
* @param parent
|
|
*/
|
|
FreeTerminalModel::FreeTerminalModel(QETProject *project, QObject *parent) :
|
|
QAbstractTableModel(parent) {
|
|
setProject(project);
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::setProject
|
|
* Set @project as project handled by this model.
|
|
* If a previous project was setted, everything is clear.
|
|
* This function track the destruction of the project,
|
|
* that mean if the project pointer is deleted
|
|
* no need to call this function with a nullptr,
|
|
* everything is made inside this class.
|
|
* @param project
|
|
*/
|
|
void FreeTerminalModel::setProject(QETProject *project)
|
|
{
|
|
if(m_project) {
|
|
disconnect(m_project, &QObject::destroyed, this, &FreeTerminalModel::clear);
|
|
}
|
|
m_project = project;
|
|
if (m_project) {
|
|
connect(m_project, &QObject::destroyed, this, &FreeTerminalModel::clear);
|
|
}
|
|
clear();
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::rowCount
|
|
* @param parent
|
|
*/
|
|
int FreeTerminalModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent)
|
|
|
|
return m_terminal_vector.size();
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::columnCount
|
|
* @param parent
|
|
*/
|
|
int FreeTerminalModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent)
|
|
return COLUMN_COUNT;
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::data
|
|
* @param index
|
|
* @param role
|
|
* @return
|
|
*/
|
|
QVariant FreeTerminalModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (index.row() >= rowCount(QModelIndex())) {
|
|
return QVariant();
|
|
}
|
|
|
|
const auto real_t_data = m_real_t_data.at(index.row());
|
|
|
|
if (role == Qt::DisplayRole)
|
|
{
|
|
|
|
switch (index.column())
|
|
{
|
|
case LABEL_CELL: return real_t_data.label_;
|
|
case XREF_CELL : return real_t_data.Xref_;
|
|
case TYPE_CELL : return ElementData::translatedTerminalType(real_t_data.type_);
|
|
case FUNCTION_CELL: return ElementData::translatedTerminalFunction(real_t_data.function_);
|
|
default : return QVariant();
|
|
}
|
|
}
|
|
else if (role == Qt::EditRole)
|
|
{
|
|
switch (index.column()) {
|
|
case LABEL_CELL : return real_t_data.label_;
|
|
default : return QVariant();
|
|
}
|
|
}
|
|
else if (role == Qt::CheckStateRole &&
|
|
index.column() == LED_CELL) {
|
|
return real_t_data.led_ ? Qt::Checked : Qt::Unchecked;
|
|
}
|
|
else if (role == Qt::BackgroundRole && index.column() <= LED_CELL)
|
|
{
|
|
if (m_modified_cell.contains(real_t_data.real_terminal) &&
|
|
m_modified_cell.value(real_t_data.real_terminal).at(index.column()))
|
|
{
|
|
return QBrush(Qt::yellow);
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool FreeTerminalModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
auto mrtd = dataAtRow(index.row());
|
|
bool modified_ = false;
|
|
int modified_cell = -1;
|
|
auto column_ = index.column();
|
|
|
|
|
|
if (column_ == LABEL_CELL &&
|
|
role == Qt::EditRole &&
|
|
mrtd.label_ != value.toString())
|
|
{
|
|
mrtd.label_ = value.toString();
|
|
modified_ = true;
|
|
modified_cell = LABEL_CELL;
|
|
}
|
|
else if (column_ == TYPE_CELL &&
|
|
role == Qt::EditRole)
|
|
{
|
|
mrtd.type_ = value.value<ElementData::TerminalType>();
|
|
modified_ = true;
|
|
modified_cell = TYPE_CELL;
|
|
}
|
|
else if (column_ == FUNCTION_CELL &&
|
|
role == Qt::EditRole)
|
|
{
|
|
mrtd.function_ = value.value<ElementData::TerminalFunction>();
|
|
modified_ = true;
|
|
modified_cell = FUNCTION_CELL;
|
|
}
|
|
else if (column_ == LED_CELL && mrtd.led_ != value.toBool())
|
|
{
|
|
mrtd.led_ = value.toBool();
|
|
modified_ = true;
|
|
modified_cell = LED_CELL;
|
|
}
|
|
|
|
//Set the modification to the terminal data
|
|
if (modified_)
|
|
{
|
|
m_real_t_data.replace(index.row(), mrtd);
|
|
|
|
QVector<bool> vector_;
|
|
if (m_modified_cell.contains(mrtd.real_terminal)) {
|
|
vector_ = m_modified_cell.value(mrtd.real_terminal);
|
|
} else {
|
|
vector_ = UNMODIFIED_CELL_VECTOR;
|
|
}
|
|
|
|
vector_.replace(modified_cell, true);
|
|
m_modified_cell.insert(mrtd.real_terminal, vector_);
|
|
emit dataChanged(index, index);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::headerData
|
|
* @param section
|
|
* @param orientation
|
|
* @param role
|
|
* @return
|
|
*/
|
|
QVariant FreeTerminalModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role == Qt::DisplayRole)
|
|
{
|
|
if (orientation == Qt::Horizontal)
|
|
{
|
|
switch (section) {
|
|
case LABEL_CELL: return tr("Label");
|
|
case XREF_CELL: return tr("Référence croisé");
|
|
case TYPE_CELL: return tr("Type");
|
|
case FUNCTION_CELL: return tr("Fonction");
|
|
case LED_CELL: return tr("led");
|
|
default : return QVariant();
|
|
}
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags FreeTerminalModel::flags(const QModelIndex &index) const
|
|
{
|
|
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
|
|
|
|
auto c = index.column();
|
|
if (c == LABEL_CELL || c == TYPE_CELL || c == FUNCTION_CELL)
|
|
flags = flags | Qt::ItemIsEditable;
|
|
if (c == LED_CELL) {
|
|
flags = flags | Qt::ItemIsUserCheckable;
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::clear
|
|
* Clear the model and set it as the current
|
|
* state of the project
|
|
*/
|
|
void FreeTerminalModel::clear()
|
|
{
|
|
beginResetModel();
|
|
m_terminal_vector.clear();
|
|
m_real_t_data.clear();
|
|
m_modified_cell.clear();
|
|
fillTerminalVector();
|
|
endResetModel();
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::modifiedModelRealTerminalData
|
|
* @return a vector of modified terminal.
|
|
*/
|
|
QVector<modelRealTerminalData> FreeTerminalModel::modifiedModelRealTerminalData() const
|
|
{
|
|
QVector<modelRealTerminalData> returned_vector;
|
|
|
|
for (const auto &real_t_data : m_real_t_data) {
|
|
if (m_modified_cell.contains(real_t_data.real_terminal)) {
|
|
returned_vector.append(real_t_data);
|
|
}
|
|
}
|
|
|
|
return returned_vector;
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::dataAtRow
|
|
* @param row
|
|
* @return the current data at row @a row
|
|
*/
|
|
modelRealTerminalData FreeTerminalModel::dataAtRow(int row) const
|
|
{
|
|
if (row >= m_terminal_vector.size()) {
|
|
return modelRealTerminalData();
|
|
} else {
|
|
return m_real_t_data.at(row);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::realTerminalForIndex
|
|
* @param index_list
|
|
* @return The QSharedPointer<RealTerminal> associated with the index found in @a index_list
|
|
*/
|
|
QVector<QSharedPointer<RealTerminal> > FreeTerminalModel::realTerminalForIndex(const QModelIndexList &index_list) const
|
|
{
|
|
QVector<QSharedPointer<RealTerminal>> vector_;
|
|
for (const auto &index : index_list)
|
|
{
|
|
if (index.isValid()
|
|
&& index.model() == this
|
|
&& index.row() < m_terminal_vector.size())
|
|
{
|
|
const auto rt_ = m_terminal_vector.at(index.row());
|
|
if (!vector_.contains(rt_)) {
|
|
vector_.append(m_terminal_vector.at(index.row()));
|
|
}
|
|
}
|
|
}
|
|
|
|
return vector_;
|
|
}
|
|
|
|
QStringList FreeTerminalModel::mimeTypes() const {
|
|
return QStringList{freeTerminalMimeType()};
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::mimeData
|
|
* @param indexes
|
|
* @return the uuids of the terminal elements of the rows in @a indexes,
|
|
* to be dropped on a terminal strip of the terminal strip tree.
|
|
*/
|
|
QMimeData *FreeTerminalModel::mimeData(const QModelIndexList &indexes) const
|
|
{
|
|
QVector<QUuid> uuids;
|
|
for (const auto &real_t : realTerminalForIndex(indexes)) {
|
|
uuids.append(real_t->elementUuid());
|
|
}
|
|
return mimeDataForUuids(uuids);
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::freeTerminalMimeType
|
|
* @return the mime type of a drag of free terminals
|
|
*/
|
|
QString FreeTerminalModel::freeTerminalMimeType() {
|
|
return QStringLiteral("application/x-qet-free-terminal-uuids");
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::uuidsFromMimeData
|
|
* @param mime_data
|
|
* @return the uuids of the terminal elements carried by @a mime_data,
|
|
* empty if it is not a drag of free terminals.
|
|
*/
|
|
QVector<QUuid> FreeTerminalModel::uuidsFromMimeData(const QMimeData *mime_data)
|
|
{
|
|
QVector<QUuid> uuids;
|
|
if (!mime_data || !mime_data->hasFormat(freeTerminalMimeType())) {
|
|
return uuids;
|
|
}
|
|
|
|
const auto lines = QString::fromLatin1(mime_data->data(freeTerminalMimeType())).split(QLatin1Char('\n'), Qt::SkipEmptyParts);
|
|
for (const auto &line : lines) {
|
|
const QUuid uuid(line);
|
|
if (!uuid.isNull()) {
|
|
uuids.append(uuid);
|
|
}
|
|
}
|
|
return uuids;
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::mimeDataForUuids
|
|
* @param uuids
|
|
* @return a new QMimeData carrying the uuids of terminal elements in @a uuids
|
|
*/
|
|
QMimeData *FreeTerminalModel::mimeDataForUuids(const QVector<QUuid> &uuids)
|
|
{
|
|
QStringList lines;
|
|
for (const auto &uuid : uuids) {
|
|
lines.append(uuid.toString());
|
|
}
|
|
|
|
auto mime_data = new QMimeData();
|
|
mime_data->setData(freeTerminalMimeType(), lines.join(QLatin1Char('\n')).toLatin1());
|
|
return mime_data;
|
|
}
|
|
|
|
/**
|
|
* @brief FreeTerminalModel::fillTerminalVector
|
|
*/
|
|
void FreeTerminalModel::fillTerminalVector()
|
|
{
|
|
if (m_project) {
|
|
ElementProvider provider_(m_project);
|
|
auto free_terminal_vector = provider_.freeTerminal();
|
|
|
|
std::sort(free_terminal_vector.begin(), free_terminal_vector.end(),
|
|
[](TerminalElement *a, TerminalElement *b)
|
|
{
|
|
return QETUtils::sortBeginIntString(a->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString(),
|
|
b->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString());
|
|
});
|
|
|
|
for (const auto &terminal_ : free_terminal_vector) {
|
|
m_terminal_vector.append(terminal_->realTerminal());
|
|
m_real_t_data.append(modelRealTerminalData::data(terminal_->realTerminal()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************
|
|
* A little delegate for add a combobox to edit type and function
|
|
****************************************************************/
|
|
|
|
FreeTerminalModelDelegate::FreeTerminalModelDelegate(QObject *parent) :
|
|
QStyledItemDelegate(parent)
|
|
{}
|
|
|
|
QWidget *FreeTerminalModelDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
|
{
|
|
if (index.column() == TYPE_CELL) {
|
|
auto qcb = new QComboBox(parent);
|
|
qcb->setObjectName(QStringLiteral("terminal_type"));
|
|
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTGeneric), ElementData::TTGeneric);
|
|
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTFuse), ElementData::TTFuse);
|
|
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTSectional), ElementData::TTSectional);
|
|
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTDiode), ElementData::TTDiode);
|
|
qcb->addItem(ElementData::translatedTerminalType(ElementData::TTGround), ElementData::TTGround);
|
|
|
|
return qcb;
|
|
}
|
|
if (index.column() == FUNCTION_CELL) {
|
|
auto qcb = new QComboBox(parent);
|
|
qcb->setObjectName(QStringLiteral("terminal_function"));
|
|
qcb->addItem(ElementData::translatedTerminalFunction(ElementData::TFGeneric), ElementData::TFGeneric);
|
|
qcb->addItem(ElementData::translatedTerminalFunction(ElementData::TFPhase), ElementData::TFPhase);
|
|
qcb->addItem(ElementData::translatedTerminalFunction(ElementData::TFNeutral), ElementData::TFNeutral);
|
|
|
|
return qcb;
|
|
}
|
|
|
|
return QStyledItemDelegate::createEditor(parent, option, index);
|
|
}
|
|
|
|
void FreeTerminalModelDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
|
{
|
|
if (index.isValid())
|
|
{
|
|
if (editor->objectName() == QLatin1String("terminal_type"))
|
|
{
|
|
if (auto qcb = dynamic_cast<QComboBox *>(editor)) {
|
|
model->setData(index, qcb->currentData(), Qt::EditRole);
|
|
}
|
|
}
|
|
else if (editor->objectName() == QLatin1String("terminal_function"))
|
|
{
|
|
if (auto qcb = dynamic_cast<QComboBox *>(editor)) {
|
|
model->setData(index, qcb->currentData(), Qt::EditRole);
|
|
}
|
|
}
|
|
else {
|
|
QStyledItemDelegate::setModelData(editor, model, index);
|
|
}
|
|
}
|
|
}
|