mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-28 04:54:13 +02:00
ffd829bb69
max_slaves records how many contacts a part is expected to carry. It was enforced as a rule the drawing had to obey, which obstructs the way both @scorpio810 and @IBSYSLevi described working in #819: draw the schematic first, choose the physical hardware afterwards. A limit that refuses the link forces the hardware decision up front, which is exactly what they said gets in the way. Two changes, both in the UI rather than in isFull(), which stays the query it always was: - MasterPropertiesWidget::on_link_button_clicked() now says the limit is reached and asks whether to link anyway, defaulting to yes, instead of refusing outright. - LinkSingleElementWidget no longer removes a full master from the candidate list. That was the worse half: a master at its limit simply was not there, indistinguishable from one that does not exist, with nothing to say why. It now stays selectable and the user decides. PLC masters are deliberately left alone. Their limit is the number of declared IO slots, which is structural rather than advisory -- a link past it would have no IO index to map to -- and PlcLinkWidget already tells the user when it hides one, via m_hidden_masters_label. Only coils that opt into a limit are affected: max_slaves defaults to -1, and no project in examples/ sets it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
810 lines
21 KiB
C++
810 lines
21 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 "linksingleelementwidget.h"
|
|
#include "contactgroupselectiondialog.h"
|
|
#include "../qetgraphicsitem/conductor.h"
|
|
#include "../diagram.h"
|
|
#include "../diagramposition.h"
|
|
#include "../qetgraphicsitem/element.h"
|
|
#include "../elementprovider.h"
|
|
#include "../undocommand/linkelementcommand.h"
|
|
#include "../qetinformation.h"
|
|
#include "../qetproject.h"
|
|
#include "../ui_linksingleelementwidget.h"
|
|
|
|
#include <QTreeWidgetItem>
|
|
#include <QInputDialog>
|
|
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::LinkSingleElementWidget
|
|
Default constructor
|
|
@param elmt
|
|
the edited element
|
|
@param parent
|
|
the parent widget
|
|
*/
|
|
LinkSingleElementWidget::LinkSingleElementWidget(Element *elmt,
|
|
QWidget *parent) :
|
|
AbstractElementPropertiesEditorWidget(parent),
|
|
ui(new Ui::LinkSingleElementWidget)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
ui->m_tree_widget->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
m_context_menu = new QMenu(this);
|
|
m_link_action = new QAction(tr("Lier l'élément"), this);
|
|
m_show_qtwi = new QAction(tr("Montrer l'élément"), this);
|
|
m_show_element = new QAction(tr("Montrer l'élément esclave"), this);
|
|
m_save_header_state = new QAction(tr("Enregistrer la disposition"), this);
|
|
|
|
connect(m_show_qtwi, &QAction::triggered, this, [=]()
|
|
{
|
|
this->on_m_tree_widget_itemDoubleClicked(this->m_qtwi_at_context_menu, 0);
|
|
});
|
|
|
|
connect(m_link_action,
|
|
&QAction::triggered,
|
|
this,
|
|
&LinkSingleElementWidget::linkTriggered);
|
|
|
|
connect(m_show_element, &QAction::triggered, this, [=]()
|
|
{
|
|
this->m_element->diagram()->showMe();
|
|
this->m_element->setHighlighted(true);
|
|
if(this->m_showed_element)
|
|
m_showed_element->setHighlighted(false);
|
|
});
|
|
|
|
QHeaderView *qhv = ui->m_tree_widget->header();
|
|
qhv->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(qhv,
|
|
&QHeaderView::customContextMenuRequested,
|
|
this,
|
|
&LinkSingleElementWidget::headerCustomContextMenuRequested);
|
|
connect(m_save_header_state, &QAction::triggered, [this, qhv]()
|
|
{
|
|
QByteArray qba = qhv->saveState();
|
|
QSettings settings;
|
|
|
|
const auto elmt_type{this->m_element->elementData().m_type};
|
|
if (elmt_type & ElementData::AllReport)
|
|
settings.setValue(QStringLiteral("link-element-widget/report-state"), qba);
|
|
else if (elmt_type == ElementData::Slave)
|
|
settings.setValue(QStringLiteral("link-element-widget/slave-state"), qba);
|
|
});
|
|
|
|
setElement(elmt);
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::~LinkSingleElementWidget
|
|
Default destructor
|
|
*/
|
|
LinkSingleElementWidget::~LinkSingleElementWidget()
|
|
{
|
|
if(m_showed_element)
|
|
m_showed_element->setHighlighted(false);
|
|
|
|
if(m_element)
|
|
{
|
|
m_element->setHighlighted(false);
|
|
if (!m_element->isFree())
|
|
m_element->linkedElements().first()->setHighlighted(false);
|
|
}
|
|
delete ui;
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::setElement
|
|
Set element to be the edited element.
|
|
@param element
|
|
*/
|
|
void LinkSingleElementWidget::setElement(Element *element)
|
|
{
|
|
if (m_element == element)
|
|
return;
|
|
|
|
//Remove connection of previous edited element
|
|
if (m_element)
|
|
{
|
|
disconnect(m_element->diagram()->project(),
|
|
&QETProject::diagramRemoved,
|
|
this,
|
|
&LinkSingleElementWidget::diagramWasRemovedFromProject);
|
|
disconnect(m_element.data(),
|
|
&Element::linkedElementChanged,
|
|
this,
|
|
&LinkSingleElementWidget::updateUi);
|
|
m_element->setHighlighted(false);
|
|
}
|
|
|
|
if(m_showed_element)
|
|
m_showed_element->setHighlighted(false);
|
|
|
|
m_unlink = false;
|
|
m_showed_element = nullptr;
|
|
m_element_to_link = nullptr;
|
|
m_pending_qtwi = nullptr;
|
|
|
|
//Setup the new element, connection and ui
|
|
m_element = element;
|
|
|
|
const auto elmt_type{m_element->elementData().m_type};
|
|
if (elmt_type == ElementData::Slave)
|
|
m_filter = ElementData::Master;
|
|
else if (elmt_type & ElementData::AllReport)
|
|
m_filter = elmt_type == ElementData::NextReport
|
|
? ElementData::PreviousReport
|
|
: ElementData::NextReport;
|
|
else
|
|
m_filter = ElementData::Simple;
|
|
|
|
connect(m_element->diagram()->project(), &QETProject::diagramRemoved,
|
|
this, &LinkSingleElementWidget::diagramWasRemovedFromProject);
|
|
connect(m_element.data(), &Element::linkedElementChanged,
|
|
this, &LinkSingleElementWidget::updateUi, Qt::QueuedConnection);
|
|
|
|
updateUi();
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::apply
|
|
Apply the new property of the edited element by pushing
|
|
the associated undo command to parent project undo stack
|
|
*/
|
|
void LinkSingleElementWidget::apply()
|
|
{
|
|
QUndoCommand *undo = associatedUndo();
|
|
if (undo)
|
|
m_element->diagram()->undoStack().push(undo);
|
|
|
|
m_unlink = false;
|
|
m_element_to_link = nullptr;
|
|
m_pending_qtwi = nullptr;
|
|
m_pending_group_index = -1;
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::associatedUndo
|
|
@return the undo command associated to the current edition
|
|
if there isn't change, return nulptr
|
|
*/
|
|
QUndoCommand *LinkSingleElementWidget::associatedUndo() const
|
|
{
|
|
LinkElementCommand *undo = new LinkElementCommand(m_element);
|
|
|
|
if (m_element_to_link || m_unlink)
|
|
{
|
|
if (m_element_to_link) {
|
|
undo->setLink(m_element_to_link);
|
|
if (m_pending_group_index >= 0) {
|
|
undo->setGroupIndex(m_pending_group_index);
|
|
}
|
|
}
|
|
else if (m_unlink)
|
|
undo->unlinkAll();
|
|
|
|
return undo;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::title
|
|
@return the title used for this editor
|
|
*/
|
|
QString LinkSingleElementWidget::title() const
|
|
{
|
|
if (m_element->elementData().m_type & ElementData::AllReport)
|
|
return tr("Report de folio");
|
|
else
|
|
return tr("Référence croisée (esclave)");
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::updateUi
|
|
Update the content of this widget
|
|
*/
|
|
void LinkSingleElementWidget::updateUi()
|
|
{
|
|
m_unlink = false;
|
|
|
|
//Update the behavior of link/unlink button
|
|
if (m_element->isFree())
|
|
hideButtons();
|
|
else
|
|
showButtons();
|
|
|
|
buildTree();
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::buildTree
|
|
Build the content of the QTreeWidget
|
|
*/
|
|
void LinkSingleElementWidget::buildTree()
|
|
{
|
|
clearTreeWidget();
|
|
setUpHeaderLabels();
|
|
QSettings settings;
|
|
|
|
const auto elmt_vector{availableElements()};
|
|
|
|
if (m_element->elementData().m_type == ElementData::Slave)
|
|
{
|
|
|
|
for(const auto &elmt : elmt_vector)
|
|
{
|
|
QStringList search_list;
|
|
QStringList str_list;
|
|
|
|
str_list << elmt->actualLabel();
|
|
if(!str_list.last().isEmpty()) {
|
|
search_list << str_list.last();
|
|
}
|
|
|
|
str_list << elmt->elementInformations()[QETInformation::ELMT_COMMENT].toString();
|
|
if (!str_list.last().isEmpty())
|
|
search_list << str_list.last();
|
|
|
|
if (const auto diag = elmt->diagram())
|
|
{
|
|
if (settings.value(QStringLiteral("genericpanel/folio"), false).toBool())
|
|
{
|
|
autonum::sequentialNumbers seq;
|
|
QString F =autonum::AssignVariables::formulaToLabel(
|
|
diag->border_and_titleblock.folio(), seq, diag, elmt);
|
|
str_list << F;
|
|
}
|
|
else
|
|
{
|
|
str_list << QString::number(diag->folioIndex() + 1);
|
|
}
|
|
str_list << diag->convertPosition(elmt->scenePos()).toString();
|
|
str_list << diag->title();
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "In method void LinkSingleElementWidget::updateUi(), provided element must be in a diagram";
|
|
}
|
|
|
|
QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_tree_widget, str_list);
|
|
m_qtwi_elmt_hash.insert(qtwi, elmt);
|
|
m_qtwi_strl_hash.insert(qtwi, search_list);
|
|
}
|
|
|
|
|
|
QVariant v = settings.value(QStringLiteral("link-element-widget/slave-state"));
|
|
if(!v.isNull())
|
|
ui->m_tree_widget->header()->restoreState(v.toByteArray());
|
|
}
|
|
|
|
else if (m_element->elementData().m_type & ElementData::AllReport)
|
|
{
|
|
for(const auto &elmt : elmt_vector)
|
|
{
|
|
QStringList search_list;
|
|
QStringList str_list;
|
|
|
|
if (!elmt->conductors().isEmpty())
|
|
{
|
|
ConductorProperties cp = elmt->conductors().first()->properties();
|
|
str_list << cp.text;
|
|
if (!str_list.last().isEmpty())
|
|
search_list << str_list.last();
|
|
str_list << cp.m_function;
|
|
if (!str_list.last().isEmpty())
|
|
search_list << str_list.last();
|
|
str_list << cp.m_tension_protocol;
|
|
if (!str_list.last().isEmpty())
|
|
search_list << str_list.last();
|
|
str_list << cp.m_wire_color;
|
|
if (!str_list.last().isEmpty())
|
|
search_list << str_list.last();
|
|
str_list << cp.m_wire_section;
|
|
if (!str_list.last().isEmpty())
|
|
search_list << str_list.last();
|
|
}
|
|
else
|
|
str_list << "" << "" << "" << "" << "";
|
|
|
|
if (Diagram *diag = elmt->diagram())
|
|
{
|
|
if (settings.value(QStringLiteral("genericpanel/folio"), false).toBool())
|
|
{
|
|
autonum::sequentialNumbers seq;
|
|
QString F =autonum::AssignVariables::formulaToLabel(diag->border_and_titleblock.folio(), seq, diag, elmt);
|
|
str_list << F;
|
|
}
|
|
else
|
|
{
|
|
str_list << QString::number(diag->folioIndex() + 1);
|
|
}
|
|
str_list << diag->convertPosition(elmt->scenePos()).toString();
|
|
str_list << diag->title();
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "In method void LinkSingleElementWidget::updateUi(), provided element must be in a diagram";
|
|
}
|
|
|
|
QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_tree_widget, str_list);
|
|
m_qtwi_elmt_hash.insert(qtwi, elmt);
|
|
m_qtwi_strl_hash.insert(qtwi, search_list);
|
|
}
|
|
|
|
QSettings settings;
|
|
QVariant v = settings.value(QStringLiteral("link-element-widget/report-state"));
|
|
auto *header = ui->m_tree_widget->header();
|
|
if (v.isNull() || !header->restoreState(v.toByteArray()))
|
|
{
|
|
// Keep logical column IDs stable for saved layouts, but show the
|
|
// folio identity first even when the candidate has no conductor.
|
|
for (int column = 5; column < 8; ++column)
|
|
header->moveSection(header->visualIndex(column), column - 5);
|
|
ui->m_tree_widget->resizeColumnToContents(5);
|
|
ui->m_tree_widget->resizeColumnToContents(6);
|
|
}
|
|
}
|
|
|
|
setUpCompleter();
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::setLiveEdit
|
|
@param live_edit
|
|
@return
|
|
*/
|
|
bool LinkSingleElementWidget::setLiveEdit(bool live_edit)
|
|
{
|
|
m_live_edit = live_edit;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::availableElements
|
|
@return A QList with all available elements
|
|
to be linked with the edited element.
|
|
This method takes care of the "find in diagram" combo box
|
|
*/
|
|
QVector <QPointer<Element>> LinkSingleElementWidget::availableElements()
|
|
{
|
|
QVector <QPointer<Element>> elmt_vector;
|
|
//if element isn't free and unlink isn't pressed, return an empty list
|
|
if (!m_element->isFree() && !m_unlink)
|
|
return elmt_vector;
|
|
|
|
if (!m_element->diagram() || !m_element->diagram()->project()) return elmt_vector;
|
|
|
|
ElementProvider ep(m_element->diagram()->project());
|
|
if (m_filter & ElementData::AllReport)
|
|
elmt_vector = ep.freeElement(m_filter);
|
|
else
|
|
elmt_vector = ep.find(m_filter);
|
|
|
|
//If element is linked, remove is parent from the list
|
|
if(!m_element->isFree()) elmt_vector.removeAll(m_element->linkedElements().first());
|
|
|
|
// Filter out incompatible elements: PLC and non-PLC must not mix
|
|
const bool element_is_plc = (m_element->elementData().m_type == ElementData::Slave &&
|
|
m_element->elementData().m_slave_type == ElementData::PLCSlave);
|
|
for (int i = elmt_vector.size() - 1; i >= 0; --i) {
|
|
Element *elmt = elmt_vector.at(i);
|
|
|
|
if (elmt->linkType() == Element::Master) {
|
|
const bool master_is_plc = (elmt->elementData().m_master_type == ElementData::PLC);
|
|
|
|
// PLC slave can only link to PLC master, and vice versa
|
|
if (element_is_plc != master_is_plc) {
|
|
elmt_vector.removeAt(i);
|
|
continue;
|
|
}
|
|
|
|
// A master at its declared limit stays in the list. Removing
|
|
// it made a full master indistinguishable from one that does
|
|
// not exist: the candidate simply was not there, with nothing
|
|
// to say why. The limit is advisory -- see the prompt in
|
|
// MasterPropertiesWidget::on_link_button_clicked() -- so the
|
|
// user decides, rather than the list deciding for them.
|
|
}
|
|
}
|
|
return elmt_vector;
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::setUpCompleter
|
|
Setup the completer of search_field
|
|
*/
|
|
void LinkSingleElementWidget::setUpCompleter()
|
|
{
|
|
ui->m_search_field->clear();
|
|
if(ui->m_search_field->completer())
|
|
delete ui->m_search_field->completer();
|
|
|
|
QStringList search;
|
|
for(const auto &str_list : m_qtwi_strl_hash.values())
|
|
search.append(str_list);
|
|
|
|
QCompleter *c = new QCompleter(search, ui->m_search_field);
|
|
c->setCaseSensitivity(Qt::CaseInsensitive);
|
|
ui->m_search_field->setCompleter(c);
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::clearTreeWidget
|
|
Clear the tree widget.
|
|
Delete all QTreeWidget (in the tree widget and in the hash).
|
|
Clear the hash.
|
|
*/
|
|
void LinkSingleElementWidget::clearTreeWidget()
|
|
{
|
|
while(ui->m_tree_widget->topLevelItemCount())
|
|
{
|
|
QTreeWidgetItem *qtwi = ui->m_tree_widget->takeTopLevelItem(0);
|
|
if (!m_qtwi_elmt_hash.contains(qtwi))
|
|
delete qtwi;
|
|
}
|
|
|
|
foreach(QTreeWidgetItem *qtwi, m_qtwi_elmt_hash.keys())
|
|
delete qtwi;
|
|
|
|
m_qtwi_elmt_hash.clear();
|
|
m_qtwi_strl_hash.clear();
|
|
}
|
|
|
|
void LinkSingleElementWidget::setUpHeaderLabels()
|
|
{
|
|
QStringList list;
|
|
const QSettings settings;
|
|
|
|
const auto elmt_type{m_element->elementData().m_type};
|
|
if (elmt_type == ElementData::Slave)
|
|
{
|
|
if (settings.value(QStringLiteral("genericpanel/folio"), false).toBool())
|
|
{
|
|
list << tr("Label")
|
|
<< tr("Commentaire")
|
|
<< tr("Label de folio")
|
|
<< tr("Position")
|
|
<< tr("Titre de folio");
|
|
}
|
|
else
|
|
{
|
|
list << tr("Label")
|
|
<< tr("Commentaire")
|
|
<< tr("N° de folio")
|
|
<< tr("Position")
|
|
<< tr("Titre de folio");
|
|
}
|
|
}
|
|
|
|
if (elmt_type & ElementData::AllReport)
|
|
{
|
|
if (settings.value(QStringLiteral("genericpanel/folio"), false).toBool())
|
|
{
|
|
list << tr("N° de fil")
|
|
<< tr("Fonction")
|
|
<< tr("Tension / Protocole")
|
|
<< tr("Couleur du conducteur")
|
|
<< tr("Section du conducteur")
|
|
<< tr("Label de folio")
|
|
<< tr("Position")
|
|
<< tr("Titre de folio");
|
|
}
|
|
else
|
|
{
|
|
list << tr("N° de fil")
|
|
<< tr("Fonction")
|
|
<< tr("Tension / Protocole")
|
|
<< tr("Couleur du conducteur")
|
|
<< tr("Section du conducteur")
|
|
<< tr("N° de folio")
|
|
<< tr("Position")
|
|
<< tr("Titre de folio");
|
|
}
|
|
}
|
|
|
|
ui->m_tree_widget->setHeaderLabels(list);
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::diagramWasRemovedFromProject
|
|
This slot is called when a diagram
|
|
is removed from the parent project of edited element
|
|
to update the content of this widget
|
|
*/
|
|
void LinkSingleElementWidget::diagramWasRemovedFromProject()
|
|
{
|
|
// We use a timer because if the removed diagram
|
|
// contains the master element linked to the edited element
|
|
// we must wait for this elements to be unlinked,
|
|
// or else the list of available master isn't up to date
|
|
QTimer::singleShot(10, this, &LinkSingleElementWidget::updateUi);
|
|
}
|
|
|
|
void LinkSingleElementWidget::showedElementWasDeleted()
|
|
{
|
|
m_showed_element = nullptr;
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::linkTriggered
|
|
Action linkis triggered
|
|
*/
|
|
void LinkSingleElementWidget::linkTriggered()
|
|
{
|
|
if(!m_qtwi_at_context_menu)
|
|
return;
|
|
|
|
m_element_to_link = m_qtwi_elmt_hash.value(m_qtwi_at_context_menu);
|
|
m_pending_group_index = -1;
|
|
|
|
//If linking a slave to a master with contact groups, show group selection dialog
|
|
if (m_element->linkType() == Element::Slave
|
|
&& m_element_to_link
|
|
&& m_element_to_link->linkType() == Element::Master)
|
|
{
|
|
// Check if this is a PLC master
|
|
if (m_element_to_link->elementData().m_master_type == ElementData::PLC)
|
|
{
|
|
// Show PLC IO selection dialog
|
|
const auto &plc_data = m_element_to_link->elementData().plcMasterData();
|
|
if (!plc_data.ios.isEmpty())
|
|
{
|
|
// Collect already-used IO indices from the master
|
|
QSet<int> used_indices;
|
|
for (Element *linked : m_element_to_link->linkedElements()) {
|
|
int idx = m_element_to_link->groupIndexForElement(linked);
|
|
if (idx >= 0) {
|
|
used_indices.insert(idx);
|
|
}
|
|
}
|
|
|
|
// Build selection dialog
|
|
QStringList items;
|
|
for (int i = 0; i < plc_data.ios.size(); ++i) {
|
|
const auto &io = plc_data.ios.at(i);
|
|
QString label = QString("[%1] %2 - %3")
|
|
.arg(i + 1)
|
|
.arg(io.address)
|
|
.arg(io.functionText);
|
|
if (used_indices.contains(i))
|
|
label += tr(" (déjà utilisé)");
|
|
items << label;
|
|
}
|
|
|
|
bool ok = false;
|
|
int selected = QInputDialog::getInt(
|
|
this,
|
|
tr("Sélectionner un IO PLC"),
|
|
tr("IO disponible:"),
|
|
0, 0, plc_data.ios.size() - 1, 1, &ok);
|
|
|
|
if (ok && selected >= 0) {
|
|
m_pending_group_index = selected;
|
|
} else {
|
|
m_element_to_link = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Normal contact group selection for non-PLC masters
|
|
const auto &groups = m_element_to_link->elementData().m_slave_contact_groups;
|
|
if (!groups.isEmpty())
|
|
{
|
|
// Collect already-used group indices from the master
|
|
QSet<int> used_indices;
|
|
for (Element *linked : m_element_to_link->linkedElements()) {
|
|
int idx = m_element_to_link->groupIndexForElement(linked);
|
|
if (idx >= 0) {
|
|
used_indices.insert(idx);
|
|
}
|
|
}
|
|
|
|
ContactGroupSelectionDialog dlg(groups, used_indices,
|
|
m_element->elementData(), this);
|
|
if (dlg.exec() == QDialog::Accepted && dlg.selectedIndex() >= 0) {
|
|
m_pending_group_index = dlg.selectedIndex();
|
|
} else {
|
|
m_element_to_link = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(m_live_edit)
|
|
{
|
|
apply();
|
|
updateUi();
|
|
}
|
|
else
|
|
{
|
|
//In no live edit mode, we set the background of the qtwi green,
|
|
// to inform the user
|
|
// which element will be linked when he press the apply button
|
|
if (m_pending_qtwi)
|
|
{
|
|
for(int i=0 ; i<6 ; i++)
|
|
{
|
|
m_pending_qtwi->setBackground(i,
|
|
QBrush(
|
|
Qt::white,
|
|
Qt::NoBrush));
|
|
}
|
|
}
|
|
|
|
for (int i=0 ; i<6 ; i++)
|
|
{
|
|
m_qtwi_at_context_menu->setBackground(i,
|
|
QBrush(Qt::green,
|
|
Qt::SolidPattern));
|
|
}
|
|
m_pending_qtwi = m_qtwi_at_context_menu;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::hideButtons
|
|
Hide the button displayed when element is already linked
|
|
*/
|
|
void LinkSingleElementWidget::hideButtons()
|
|
{
|
|
ui->m_label->hide();
|
|
ui->m_unlink_pb->hide();
|
|
ui->m_show_linked_pb->hide();
|
|
ui->m_show_this_pb->hide();
|
|
ui->m_search_field->show();
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::showButtons
|
|
Show the button displayed when element is already linked
|
|
*/
|
|
void LinkSingleElementWidget::showButtons()
|
|
{
|
|
ui->m_label->show();
|
|
ui->m_unlink_pb->show();
|
|
ui->m_show_linked_pb->show();
|
|
ui->m_show_this_pb->show();
|
|
ui->m_search_field->hide();
|
|
}
|
|
|
|
void LinkSingleElementWidget::headerCustomContextMenuRequested(
|
|
const QPoint &pos)
|
|
{
|
|
m_context_menu->clear();
|
|
m_context_menu->addAction(m_save_header_state);
|
|
m_context_menu->popup(ui->m_tree_widget->header()->mapToGlobal(pos));
|
|
}
|
|
|
|
void LinkSingleElementWidget::on_m_unlink_pb_clicked()
|
|
{
|
|
m_unlink = true;
|
|
|
|
if(m_live_edit)
|
|
{
|
|
apply();
|
|
updateUi();
|
|
}
|
|
else
|
|
buildTree();
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::on_m_tree_widget_itemDoubleClicked
|
|
Highlight the element represented by item
|
|
@param item
|
|
@param column
|
|
*/
|
|
void LinkSingleElementWidget::on_m_tree_widget_itemDoubleClicked(
|
|
QTreeWidgetItem *item,
|
|
int column)
|
|
{
|
|
Q_UNUSED(column);
|
|
|
|
if (m_showed_element)
|
|
{
|
|
disconnect(m_showed_element, &QObject::destroyed, this, &LinkSingleElementWidget::showedElementWasDeleted);
|
|
m_showed_element->setHighlighted(false);
|
|
}
|
|
|
|
Element *elmt = m_qtwi_elmt_hash.value(item);
|
|
elmt->diagram()->showMe();
|
|
elmt->setHighlighted(true);
|
|
m_showed_element = elmt;
|
|
connect(m_showed_element, &QObject::destroyed, this, &LinkSingleElementWidget::showedElementWasDeleted);
|
|
|
|
}
|
|
|
|
void LinkSingleElementWidget::on_m_tree_widget_customContextMenuRequested(
|
|
const QPoint &pos)
|
|
{
|
|
//add the size of the header to display the topleft of the QMenu
|
|
//at the position of the mouse.
|
|
//See doc about QWidget::customContextMenuRequested
|
|
//section related to QAbstractScrollArea
|
|
QPoint point = pos;
|
|
point.ry()+=ui->m_tree_widget->header()->height();
|
|
point = ui->m_tree_widget->mapToGlobal(point);
|
|
|
|
m_context_menu->clear();
|
|
|
|
if (ui->m_tree_widget->currentItem())
|
|
{
|
|
m_qtwi_at_context_menu = ui->m_tree_widget->currentItem();
|
|
m_context_menu->addAction(m_link_action);
|
|
m_context_menu->addAction(m_show_qtwi);
|
|
}
|
|
|
|
m_context_menu->addAction(m_show_element);
|
|
m_context_menu->popup(point);
|
|
}
|
|
|
|
void LinkSingleElementWidget::on_m_show_linked_pb_clicked()
|
|
{
|
|
if (!m_element->isFree())
|
|
{
|
|
Element *elmt = m_element->linkedElements().first();
|
|
elmt->diagram()->showMe();
|
|
elmt->setHighlighted(true);
|
|
}
|
|
}
|
|
|
|
void LinkSingleElementWidget::on_m_show_this_pb_clicked()
|
|
{
|
|
m_show_element->trigger();
|
|
}
|
|
|
|
/**
|
|
@brief LinkSingleElementWidget::on_m_search_field_textEdited
|
|
Search all items which match with arg1 and shows it,
|
|
other items is hidden.
|
|
If arg1 is empty, show all items.
|
|
@param arg1
|
|
*/
|
|
void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &arg1)
|
|
{
|
|
//Show all items if arg1 is empty, if not hide all items
|
|
foreach(QTreeWidgetItem *qtwi, m_qtwi_elmt_hash.keys())
|
|
qtwi->setHidden(!arg1.isEmpty());
|
|
|
|
QList <QTreeWidgetItem *> qtwi_list;
|
|
|
|
foreach(QTreeWidgetItem *qtwi, m_qtwi_strl_hash.keys())
|
|
{
|
|
foreach(QString str, m_qtwi_strl_hash.value(qtwi))
|
|
{
|
|
if(str.contains(arg1, Qt::CaseInsensitive))
|
|
{
|
|
qtwi_list << qtwi;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Show items which match with arg1
|
|
foreach(QTreeWidgetItem *qtwi, qtwi_list)
|
|
qtwi->setHidden(false);
|
|
}
|