/* 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 "genericpanel.h" #include #include "qetproject.h" #include "diagram.h" #include "titleblock/templatescollection.h" #include "titleblock/templatelocation.h" #include "qeticons.h" #include "qetapp.h" /** Constructor @param parent Parent QWidget */ GenericPanel::GenericPanel(QWidget *parent) : QTreeWidget(parent), first_activation_(true) { header() -> hide(); setIconSize(QSize(50, 50)); } /** Destructor */ GenericPanel::~GenericPanel() { } /** @return the type of the current item */ int GenericPanel::currentItemType() { QTreeWidgetItem *current_qtwi = currentItem(); if (!current_qtwi) return(0); return(current_qtwi -> type()); } QETProject *GenericPanel::projectForItem(QTreeWidgetItem *item) const { if (item && item -> type() == QET::Project) { return(valueForItem(item)); } return(nullptr); } /** */ Diagram *GenericPanel::diagramForItem(QTreeWidgetItem *item) const { if (item && item -> type() == QET::Diagram) { return(valueForItem(item)); } return(nullptr); } /** */ TitleBlockTemplateLocation GenericPanel::templateLocationForItem(QTreeWidgetItem *item) const { if (item && item -> type() & QET::TitleBlockTemplatesCollectionItem) { return(valueForItem(item)); } return(TitleBlockTemplateLocation()); } /** */ QETProject *GenericPanel::selectedProject() const { return(projectForItem(currentItem())); } /** */ Diagram *GenericPanel::selectedDiagram() const { return(diagramForItem(currentItem())); } /** */ TitleBlockTemplateLocation GenericPanel::selectedTemplateLocation() const { return(templateLocationForItem(currentItem())); } /** */ QTreeWidgetItem *GenericPanel::addProject(QETProject *project, QTreeWidgetItem *parent_item, PanelOptions options) { if (!project) return(nullptr); bool creation_required; QTreeWidgetItem *project_qtwi = getItemForProject(project, &creation_required); updateProjectItem(project_qtwi, project, options, creation_required); reparent(project_qtwi, parent_item); fillProjectItem(project_qtwi, project, options, creation_required); return(project_qtwi); } /** @param project A standard project. @return the tree item representing the provided project or 0 if this project does not appear within this panel. */ QTreeWidgetItem *GenericPanel::itemForProject(QETProject *project) { if (!project) return(nullptr); return(projects_.value(project, nullptr)); } /** @param project A standard project. @param created if provided with a pointer to a boolean, this method will update it to reflect whether the returned item has been freshly created or not. @return the tree item representing the provided project. If it does not appear within this panel, it is created. */ QTreeWidgetItem *GenericPanel::getItemForProject(QETProject *project, bool *created) { if (!project) return(nullptr); QTreeWidgetItem *project_qtwi = projects_.value(project, nullptr); if (project_qtwi) { if (created) *created = false; return(project_qtwi); } project_qtwi = makeItem(QET::Project); if (created) *created = true; return(project_qtwi); } /** */ QTreeWidgetItem *GenericPanel::updateProjectItem(QTreeWidgetItem *project_qtwi, QETProject *project, PanelOptions options, bool freshly_created) { Q_UNUSED(options) if (!project_qtwi || !project) return(nullptr); if (freshly_created) { project_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(project)); projects_.insert(project, project_qtwi); connect( project, SIGNAL(projectInformationsChanged(QETProject *)), this, SLOT (projectInformationsChanged(QETProject *)) ); connect( project, SIGNAL(readOnlyChanged(QETProject *, bool)), this, SLOT (projectInformationsChanged(QETProject *)) ); } // text project_qtwi -> setText(0, project -> pathNameTitle()); // tooltip QString final_tooltip = QDir::toNativeSeparators(project -> filePath()); if (final_tooltip.isEmpty()) { final_tooltip = tr( "Pas de fichier", "tooltip for a file-less project in the element panel" ); } project_qtwi -> setToolTip(0, final_tooltip); QString project_whatsthis = tr("Ceci est un projet QElectroTech, c'est-à-dire un fichier d'extension .qet regroupant plusieurs folios. Il embarque également les éléments et modèles de cartouches utilisés dans ces folios.", "\"What's this\" tip"); project_qtwi -> setWhatsThis(0, project_whatsthis); return(updateItem(project_qtwi, options, freshly_created)); } /** */ QTreeWidgetItem *GenericPanel::fillProjectItem(QTreeWidgetItem *project_qtwi, QETProject *project, PanelOptions options, bool freshly_created) { if (!project_qtwi || !project) return(nullptr); if (options & AddChildDiagrams) { if (freshly_created) { connect( project, SIGNAL(diagramAdded(QETProject *, Diagram *)), this, SLOT (diagramAdded(QETProject *, Diagram *)) ); connect( project, SIGNAL(diagramRemoved(QETProject *, Diagram *)), this, SLOT (diagramRemoved(QETProject *, Diagram *)) ); connect( project, SIGNAL(projectDiagramsOrderChanged(QETProject *, int, int)), this, SLOT (projectDiagramsOrderChanged(QETProject *, int, int)) ); } else { // remove diagrams unknown to the project (presumably removed) removeObsoleteItems(project -> diagrams(), project_qtwi, QET::Diagram, false); } int index = 0; foreach (Diagram *diagram, project -> diagrams()) { QTreeWidgetItem *diagram_qtwi = addDiagram(diagram, nullptr, options); project_qtwi -> insertChild(index, diagram_qtwi); ++ index; } } if (options & AddChildTemplatesCollection) { if (freshly_created) { connect( project, SIGNAL(diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &)), this, SLOT (diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &)) ); } addTemplatesCollection( project -> embeddedTitleBlockTemplatesCollection(), project_qtwi, options ); } return(fillItem(project_qtwi, options, freshly_created)); } /** */ QTreeWidgetItem *GenericPanel::addDiagram(Diagram *diagram, QTreeWidgetItem *parent_item, PanelOptions options) { Q_UNUSED(options) if (!diagram) return(nullptr); bool creation_required; QTreeWidgetItem *diagram_qtwi = getItemForDiagram(diagram, &creation_required); updateDiagramItem(diagram_qtwi, diagram, options, creation_required); reparent(diagram_qtwi, parent_item); fillDiagramItem(diagram_qtwi, diagram, options, creation_required); return(diagram_qtwi); } /** */ QTreeWidgetItem *GenericPanel::getItemForDiagram(Diagram *diagram, bool *created) { if (!diagram) return(nullptr); QTreeWidgetItem *diagram_qtwi = diagrams_.value(diagram, nullptr); if (diagram_qtwi) { if (created) *created = false; return(diagram_qtwi); } diagram_qtwi = makeItem(QET::Diagram); if (created) *created = true; return(diagram_qtwi); } /** */ QTreeWidgetItem *GenericPanel::updateDiagramItem(QTreeWidgetItem *diagram_qtwi, Diagram *diagram, PanelOptions options, bool freshly_created) { Q_UNUSED(options) if (!diagram || !diagram_qtwi) return(nullptr); QSettings settings; QString displayed_title = diagram -> title(); if (displayed_title.isEmpty()) { displayed_title = tr("Folio sans titre", "Fallback label when a diagram has no title"); } if (settings.value("genericpanel/folio", true).toBool()) { QString displayed_label = diagram ->border_and_titleblock.finalfolio(); int diagram_folio_idx = diagram -> folioIndex(); if (diagram_folio_idx != -1) { displayed_label = QString( tr( "%1 - %2", "label displayed for a diagram in the panel ; %1 is the folio index, %2 is the diagram title" ) ).arg(displayed_label).arg(displayed_title); diagram_qtwi -> setText(0, displayed_label); } } else { QString displayed_label; int diagram_folio_idx = diagram -> folioIndex(); if (diagram_folio_idx != -1) { displayed_label = QString( tr( "%1 - %2", "label displayed for a diagram in the panel ; %1 is the folio index, %2 is the diagram title" ) ).arg(diagram_folio_idx + 1).arg(displayed_title); } diagram_qtwi -> setText(0, displayed_label); } if (freshly_created) { diagram_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(diagram)); diagrams_.insert(diagram, diagram_qtwi); connect(diagram, &Diagram::diagramTitleChanged, this, &GenericPanel::diagramTitleChanged); } return(updateItem(diagram_qtwi, options, freshly_created)); } /** */ QTreeWidgetItem *GenericPanel::fillDiagramItem(QTreeWidgetItem *diagram_qtwi, Diagram *diagram, PanelOptions options, bool freshly_created) { Q_UNUSED(diagram) Q_UNUSED(options) Q_UNUSED(freshly_created) return(fillItem(diagram_qtwi, options, freshly_created)); } /** */ QTreeWidgetItem *GenericPanel::addTemplatesCollection(TitleBlockTemplatesCollection *tbt_collection, QTreeWidgetItem *parent_item, PanelOptions options) { if (!tbt_collection) return(nullptr); bool creation_required; QTreeWidgetItem *tbt_collection_qtwi = getItemForTemplatesCollection(tbt_collection, &creation_required); updateTemplatesCollectionItem(tbt_collection_qtwi, tbt_collection, options, creation_required); reparent(tbt_collection_qtwi, parent_item); fillTemplatesCollectionItem(tbt_collection_qtwi, tbt_collection, options, creation_required); return(tbt_collection_qtwi); } /** */ QTreeWidgetItem *GenericPanel::itemForTemplatesCollection(TitleBlockTemplatesCollection *tbt_collection) { if (!tbt_collection) return(nullptr); return(tb_templates_.value(tbt_collection -> location(), nullptr)); } /** */ QTreeWidgetItem *GenericPanel::getItemForTemplatesCollection(TitleBlockTemplatesCollection *tbt_collection, bool *created) { if (!tbt_collection) return(nullptr); QTreeWidgetItem *tbt_collection_item = tb_templates_.value(tbt_collection -> location(), nullptr); if (tbt_collection_item) { if (created) *created = false; return(tbt_collection_item); } tbt_collection_item = makeItem(QET::TitleBlockTemplatesCollection); if (created) *created = true; return(tbt_collection_item); } /** */ QTreeWidgetItem *GenericPanel::updateTemplatesCollectionItem(QTreeWidgetItem *tbt_collection_qtwi, TitleBlockTemplatesCollection *tbt_collection, PanelOptions options, bool freshly_created) { Q_UNUSED(options) QString label = tbt_collection -> title(); if (label.isEmpty()) label = tr("Modèles de cartouche"); tbt_collection_qtwi -> setText(0, label); tbt_collection_qtwi -> setToolTip(0, tbt_collection -> location().toString()); if (freshly_created) { tbt_collection_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(tbt_collection -> location())); tb_templates_.insert(tbt_collection -> location(), tbt_collection_qtwi); } return(updateItem(tbt_collection_qtwi, options, freshly_created)); } /** */ QTreeWidgetItem *GenericPanel::fillTemplatesCollectionItem(QTreeWidgetItem *tbt_collection_qtwi, TitleBlockTemplatesCollection *tbt_collection, PanelOptions options, bool freshly_created) { if (!tbt_collection_qtwi || !tbt_collection) return(tbt_collection_qtwi); if (options & AddChildTemplates) { if (freshly_created) { connect( tbt_collection, SIGNAL(changed(TitleBlockTemplatesCollection*,QString)), this, SLOT(templatesCollectionChanged(TitleBlockTemplatesCollection*, const QString &)) ); if (QETProject *project = tbt_collection -> parentProject()) { connect( project, SIGNAL(diagramUsedTemplate(TitleBlockTemplatesCollection *, const QString &)), this, SLOT (templatesCollectionChanged(TitleBlockTemplatesCollection *, const QString &)) ); } } else { // remove templates unknown to the collection (presumably removed) removeObsoleteItems(tbt_collection -> templatesLocations(), tbt_collection_qtwi, QET::TitleBlockTemplate, false); } int index = 0; foreach (QString template_name, tbt_collection -> templates()) { QTreeWidgetItem *template_item = addTemplate(tbt_collection -> location(template_name), nullptr, options); tbt_collection_qtwi -> insertChild(index ++, template_item); } } return(fillItem(tbt_collection_qtwi, options, freshly_created)); } /** */ QTreeWidgetItem *GenericPanel::addTemplate(const TitleBlockTemplateLocation &tb_template, QTreeWidgetItem *parent_item, PanelOptions options) { if (!tb_template.isValid()) return(nullptr); bool creation_required; QTreeWidgetItem *tb_template_qtwi = getItemForTemplate(tb_template, &creation_required); updateTemplateItem(tb_template_qtwi, tb_template, options, creation_required); reparent(tb_template_qtwi, parent_item); fillTemplateItem(tb_template_qtwi, tb_template, options, creation_required); return(tb_template_qtwi); } /** */ QTreeWidgetItem *GenericPanel::getItemForTemplate(const TitleBlockTemplateLocation &tb_template, bool *created) { if (!tb_template.isValid()) return(nullptr); QTreeWidgetItem *tb_template_qtwi = tb_templates_.value(tb_template, nullptr); if (tb_template_qtwi) { if (created) *created = false; return(tb_template_qtwi); } tb_template_qtwi = makeItem(QET::TitleBlockTemplate); if (created) *created = true; return(tb_template_qtwi); } /** */ QTreeWidgetItem *GenericPanel::updateTemplateItem(QTreeWidgetItem *tb_template_qtwi, const TitleBlockTemplateLocation &tb_template, PanelOptions options, bool freshly_created) { Q_UNUSED(options) tb_template_qtwi -> setText(0, tr("Modèle \"%1\"", "used to display a title block template").arg(tb_template.name())); QString tbt_whatsthis = tr( "Ceci est un modèle de cartouche, qui peut être appliqué à un folio.", "\"What's this\" tip" ); tb_template_qtwi -> setWhatsThis(0, tbt_whatsthis); // note the following lines are technically marking the template as used tb_template_qtwi -> setToolTip(0, tb_template.toString()); tb_template_qtwi -> setBackground(0, QBrush()); // special action for templates that belong to a project if (QETProject *tbt_project = tb_template.parentProject()) { // display unused templates using a red background if (!tbt_project -> usesTitleBlockTemplate(tb_template)) { markItemAsUnused(tb_template_qtwi); } } if (freshly_created) { tb_template_qtwi -> setData(0, GenericPanel::Item, qVariantFromValue(tb_template)); tb_templates_.insert(tb_template, tb_template_qtwi); } return(updateItem(tb_template_qtwi, options, freshly_created)); } /** */ QTreeWidgetItem *GenericPanel::fillTemplateItem(QTreeWidgetItem *tb_template_qtwi, const TitleBlockTemplateLocation &tb_template, PanelOptions options, bool freshly_created) { Q_UNUSED(tb_template) Q_UNUSED(options) Q_UNUSED(freshly_created) return(fillItem(tb_template_qtwi, options, freshly_created)); } /** This generic method is called at the end of each update*Item method. Its only purpose is being reimplemented in a subclass. The default implementation does nothing. */ QTreeWidgetItem *GenericPanel::updateItem(QTreeWidgetItem *qtwi, PanelOptions options, bool freshly_created) { Q_UNUSED(qtwi); Q_UNUSED(options); Q_UNUSED(freshly_created); QApplication::processEvents(); return(qtwi); } /** This generic method is called at the end of each fill*Item method. Its only purpose is being reimplemented in a subclass. The default implementation does nothing. */ QTreeWidgetItem *GenericPanel::fillItem(QTreeWidgetItem *qtwi, PanelOptions options, bool freshly_created) { Q_UNUSED(qtwi); Q_UNUSED(options); Q_UNUSED(freshly_created); return(qtwi); } /** */ void GenericPanel::projectInformationsChanged(QETProject *project) { addProject(project, nullptr, nullptr); emit(panelContentChanged()); } /** */ void GenericPanel::diagramAdded(QETProject *project, Diagram *diagram) { Q_UNUSED(diagram) addProject(project, nullptr, GenericPanel::AddChildDiagrams); emit(panelContentChanged()); } /** */ void GenericPanel::diagramRemoved(QETProject *project, Diagram *diagram) { Q_UNUSED(diagram) addProject(project, nullptr, GenericPanel::AddChildDiagrams); emit(panelContentChanged()); } /** * @brief GenericPanel::projectDiagramsOrderChanged * @param project project where diagram moved * @param from Index of diagram before move * @param to Index of diagram after move */ void GenericPanel::projectDiagramsOrderChanged(QETProject *project, int from, int to) { // get the item representing the provided project QTreeWidgetItem *qtwi_project = itemForProject(project); if (!qtwi_project) return; // get the item representing the moved diagram QTreeWidgetItem *moved_qtwi_diagram = qtwi_project -> child(from); if (!moved_qtwi_diagram) return; // remove the QTWI then insert it back at the adequate location bool was_selected = moved_qtwi_diagram -> isSelected(); qtwi_project -> removeChild (moved_qtwi_diagram); qtwi_project -> insertChild (to, moved_qtwi_diagram); // update the QTWI labels because they may display the folio index for (int i = qMin(from, to); i < qMax(from, to) + 1; i++) { QTreeWidgetItem *qtwi_diagram = qtwi_project -> child(i); if (!qtwi_diagram) continue; Diagram *diagram = valueForItem(qtwi_diagram); if (diagram) updateDiagramItem(qtwi_diagram, diagram); } if (was_selected) setCurrentItem(moved_qtwi_diagram); emit(panelContentChanged()); } /** Inform this panel the diagram \a diagram has changed its title to \a title. */ void GenericPanel::diagramTitleChanged(Diagram *diagram, const QString &title) { Q_UNUSED(title) GenericPanel::addDiagram(diagram); emit(panelContentChanged()); } /** @param collection Title block templates collection that changed and should be updated @param template_name Name of the changed template (unused) */ void GenericPanel::templatesCollectionChanged(TitleBlockTemplatesCollection*collection, const QString &template_name) { Q_UNUSED(template_name) addTemplatesCollection(collection); emit(panelContentChanged()); } /** */ void GenericPanel::diagramUsedTemplate(TitleBlockTemplatesCollection *collection, const QString &name) { Q_UNUSED(collection) Q_UNUSED(name) addTemplatesCollection(collection); emit(panelContentChanged()); } /** */ QString GenericPanel::defaultText(QET::ItemType type) { switch(type) { case QET::ElementsCollectionItem: case QET::Element: return("element"); case QET::ElementsContainer: case QET::ElementsCategory: return("elements category"); case QET::ElementsCollection: return("elements collection"); case QET::TitleBlockTemplatesCollectionItem: case QET::TitleBlockTemplate: return("title block template"); case QET::TitleBlockTemplatesCollection: return("title block templates collection"); case QET::Diagram: return("diagram"); case QET::Project: return("project"); default: return(QString()); } return(QString()); } /** @param type Item type we want the default icon for @return the default icon for \a type */ QIcon GenericPanel::defaultIcon(QET::ItemType type) { if (type & QET::ElementsContainer) { return(QET::Icons::Folder); } else if (type & QET::TitleBlockTemplatesCollectionItem) { return(QIcon(QET::Icons::TitleBlock.pixmap(QSize(16, 16)))); } else if (type == QET::Diagram) { return(QET::Icons::Diagram); } else if (type == QET::Project) { return(QIcon(QET::Icons::ProjectFileGP.pixmap(QSize(16, 16)))); } return(QIcon()); } /** Create a QTreeWidgetItem @param parent Parent for the created item @param type Item type (e.g QET::Diagram, QET::Project, ...) @param label Label for the created item @param icon Icon for the created item @return the create QTreeWidgetItem */ QTreeWidgetItem *GenericPanel::makeItem(QET::ItemType type, QTreeWidgetItem *parent, const QString &label, const QIcon &icon) { QTreeWidgetItem *qtwi = new QTreeWidgetItem(parent, type); qtwi -> setText(0, label.isEmpty() ? defaultText(type) : label); qtwi -> setIcon(0, icon.isNull() ? defaultIcon(type) : icon); return(qtwi); } /** Delete and item and its children. @param item item to delete @param deleted_on_cascade true if the item is not being directly deleted but is undergoing the deletion of its parent. */ void GenericPanel::deleteItem(QTreeWidgetItem *item, bool deleted_on_cascade) { Q_UNUSED(deleted_on_cascade) // recursively delete child items first for (int i = item -> childCount() - 1 ; i >= 0 ; -- i) { deleteItem(item -> child(i), true); } // delete the item itself unregisterItem(item); delete item; } /** Mark the provided QTreeWidgetItem as unused in its parent project. @param qtwi A QTreeWidgetItem */ void GenericPanel::markItemAsUnused(QTreeWidgetItem *qtwi) { QLinearGradient t(0, 0, 200, 0); t.setColorAt(0, QColor("#ffc0c0")); t.setColorAt(1, QColor("#ffffff")); qtwi -> setBackground(0, QBrush(t)); qtwi -> setToolTip(0, QString(tr("%1 [non utilisé dans le projet]")).arg(qtwi -> toolTip(0))); } /** */ void GenericPanel::reparent(QTreeWidgetItem *item, QTreeWidgetItem *parent) { if (parent && item -> parent() != parent) { parent -> addChild(item); } } /** @return the child items of \a item of type \a type @param item Parent item that will be searched. @param type Type of items to look for. @param recursive Whether to search recursively. */ QList GenericPanel::childItems(QTreeWidgetItem *item, QET::ItemType type, bool recursive) const { QList items; if (!item) return(items); for (int i = 0 ; i < item -> childCount() ; ++ i) { QTreeWidgetItem *current_item = item -> child(i); if (!current_item) continue; if (current_item -> type() == type) { items << current_item; } if (recursive) { items << childItems(current_item, type, true); } } return(items); } /** This variant of childItems() removes any child considered obsolete, i.e. not found in \a expected_items. @param expected_items A list of non-obsolete values @param item Parent item that will be searched. @param type Type of items to look for. @param recursive Whether to search recursively. @see GenericPanel::childItems() */ template void GenericPanel::removeObsoleteItems(const QList &expected_items, QTreeWidgetItem *item, QET::ItemType type, bool recursive) { // remove items not found in expected_items foreach (QTreeWidgetItem *child_item, childItems(item, type, recursive)) { T child_value = valueForItem(child_item); if (!expected_items.contains(child_value)) { deleteItem(child_item); } } } /** @return the value stored in \a item */ template T GenericPanel::valueForItem(QTreeWidgetItem *item) const { return item -> data(0, GenericPanel::Item).value(); } template TitleBlockTemplateLocation GenericPanel::valueForItem(QTreeWidgetItem *item) const; template ElementsLocation GenericPanel::valueForItem(QTreeWidgetItem*) const; template QETProject* GenericPanel::valueForItem(QTreeWidgetItem*) const; template Diagram* GenericPanel::valueForItem(QTreeWidgetItem*) const; /** */ void GenericPanel::unregisterItem(QTreeWidgetItem *item) { if (!item) return; int type = item ->type(); if (type & QET::TitleBlockTemplatesCollectionItem) { tb_templates_.remove(valueForItem(item)); } else if (type == QET::Diagram) { diagrams_.remove(valueForItem(item)); } else if (type == QET::Project) { projects_.remove(valueForItem(item)); } } /** Handle various events; reimplemented here to emit the signal firstActivated(). */ bool GenericPanel::event(QEvent *event) { if (first_activation_) { if (event -> type() == QEvent::WindowActivate || event -> type() == QEvent::Show) { QTimer::singleShot(250, this, SLOT(emitFirstActivated())); first_activation_ = false; } } return(QTreeWidget::event(event)); } /** Emit the signal firstActivated(). */ void GenericPanel::emitFirstActivated() { emit(firstActivated()); }