1 /*
2 Copyright 2006-2019 The QElectroTech Team
3 This file is part of QElectroTech.
4
5 QElectroTech is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 (at your option) any later version.
9
10 QElectroTech is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "linksingleelementwidget.h"
19 #include "ui_linksingleelementwidget.h"
20 #include "diagram.h"
21 #include "elementprovider.h"
22 #include "linkelementcommand.h"
23 #include "diagramposition.h"
24 #include "conductor.h"
25
26 #include <QTreeWidgetItem>
27
28 /**
29 * @brief LinkSingleElementWidget::LinkSingleElementWidget
30 * Default constructor
31 * @param elmt
32 * the edited element
33 * @param parent
34 * the parent widget
35 */
LinkSingleElementWidget(Element * elmt,QWidget * parent)36 LinkSingleElementWidget::LinkSingleElementWidget(Element *elmt, QWidget *parent) :
37 AbstractElementPropertiesEditorWidget(parent),
38 ui(new Ui::LinkSingleElementWidget)
39 {
40 ui->setupUi(this);
41
42 ui->m_tree_widget->setContextMenuPolicy(Qt::CustomContextMenu);
43 m_context_menu = new QMenu(this);
44 m_link_action = new QAction(tr("Lier l'élément"), this);
45 m_show_qtwi = new QAction(tr("Montrer l'élément"), this);
46 m_show_element = new QAction(tr("Montrer l'élément esclave"), this);
47 m_save_header_state = new QAction(tr("Enregistrer la disposition"), this);
48
49 connect(m_show_qtwi, &QAction::triggered, [this]() {this->on_m_tree_widget_itemDoubleClicked(this->m_qtwi_at_context_menu, 0);});
50 connect(m_link_action, &QAction::triggered, this, &LinkSingleElementWidget::linkTriggered);
51
52 connect(m_show_element, &QAction::triggered, [this]()
53 {
54 this->m_element->diagram()->showMe();
55 this->m_element->setHighlighted(true);
56 if(this->m_showed_element)
57 m_showed_element->setHighlighted(false);
58 });
59
60 QHeaderView *qhv = ui->m_tree_widget->header();
61 qhv->setContextMenuPolicy(Qt::CustomContextMenu);
62 connect(qhv, &QHeaderView::customContextMenuRequested, this, &LinkSingleElementWidget::headerCustomContextMenuRequested);
63 connect(m_save_header_state, &QAction::triggered, [this, qhv]()
64 {
65 QByteArray qba = qhv->saveState();
66 QSettings settings;
67
68 if (this->m_element->linkType() & Element::AllReport)
69 settings.setValue("link-element-widget/report-state", qba);
70 else if (this->m_element->linkType() == Element::Slave)
71 settings.setValue("link-element-widget/slave-state", qba);
72 });
73
74 setElement(elmt);
75 }
76
77 /**
78 * @brief LinkSingleElementWidget::~LinkSingleElementWidget
79 * Default destructor
80 */
~LinkSingleElementWidget()81 LinkSingleElementWidget::~LinkSingleElementWidget()
82 {
83 if(m_showed_element)
84 m_showed_element->setHighlighted(false);
85
86 if(m_element)
87 {
88 m_element->setHighlighted(false);
89 if (!m_element->isFree())
90 m_element->linkedElements().first()->setHighlighted(false);
91 }
92 delete ui;
93 }
94
95 /**
96 * @brief LinkSingleElementWidget::setElement
97 * Set element to be the edited element.
98 * @param element
99 */
setElement(Element * element)100 void LinkSingleElementWidget::setElement(Element *element)
101 {
102 if (m_element == element)
103 return;
104
105 //Remove connection of previous edited element
106 if (m_element)
107 {
108 disconnect(m_element->diagram()->project(), &QETProject::diagramRemoved, this, &LinkSingleElementWidget::diagramWasRemovedFromProject);
109 disconnect(m_element.data(), &Element::linkedElementChanged, this, &LinkSingleElementWidget::updateUi);
110 m_element->setHighlighted(false);
111 }
112
113 if(m_showed_element)
114 m_showed_element->setHighlighted(false);
115
116 m_unlink = false;
117 m_showed_element = nullptr;
118 m_element_to_link = nullptr;
119 m_pending_qtwi = nullptr;
120
121 //Setup the new element, connection and ui
122 m_element = element;
123
124 if (m_element->linkType() & Element::Slave)
125 m_filter = Element::Master;
126 else if (m_element->linkType() & Element::AllReport)
127 m_filter = m_element->linkType() == Element::NextReport? Element::PreviousReport : Element::NextReport;
128 else
129 m_filter = Element::Simple;
130
131 connect(m_element->diagram()->project(), &QETProject::diagramRemoved, this, &LinkSingleElementWidget::diagramWasRemovedFromProject);
132 connect(m_element.data(), &Element::linkedElementChanged, this, &LinkSingleElementWidget::updateUi, Qt::QueuedConnection);
133
134 updateUi();
135 }
136
137 /**
138 * @brief LinkSingleElementWidget::apply
139 * Apply the new property of the edited element by pushing
140 * the associated undo command to parent project undo stack
141 */
apply()142 void LinkSingleElementWidget::apply()
143 {
144 QUndoCommand *undo = associatedUndo();
145 if (undo)
146 m_element->diagram()->undoStack().push(undo);
147
148 m_unlink = false;
149 m_element_to_link = nullptr;
150 m_pending_qtwi = nullptr;
151 }
152
153 /**
154 * @brief LinkSingleElementWidget::associatedUndo
155 * @return the undo command associated to the current edition
156 * if there isn't change, return nulptr
157 */
associatedUndo() const158 QUndoCommand *LinkSingleElementWidget::associatedUndo() const
159 {
160 LinkElementCommand *undo = new LinkElementCommand(m_element);
161
162 if (m_element_to_link || m_unlink)
163 {
164 if (m_element_to_link)
165 undo->setLink(m_element_to_link);
166 else if (m_unlink)
167 undo->unlinkAll();
168
169 return undo;
170 }
171
172 return nullptr;
173 }
174
175 /**
176 * @brief LinkSingleElementWidget::title
177 * @return the title used for this editor
178 */
title() const179 QString LinkSingleElementWidget::title() const
180 {
181 if (m_element->linkType() & Element::AllReport)
182 return tr("Report de folio");
183 else
184 return tr("Référence croisée (esclave)");
185 }
186
187 /**
188 * @brief LinkSingleElementWidget::updateUi
189 * Update the content of this widget
190 */
updateUi()191 void LinkSingleElementWidget::updateUi()
192 {
193 m_unlink = false;
194
195 //Update the behavior of link/unlink button
196 if (m_element->isFree())
197 hideButtons();
198 else
199 showButtons();
200
201 buildTree();
202 }
203
204 /**
205 * @brief LinkSingleElementWidget::buildTree
206 * Build the content of the QTreeWidget
207 */
buildTree()208 void LinkSingleElementWidget::buildTree()
209 {
210 clearTreeWidget();
211 setUpHeaderLabels();
212 QSettings settings;
213
214 const QList <Element *> elmt_list = availableElements();
215 if (m_element->linkType() == Element::Slave)
216 {
217
218 for(Element *elmt : elmt_list)
219 {
220 QStringList search_list;
221 QStringList str_list;
222
223 QString formula = elmt->elementInformations()["formula"].toString();
224 if(!formula.isEmpty())
225 {
226 str_list << autonum::AssignVariables::formulaToLabel(formula, elmt->rSequenceStruct(), elmt->diagram(), elmt);
227 search_list << str_list.last();
228 }
229 else
230 {
231 str_list << elmt->elementInformations()["label"].toString();
232 if(!str_list.last().isEmpty())
233 search_list << str_list.last();
234 }
235
236 str_list << elmt->elementInformations()["comment"].toString();
237 if (!str_list.last().isEmpty())
238 search_list << str_list.last();
239
240 if (Diagram *diag = elmt->diagram())
241 {
242 if (settings.value("genericpanel/folio", false).toBool())
243 {
244 autonum::sequentialNumbers seq;
245 QString F =autonum::AssignVariables::formulaToLabel(diag->border_and_titleblock.folio(), seq, diag, elmt);
246 str_list << F;
247 }
248 else
249 {
250 str_list << QString::number(diag->folioIndex() + 1);
251 }
252 str_list << diag->convertPosition(elmt->scenePos()).toString();
253 str_list << diag->title();
254 }
255 else
256 {
257 qDebug() << "In method void LinkSingleElementWidget::updateUi(), provided element must be in a diagram";
258 }
259
260 QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_tree_widget, str_list);
261 m_qtwi_elmt_hash.insert(qtwi, elmt);
262 m_qtwi_strl_hash.insert(qtwi, search_list);
263 }
264
265
266 QVariant v = settings.value("link-element-widget/slave-state");
267 if(!v.isNull())
268 ui->m_tree_widget->header()->restoreState(v.toByteArray());
269 }
270
271 else if (m_element->linkType() & Element::AllReport)
272 {
273 for(Element *elmt : elmt_list)
274 {
275 QStringList search_list;
276 QStringList str_list;
277
278 if (elmt->conductors().size())
279 {
280 ConductorProperties cp = elmt->conductors().first()->properties();
281 str_list << cp.text;
282 if (!str_list.last().isEmpty())
283 search_list << str_list.last();
284 str_list << cp.m_function;
285 if (!str_list.last().isEmpty())
286 search_list << str_list.last();
287 str_list << cp.m_tension_protocol;
288 if (!str_list.last().isEmpty())
289 search_list << str_list.last();
290 }
291 else
292 str_list << "" << "" << "";
293
294 if (Diagram *diag = elmt->diagram())
295 {
296 if (settings.value("genericpanel/folio", false).toBool())
297 {
298 autonum::sequentialNumbers seq;
299 QString F =autonum::AssignVariables::formulaToLabel(diag->border_and_titleblock.folio(), seq, diag, elmt);
300 str_list << F;
301 }
302 else
303 {
304 str_list << QString::number(diag->folioIndex() + 1);
305 }
306 str_list << diag->convertPosition(elmt->scenePos()).toString();
307 str_list << diag->title();
308 }
309 else
310 {
311 qDebug() << "In method void LinkSingleElementWidget::updateUi(), provided element must be in a diagram";
312 }
313
314 QTreeWidgetItem *qtwi = new QTreeWidgetItem(ui->m_tree_widget, str_list);
315 m_qtwi_elmt_hash.insert(qtwi, elmt);
316 m_qtwi_strl_hash.insert(qtwi, search_list);
317 }
318
319 QSettings settings;
320 QVariant v = settings.value("link-element-widget/report-state");
321 if(!v.isNull())
322 ui->m_tree_widget->header()->restoreState(v.toByteArray());
323 }
324
325 setUpCompleter();
326 }
327
328 /**
329 * @brief LinkSingleElementWidget::setLiveEdit
330 * @param live_edit
331 * @return
332 */
setLiveEdit(bool live_edit)333 bool LinkSingleElementWidget::setLiveEdit(bool live_edit)
334 {
335 if (m_live_edit == live_edit)
336 return true;
337
338 m_live_edit = live_edit;
339
340 return true;
341 }
342
343 /**
344 * @brief LinkSingleElementWidget::availableElements
345 * @return A QList with all available element
346 * to be linked with the edited element.
347 * This methode take care of the combo box "find in diagram"
348 */
availableElements()349 QList <Element *> LinkSingleElementWidget::availableElements()
350 {
351 QList <Element *> elmt_list;
352 //if element isn't free and unlink isn't pressed, return an empty list
353 if (!m_element->isFree() && !m_unlink)
354 return elmt_list;
355
356 if (!m_element->diagram() || !m_element->diagram()->project()) return elmt_list;
357
358 ElementProvider ep(m_element->diagram()->project());
359 if (m_filter & Element::AllReport)
360 elmt_list = ep.freeElement(m_filter);
361 else
362 elmt_list = ep.find(m_filter);
363
364 //If element is linked, remove is parent from the list
365 if(!m_element->isFree()) elmt_list.removeAll(m_element->linkedElements().first());
366
367 return elmt_list;
368 }
369
370 /**
371 * @brief LinkSingleElementWidget::setUpCompleter
372 * Setup the completer of search_field
373 */
setUpCompleter()374 void LinkSingleElementWidget::setUpCompleter()
375 {
376 ui->m_search_field->clear();
377 if(ui->m_search_field->completer())
378 delete ui->m_search_field->completer();
379
380 QStringList search;
381 foreach(QStringList strl , m_qtwi_strl_hash.values())
382 search.append(strl);
383
384 QCompleter *c = new QCompleter(search, ui->m_search_field);
385 c->setCaseSensitivity(Qt::CaseInsensitive);
386 ui->m_search_field->setCompleter(c);
387 }
388
389 /**
390 * @brief LinkSingleElementWidget::clearTreeWidget
391 * Clear the tree widget.
392 * Delete all QTreeWidget (in the tree widget and in the hash).
393 * Clear the hash.
394 */
clearTreeWidget()395 void LinkSingleElementWidget::clearTreeWidget()
396 {
397 while(ui->m_tree_widget->topLevelItemCount())
398 {
399 QTreeWidgetItem *qtwi = ui->m_tree_widget->takeTopLevelItem(0);
400 if (!m_qtwi_elmt_hash.contains(qtwi))
401 delete qtwi;
402 }
403
404 foreach(QTreeWidgetItem *qtwi, m_qtwi_elmt_hash.keys())
405 delete qtwi;
406
407 m_qtwi_elmt_hash.clear();
408 m_qtwi_strl_hash.clear();
409 }
410
setUpHeaderLabels()411 void LinkSingleElementWidget::setUpHeaderLabels()
412 {
413 QStringList list;
414 QSettings settings;
415
416 if (m_element->linkType() == Element::Slave)
417 {
418 if (settings.value("genericpanel/folio", false).toBool())
419 {
420 list << tr("Label") << tr("Commentaire") << tr("Label de folio") << tr("Position") << tr("Titre de folio");
421 }
422 else
423 {
424 list << tr("Label") << tr("Commentaire") << tr("N° de folio") << tr("Position") << tr("Titre de folio");
425 }
426 }
427
428 if (m_element->linkType() & Element::AllReport)
429 {
430 if (settings.value("genericpanel/folio", false).toBool())
431 {
432 list << tr("N° de fil") << tr("Fonction") << tr("Tension / Protocole") << tr("Label de folio") << tr("Position") << tr("Titre de folio");
433 }
434 else
435 {
436 list << tr("N° de fil") << tr("Fonction") << tr("Tension / Protocole") << tr("N° de folio") << tr("Position") << tr("Titre de folio");
437 }
438 }
439
440 ui->m_tree_widget->setHeaderLabels(list);
441 }
442
443 /**
444 * @brief LinkSingleElementWidget::diagramWasRemovedFromProject
445 * * This slot is called when a diagram is removed from the parent project of edited element
446 * to update the content of this widget
447 */
diagramWasRemovedFromProject()448 void LinkSingleElementWidget::diagramWasRemovedFromProject()
449 {
450 //We use a timer because if the removed diagram contain the master element linked to the edited element
451 //we must to wait for this elements be unlinked, else the list of available master isn't up to date
452 QTimer::singleShot(10, this, SLOT(updateUi()));
453 }
454
showedElementWasDeleted()455 void LinkSingleElementWidget::showedElementWasDeleted()
456 {
457 m_showed_element = nullptr;
458 }
459
460 /**
461 * @brief LinkSingleElementWidget::linkTriggered
462 * Action linkis triggered
463 */
linkTriggered()464 void LinkSingleElementWidget::linkTriggered()
465 {
466 if(!m_qtwi_at_context_menu)
467 return;
468
469 m_element_to_link = m_qtwi_elmt_hash.value(m_qtwi_at_context_menu);
470
471 if(m_live_edit)
472 {
473 apply();
474 updateUi();
475 }
476 else
477 {
478 //In no live edit mode, we set the background of the qtwi green, to inform the user
479 //which element will be linked when he press the apply button
480 if (m_pending_qtwi)
481 {
482 QBrush brush(Qt::white, Qt::NoBrush);
483 for(int i=0 ; i<6 ; i++)
484 {
485 m_pending_qtwi->setBackground(i,brush);
486 }
487 }
488
489 for (int i=0 ; i<6 ; i++)
490 {
491 m_qtwi_at_context_menu->setBackgroundColor(i, Qt::green);
492 }
493 m_pending_qtwi = m_qtwi_at_context_menu;
494 }
495
496 }
497
498 /**
499 * @brief LinkSingleElementWidget::hideButtons
500 * Hide the button displayed when element is already linked
501 */
hideButtons()502 void LinkSingleElementWidget::hideButtons()
503 {
504 ui->m_label->hide();
505 ui->m_unlink_pb->hide();
506 ui->m_show_linked_pb->hide();
507 ui->m_show_this_pb->hide();
508 ui->m_search_field->show();
509 }
510
511 /**
512 * @brief LinkSingleElementWidget::showButtons
513 * Show the button displayed when element is already linked
514 */
showButtons()515 void LinkSingleElementWidget::showButtons()
516 {
517 ui->m_label->show();
518 ui->m_unlink_pb->show();
519 ui->m_show_linked_pb->show();
520 ui->m_show_this_pb->show();
521 ui->m_search_field->hide();
522 }
523
headerCustomContextMenuRequested(const QPoint & pos)524 void LinkSingleElementWidget::headerCustomContextMenuRequested(const QPoint &pos)
525 {
526 m_context_menu->clear();
527 m_context_menu->addAction(m_save_header_state);
528 m_context_menu->popup(ui->m_tree_widget->header()->mapToGlobal(pos));
529 }
530
on_m_unlink_pb_clicked()531 void LinkSingleElementWidget::on_m_unlink_pb_clicked()
532 {
533 m_unlink = true;
534
535 if(m_live_edit)
536 {
537 apply();
538 updateUi();
539 }
540 else
541 buildTree();
542 }
543
544 /**
545 * @brief LinkSingleElementWidget::on_m_tree_widget_itemDoubleClicked
546 * Highlight the element represented by @item
547 * @param item
548 * @param column
549 */
on_m_tree_widget_itemDoubleClicked(QTreeWidgetItem * item,int column)550 void LinkSingleElementWidget::on_m_tree_widget_itemDoubleClicked(QTreeWidgetItem *item, int column)
551 {
552 Q_UNUSED(column);
553
554 if (m_showed_element)
555 {
556 disconnect(m_showed_element, SIGNAL(destroyed()), this, SLOT(showedElementWasDeleted()));
557 m_showed_element->setHighlighted(false);
558 }
559
560 Element *elmt = m_qtwi_elmt_hash.value(item);
561 elmt->diagram()->showMe();
562 elmt->setHighlighted(true);
563 m_showed_element = elmt;
564 connect(m_showed_element, SIGNAL(destroyed()), this, SLOT(showedElementWasDeleted()));
565
566 }
567
on_m_tree_widget_customContextMenuRequested(const QPoint & pos)568 void LinkSingleElementWidget::on_m_tree_widget_customContextMenuRequested(const QPoint &pos)
569 {
570 //add the size of the header to display the topleft of the QMenu at the position of the mouse.
571 //See doc about QWidget::customContextMenuRequested section related to QAbstractScrollArea
572 QPoint point = pos;
573 point.ry()+=ui->m_tree_widget->header()->height();
574 point = ui->m_tree_widget->mapToGlobal(point);
575
576 m_context_menu->clear();
577
578 if (ui->m_tree_widget->currentItem())
579 {
580 m_qtwi_at_context_menu = ui->m_tree_widget->currentItem();
581 m_context_menu->addAction(m_link_action);
582 m_context_menu->addAction(m_show_qtwi);
583 }
584
585 m_context_menu->addAction(m_show_element);
586 m_context_menu->popup(point);
587 }
588
on_m_show_linked_pb_clicked()589 void LinkSingleElementWidget::on_m_show_linked_pb_clicked()
590 {
591 if (!m_element->isFree())
592 {
593 Element *elmt = m_element->linkedElements().first();
594 elmt->diagram()->showMe();
595 elmt->setHighlighted(true);
596 }
597 }
598
on_m_show_this_pb_clicked()599 void LinkSingleElementWidget::on_m_show_this_pb_clicked()
600 {
601 m_show_element->trigger();
602 }
603
604 /**
605 * @brief LinkSingleElementWidget::on_m_search_field_textEdited
606 * Search all items which match with @arg1 and shows it, other items is hidden.
607 * If @arg1 is empty, show all items.
608 * @param arg1
609 */
on_m_search_field_textEdited(const QString & arg1)610 void LinkSingleElementWidget::on_m_search_field_textEdited(const QString &arg1)
611 {
612 //Show all items if arg1 is empty, if not hide all items
613 foreach(QTreeWidgetItem *qtwi, m_qtwi_elmt_hash.keys())
614 qtwi->setHidden(!arg1.isEmpty());
615
616 QList <QTreeWidgetItem *> qtwi_list;
617
618 foreach(QTreeWidgetItem *qtwi, m_qtwi_strl_hash.keys())
619 {
620 foreach(QString str, m_qtwi_strl_hash.value(qtwi))
621 {
622 if(str.contains(arg1, Qt::CaseInsensitive))
623 {
624 qtwi_list << qtwi;
625 continue;
626 }
627 }
628 }
629
630 //Show items which match with arg1
631 foreach(QTreeWidgetItem *qtwi, qtwi_list)
632 qtwi->setHidden(false);
633 }
634