/* Copyright 2006-2019 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 "linksingleelementwidget.h" #include "ui_linksingleelementwidget.h" #include "diagram.h" #include "elementprovider.h" #include "linkelementcommand.h" #include "diagramposition.h" #include "conductor.h" #include /** * @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; if (this->m_element->linkType() & Element::AllReport) settings.setValue("link-element-widget/report-state", qba); else if (this->m_element->linkType() == Element::Slave) settings.setValue("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; if (m_element->linkType() & Element::Slave) m_filter = Element::Master; else if (m_element->linkType() & Element::AllReport) m_filter = m_element->linkType() == Element::NextReport? Element::PreviousReport : Element::NextReport; else m_filter = Element::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; } /** * @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); 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->linkType() & Element::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 QList elmt_list = availableElements(); if (m_element->linkType() == Element::Slave) { for(Element *elmt : elmt_list) { QStringList search_list; QStringList str_list; QString formula = elmt->elementInformations()["formula"].toString(); if(!formula.isEmpty()) { str_list << autonum::AssignVariables::formulaToLabel(formula, elmt->rSequenceStruct(), elmt->diagram(), elmt); search_list << str_list.last(); } else { str_list << elmt->elementInformations()["label"].toString(); if(!str_list.last().isEmpty()) search_list << str_list.last(); } str_list << elmt->elementInformations()["comment"].toString(); if (!str_list.last().isEmpty()) search_list << str_list.last(); if (Diagram *diag = elmt->diagram()) { if (settings.value("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("link-element-widget/slave-state"); if(!v.isNull()) ui->m_tree_widget->header()->restoreState(v.toByteArray()); } else if (m_element->linkType() & Element::AllReport) { for(Element *elmt : elmt_list) { QStringList search_list; QStringList str_list; if (elmt->conductors().size()) { 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(); } else str_list << "" << "" << ""; if (Diagram *diag = elmt->diagram()) { if (settings.value("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("link-element-widget/report-state"); if(!v.isNull()) ui->m_tree_widget->header()->restoreState(v.toByteArray()); } setUpCompleter(); } /** * @brief LinkSingleElementWidget::setLiveEdit * @param live_edit * @return */ bool LinkSingleElementWidget::setLiveEdit(bool live_edit) { if (m_live_edit == live_edit) return true; m_live_edit = live_edit; return true; } /** * @brief LinkSingleElementWidget::availableElements * @return A QList with all available element * to be linked with the edited element. * This methode take care of the combo box "find in diagram" */ QList LinkSingleElementWidget::availableElements() { QList elmt_list; //if element isn't free and unlink isn't pressed, return an empty list if (!m_element->isFree() && !m_unlink) return elmt_list; if (!m_element->diagram() || !m_element->diagram()->project()) return elmt_list; ElementProvider ep(m_element->diagram()->project()); if (m_filter & Element::AllReport) elmt_list = ep.freeElement(m_filter); else elmt_list = ep.find(m_filter); //If element is linked, remove is parent from the list if(!m_element->isFree()) elmt_list.removeAll(m_element->linkedElements().first()); return elmt_list; } /** * @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; foreach(QStringList strl , m_qtwi_strl_hash.values()) search.append(strl); 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; QSettings settings; if (m_element->linkType() == Element::Slave) { if (settings.value("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 (m_element->linkType() & Element::AllReport) { if (settings.value("genericpanel/folio", false).toBool()) { list << tr("N° de fil") << tr("Fonction") << tr("Tension / Protocole") << tr("Label de folio") << tr("Position") << tr("Titre de folio"); } else { list << tr("N° de fil") << tr("Fonction") << tr("Tension / Protocole") << 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 contain the master element linked to the edited element //we must to wait for this elements be unlinked, else the list of available master isn't up to date QTimer::singleShot(10, this, SLOT(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); 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) { QBrush brush(Qt::white, Qt::NoBrush); for(int i=0 ; i<6 ; i++) { m_pending_qtwi->setBackground(i,brush); } } for (int i=0 ; i<6 ; i++) { m_qtwi_at_context_menu->setBackgroundColor(i, Qt::green); } 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, SIGNAL(destroyed()), this, SLOT(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, SIGNAL(destroyed()), this, SLOT(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 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); }