template_editors = QETApp::titleBlockTemplateEditors(m_project);
foreach(QETTitleBlockTemplateEditor *template_editor, template_editors) {
if (!template_editor -> close()) return(false);
}
return(true);
}
/**
@brief ProjectView::tryClosingDiagrams
try to close this project, if diagram or project option are changed
a dialog ask if user want to save the modification.
@return the answer of dialog or discard if no change.
*/
int ProjectView::tryClosingDiagrams()
{
if (!m_project) return(QMessageBox::Discard);
if (!project()->projectOptionsWereModified() &&
project()->undoStack()->isClean() &&
!project()->filePath().isEmpty()) {
// nothing was modified, and we have a filepath, i.e. everything was already
// saved, i.e we can close the project right now
return(QMessageBox::Discard);
}
QString title = project()->title();
if (title.isEmpty()) title = "QElectroTech ";
int close_dialog = QMessageBox::question(this, title,
tr("Le projet à été modifié.\n"
"Voulez-vous enregistrer les modifications ?"),
QMessageBox::Save | QMessageBox::Discard
| QMessageBox::Cancel,
QMessageBox::Save);
return(close_dialog);
}
/**
Ask the user to provide a file path in which the currently edited project will
be saved.
@param assign When true, assign the provided filepath to the project through
setFilePath(). Defaults to true.
@return the file path, or an empty string if none were provided
*/
QString ProjectView::askUserForFilePath(bool assign) {
// ask the user for a filepath in order to save the project
QString filepath = QFileDialog::getSaveFileName(
this,
tr("Enregistrer sous", "dialog title"),
m_project -> currentDir() + "/" + tr("sansnom") + ".qet",
tr("Projet QElectroTech (*.qet)", "filetypes allowed when saving a project file")
);
// if no filepath is provided, return an empty string
if (filepath.isEmpty()) return(filepath);
// if the name does not end with the .qet extension and we're _not_ using xdg-desktop-portal, append it
bool usesPortal =
qEnvironmentVariableIsSet("FLATPAK_ID") ||
qEnvironmentVariableIsSet("SNAP_NAME");
if (!filepath.endsWith(".qet", Qt::CaseInsensitive) && !usesPortal) filepath += ".qet";
if (assign) {
// assign the provided filepath to the currently edited project
m_project -> setFilePath(filepath);
}
return(filepath);
}
/**
@return the QETResult object to be returned when it appears this project
view is not associated to any project.
*/
QETResult ProjectView::noProjectResult() const
{
QETResult no_project(tr("aucun projet affiché", "error message"), false);
return(no_project);
}
/**
* @brief ProjectView::removeDiagram
* Remove a diagram (folio) of the project
* @param diagram_view : diagram view to remove
* @param silent : if true, bypasses the confirmation message box
*/
void ProjectView::removeDiagram(DiagramView *diagram_view, bool silent)
{
if (!diagram_view)
return;
if (m_project -> isReadOnly())
return;
if (!m_diagram_ids.values().contains(diagram_view))
return;
if (!silent) {
//Ask confirmation to user.
int answer = QET::QetMessageBox::question(
this,
tr("Supprimer le folio ?", "message box title"),
tr("Êtes-vous sûr de vouloir supprimer ce folio du projet ?", "message box content"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
QMessageBox::No
);
if (answer != QMessageBox::Yes) {
return;
}
}
m_project->undoStack()->push(new RemoveDiagramCommand(m_project, diagram_view->diagram()));
}
/**
@brief ProjectView::diagramRemoved
Slot called when the project emits diagramRemoved (either from a direct,
non-undoable QETProject::removeDiagram() call, or -- the normal UI path
-- from a RemoveDiagramCommand's redo()/undo()). Tears down the tab and
DiagramView for the given diagram; the diagram itself is left untouched,
since by this point it has already been detached (and possibly parked
for undo) by the caller.
@param diagram
*/
void ProjectView::diagramRemoved(Diagram *diagram)
{
DiagramView *diagram_view = findDiagram(diagram);
if (!diagram_view)
return;
int index_to_remove = m_diagram_ids.key(diagram_view);
m_tab->removeTab(index_to_remove);
m_diagram_view_list.removeAll(diagram_view);
rebuildDiagramsMap();
emit(diagramRemoved(diagram_view));
delete diagram_view;
updateAllTabsTitle();
m_project -> setModified(true);
}
/**
* Enleve un schema du ProjectView
* @param diagram Schema a enlever
* @param silent Si vrai, supprime sans demander confirmation
*/
void ProjectView::removeDiagram(Diagram *diagram, bool silent) {
if (!diagram) return;
if (DiagramView *diagram_view = findDiagram(diagram)) {
removeDiagram(diagram_view, silent);
}
}
/**
Active l'onglet adequat pour afficher le schema passe en parametre
@param diagram Schema a afficher
*/
void ProjectView::showDiagram(DiagramView *diagram) {
if (!diagram) return;
m_tab -> setCurrentWidget(diagram);
}
/**
Active l'onglet adequat pour afficher le schema passe en parametre
@param diagram Schema a afficher
*/
void ProjectView::showDiagram(Diagram *diagram) {
if (!diagram) return;
if (DiagramView *diagram_view = findDiagram(diagram)) {
m_tab -> setCurrentWidget(diagram_view);
}
}
/**
Enable the user to edit properties of the current project through a
configuration dialog.
*/
void ProjectView::editProjectProperties()
{
if (!m_project) return;
ProjectPropertiesDialog dialog(m_project, parentWidget());
dialog.exec();
}
/**
Edite les proprietes du schema courant
*/
void ProjectView::editCurrentDiagramProperties()
{
editDiagramProperties(currentDiagram());
}
/**
Edite les proprietes du schema diagram_view
*/
void ProjectView::editDiagramProperties(DiagramView *diagram_view) {
if (!diagram_view) return;
showDiagram(diagram_view);
diagram_view -> editDiagramProperties();
}
/**
Edite les proprietes du schema diagram
*/
void ProjectView::editDiagramProperties(Diagram *diagram) {
editDiagramProperties(findDiagram(diagram));
}
/**
Deplace le schema diagram_view vers le haut / la gauche
*/
void ProjectView::moveDiagramUp(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (!diagram_view_position) {
// le schema est le premier du projet
return;
}
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position - 1));
}
/**
Deplace le schema diagram vers le haut / la gauche
*/
void ProjectView::moveDiagramUp(Diagram *diagram) {
moveDiagramUp(findDiagram(diagram));
}
/**
Deplace le schema diagram_view vers le bas / la droite
*/
void ProjectView::moveDiagramDown(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (diagram_view_position + 1 == m_diagram_ids.count()) {
// le schema est le dernier du projet
return;
}
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position + 1));
}
/**
Deplace le schema diagram vers le bas / la droite
*/
void ProjectView::moveDiagramDown(Diagram *diagram) {
moveDiagramDown(findDiagram(diagram));
}
/*
Deplace le schema diagram_view vers le haut / la gauche en position 0
*/
void ProjectView::moveDiagramUpTop(DiagramView *diagram_view)
{
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (!diagram_view_position) {
// le schema est le premier du projet
return;
}
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), 0));
}
/*
Deplace le schema diagram vers le haut / la gauche en position 0
*/
void ProjectView::moveDiagramUpTop(Diagram *diagram)
{
moveDiagramUpTop(findDiagram(diagram));
}
/**
Deplace le schema diagram_view vers le haut / la gauche x10
*/
void ProjectView::moveDiagramUpx10(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (!diagram_view_position) {
// le schema est le premier du projet
return;
}
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position - 10));
}
/**
Deplace le schema diagram vers le haut / la gauche x10
*/
void ProjectView::moveDiagramUpx10(Diagram *diagram) {
moveDiagramUpx10(findDiagram(diagram));
}
/**
* @brief ProjectView::moveDiagramUpx100
* Moves the diagram_view up / left x100
* @param diagram_view View to move
*/
void ProjectView::moveDiagramUpx100(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (!diagram_view_position) {
// The diagram is the first of the project
return;
}
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position - 100));
}
/**
* @brief ProjectView::moveDiagramUpx100
* Moves the diagram up / left x100
* @param diagram Diagram to move
*/
void ProjectView::moveDiagramUpx100(Diagram *diagram) {
moveDiagramUpx100(findDiagram(diagram));
}
/**
* @brief ProjectView::moveDiagramDownx100
* Moves the diagram_view down / right x100
* @param diagram_view View to move
*/
void ProjectView::moveDiagramDownx100(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (diagram_view_position + 1 == m_diagram_ids.count()) {
// The diagram is the last of the project
return;
}
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position + 100));
}
/**
* @brief ProjectView::moveDiagramDownx100
* Moves the diagram down / right x100
* @param diagram Diagram to move
*/
void ProjectView::moveDiagramDownx100(Diagram *diagram) {
moveDiagramDownx100(findDiagram(diagram));
}
/**
Deplace le schema diagram_view vers le bas / la droite x10
*/
void ProjectView::moveDiagramDownx10(DiagramView *diagram_view) {
if (!diagram_view) return;
int diagram_view_position = m_diagram_ids.key(diagram_view);
if (diagram_view_position + 1 == m_diagram_ids.count()) {
// le schema est le dernier du projet
return;
}
m_project->undoStack()->push(new MoveDiagramCommand(this, diagram_view->diagram(), diagram_view_position + 10));
}
/**
Deplace le schema diagram vers le bas / la droite x10
*/
void ProjectView::moveDiagramDownx10(Diagram *diagram) {
moveDiagramDownx10(findDiagram(diagram));
}
/**
Exporte le schema.
*/
void ProjectView::exportProject()
{
if (!m_project) return;
ExportDialog ed(m_project, parentWidget());
#ifdef Q_OS_MACOS
ed.setWindowFlags(Qt::Sheet);
#endif
ed.exec();
}
/**
Save project properties along with all modified diagrams.
@see filePath()
@see setFilePath()
@return a QETResult object reflecting the situation
*/
QETResult ProjectView::save()
{
return(doSave());
}
/**
Ask users for a filepath in order to save the project.
@param options May be used to specify what should be saved; defaults to
all modified diagrams.
@return a QETResult object reflecting the situation; note that a valid
QETResult object is returned if the operation was cancelled.
*/
QETResult ProjectView::saveAs()
{
if (!m_project) return(noProjectResult());
QString filepath = askUserForFilePath();
if (filepath.isEmpty()) return(QETResult());
return(doSave());
}
/**
Save project content, then write the project file. May
call saveAs if no filepath was provided before.
@return a QETResult object reflecting the situation; note that a valid
QETResult object is returned if the operation was cancelled.
*/
QETResult ProjectView::doSave()
{
if (!m_project) return(noProjectResult());
if (m_project -> filePath().isEmpty()) {
// The project has not been saved to a file yet,
// so save() actually means saveAs().
return(saveAs());
}
// write to file
QETResult result = m_project -> write();
updateWindowTitle();
project()->undoStack()->clear();
return(result);
}
/**
Allow the user to clean the project, which includes:
* deleting unused title block templates
* deleting unused elements
* deleting empty categories
@return an integer value above zero if elements and/or categories were
cleaned.
*/
int ProjectView::cleanProject()
{
if (!m_project) return(0);
// s'assure que le schema n'est pas en lecture seule
if (m_project -> isReadOnly()) {
QET::QetMessageBox::critical(
this,
tr("Projet en lecture seule", "message box title"),
tr("Ce projet est en lecture seule. Il n'est donc pas possible de le nettoyer.", "message box content")
);
return(0);
}
// construit un petit dialogue pour parametrer le nettoyage
QCheckBox *clean_tbt = new QCheckBox(tr("Supprimer les modèles de cartouche inutilisés dans le projet"));
QCheckBox *clean_elements = new QCheckBox(tr("Supprimer les éléments inutilisés dans le projet"));
QCheckBox *clean_categories = new QCheckBox(tr("Supprimer les catégories vides"));
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
clean_tbt -> setChecked(true);
clean_elements -> setChecked(true);
clean_categories -> setChecked(true);
QDialog clean_dialog(parentWidget());
#ifdef Q_OS_MACOS
clean_dialog.setWindowFlags(Qt::Sheet);
#endif
clean_dialog.setWindowTitle(tr("Nettoyer le projet", "window title"));
QVBoxLayout *clean_dialog_layout = new QVBoxLayout();
clean_dialog_layout -> addWidget(clean_tbt);
clean_dialog_layout -> addWidget(clean_elements);
clean_dialog_layout -> addWidget(clean_categories);
clean_dialog_layout -> addWidget(buttons);
clean_dialog.setLayout(clean_dialog_layout);
connect(buttons, SIGNAL(accepted()), &clean_dialog, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), &clean_dialog, SLOT(reject()));
int clean_count = 0;
if (clean_dialog.exec() == QDialog::Accepted)
{
if (clean_tbt -> isChecked()) {
m_project->embeddedTitleBlockTemplatesCollection()->deleteUnusedTitleBlocKTemplates();
}
if (clean_elements->isChecked()) {
m_project->embeddedElementCollection()->cleanUnusedElement();
}
if (clean_categories->isChecked()) {
m_project->embeddedElementCollection()->cleanUnusedDirectory();
}
}
m_project -> setModified(true);
return(clean_count);
}
/**
Initialize actions for this widget.
*/
void ProjectView::initActions()
{
m_add_new_diagram = new QAction(QET::Icons::AddFolio, tr("Ajouter un folio"), this);
connect(m_add_new_diagram, &QAction::triggered, [this](){this->m_project->addNewDiagram();});
m_first_view = new QAction(QET::Icons::ArrowLeftDouble, tr("Revenir au debut du projet"),this);
connect(m_first_view, &QAction::triggered, [this](){this->m_tab->setCurrentWidget(firstDiagram());});
m_end_view = new QAction(QET::Icons::ArrowRightDouble, tr("Aller à la fin du projet"),this);
connect(m_end_view, &QAction::triggered, [this](){this->m_tab->setCurrentWidget(lastDiagram());});
// button to scroll one page left
m_next_view_left = new QAction(QET::Icons::ArrowLeft, tr("go one page left"),this);
connect(m_next_view_left, &QAction::triggered, [this](){this->m_tab->setCurrentWidget(previousDiagram());});
// button to scroll one page right
m_next_view_right = new QAction(QET::Icons::ArrowRight, tr("go one page right"),this);
connect(m_next_view_right, &QAction::triggered, [this](){this->m_tab->setCurrentWidget(nextDiagram());});
}
/**
Initialize child widgets for this widget.
*/
void ProjectView::initWidgets()
{
setObjectName("ProjectView");
setWindowIcon(QET::Icons::ProjectFileGP);
// initialize the "fallback" widget
fallback_widget_ = new QWidget();
fallback_label_ = new QLabel(
tr(
"Ce projet ne contient aucun folio",
"label displayed when a project contains no diagram"
)
);
fallback_label_ -> setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
// initialize tabs
#ifdef Q_OS_MACOS
m_tab = new WheelEnabledTabBar(this);
#else
m_tab = new QTabWidget(this);
#endif
m_tab -> setMovable(true);
// setting UsesScrollButton ensures that when the tab bar is full, the tabs are scrolled.
m_tab -> setUsesScrollButtons(true);
// disable the internal scroll buttons of the TabWidget, we will use our own buttons.
m_tab->setStyleSheet("QTabBar QToolButton {border-image: ;border-width: 0px}");
m_tab->setStyleSheet("QTabBar::scroller {width: 0px;}");
// add layouts
QHBoxLayout *TopRightCorner_Layout = new QHBoxLayout();
TopRightCorner_Layout->setContentsMargins(0,0,0,0);
// some place left to the 'next_right_view_button' button
TopRightCorner_Layout->insertSpacing(1,10);
QHBoxLayout *TopLeftCorner_Layout = new QHBoxLayout();
TopLeftCorner_Layout->setContentsMargins(0,0,0,0);
// add buttons
QToolButton *m_next_right_view_button =new QToolButton;
m_next_right_view_button->setDefaultAction(m_next_view_right);
m_next_right_view_button->setAutoRaise(true);
TopRightCorner_Layout->addWidget(m_next_right_view_button);
QToolButton *m_end_view_button =new QToolButton;
m_end_view_button->setDefaultAction(m_end_view);
m_end_view_button->setAutoRaise(true);
TopRightCorner_Layout->addWidget(m_end_view_button);
QToolButton *add_new_diagram_button = new QToolButton;
add_new_diagram_button -> setDefaultAction(m_add_new_diagram);
add_new_diagram_button -> setAutoRaise(true);
TopRightCorner_Layout->addWidget(add_new_diagram_button);
// some place right to the 'add_new_diagram_button' button
TopRightCorner_Layout->addSpacing(5);
QToolButton *m_first_view_button =new QToolButton;
m_first_view_button->setDefaultAction(m_first_view);
m_first_view_button->setAutoRaise(true);
TopLeftCorner_Layout->addWidget(m_first_view_button);
QToolButton *m_next_left_view_button =new QToolButton;
m_next_left_view_button->setDefaultAction(m_next_view_left);
m_next_left_view_button->setAutoRaise(true);
TopLeftCorner_Layout->addWidget(m_next_left_view_button);
// some place right to the 'first_view_button' button
TopLeftCorner_Layout->addSpacing(10);
// add widgets to tabbar
QWidget *tabwidgetRight=new QWidget(this);
tabwidgetRight->setLayout(TopRightCorner_Layout);
m_tab -> setCornerWidget(tabwidgetRight, Qt::TopRightCorner);
QWidget *tabwidgetLeft=new QWidget(this);
tabwidgetLeft->setLayout(TopLeftCorner_Layout);
m_tab -> setCornerWidget(tabwidgetLeft, Qt::TopLeftCorner);
// manage signals
connect(m_tab, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
connect(m_tab, SIGNAL(tabBarDoubleClicked(int)), this, SLOT(tabDoubleClicked(int)));
connect(m_tab->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int)), Qt::QueuedConnection);
fallback_widget_ -> setVisible(false);
m_tab -> setVisible(false);
}
/**
Initialize layout for this widget.
*/
void ProjectView::initLayout()
{
QVBoxLayout *fallback_widget_layout_ = new QVBoxLayout(fallback_widget_);
fallback_widget_layout_ -> addWidget(fallback_label_);
layout_ = new QVBoxLayout(this);
#ifdef Q_OS_MACOS
layout_ -> setContentsMargins(0, 8, 0, 0);
#else
layout_ -> setContentsMargins(0, 0, 0, 0);
#endif
layout_ -> setSpacing(0);
layout_ -> addWidget(fallback_widget_);
layout_ -> addWidget(m_tab);
}
/**
@brief ProjectView::loadDiagrams
Load diagrams of project.
We create a diagram view for each diagram,
and add it to the project view.
*/
void ProjectView::loadDiagrams()
{
if (!m_project) return;
setDisplayFallbackWidget(m_project -> diagrams().isEmpty());
auto dialog = DialogWaiting::instance();
if(dialog)
{
dialog->setTitle( tr(""
"Ouverture du projet en cours...
"
"Création des onglets de folio :"
"
"));
}
for(auto diagram : m_project->diagrams())
{
if(dialog)
{
dialog->setDetail(diagram->title());
dialog->setProgressBar(dialog->progressBarValue()+1);
}
auto dv = new DiagramView(diagram);
dv->setFrameStyle(QFrame::Plain | QFrame::NoFrame);
auto index = m_project->folioIndex(diagram);
m_tab->insertTab(index, dv, QET::Icons::Diagram, dv->title());
m_diagram_view_list.insert(index, dv);
connect(dv, &DiagramView::showDiagram, this, QOverload::of(&ProjectView::showDiagram));
connect(dv, &DiagramView::titleChanged, this, &ProjectView::updateTabTitle);
connect(dv, &DiagramView::findElementRequired, this, &ProjectView::findElementRequired);
connect(&dv->diagram()->border_and_titleblock , &BorderTitleBlock::titleBlockFolioChanged, [this, dv]() {this->updateTabTitle(dv);});
}
rebuildDiagramsMap();
updateAllTabsTitle();
m_tab->setCurrentWidget(firstDiagram());
}
/**
@brief ProjectView::updateWindowTitle
Update the project view title
*/
void ProjectView::updateWindowTitle()
{
QString title;
if (m_project) {
title = m_project -> pathNameTitle();
} else {
title = tr("Projet", "window title for a project-less ProjectView");
}
setWindowTitle(title);
}
/**
Effectue les actions necessaires lorsque le projet visualise entre ou sort
du mode lecture seule.
*/
void ProjectView::adjustReadOnlyState()
{
bool editable = !(m_project -> isReadOnly());
// prevent users from moving existing diagrams
m_tab -> setMovable(editable);
// prevent users from adding new diagrams
m_add_new_diagram -> setEnabled(editable);
// on met a jour le titre du widget, qui reflete l'etat de lecture seule
updateWindowTitle();
}
/**
@brief ProjectView::diagramAdded
Slot called when qetproject emit diagramAdded
@param diagram
*/
void ProjectView::diagramAdded(Diagram *diagram)
{
auto dv = new DiagramView(diagram);
auto index = m_project->folioIndex(diagram);
m_tab->insertTab(index, dv, QET::Icons::Diagram, dv->title());
dv->setFrameStyle(QFrame::Plain | QFrame::NoFrame);
m_diagram_view_list.insert(index, dv);
rebuildDiagramsMap();
updateAllTabsTitle();
connect(dv, &DiagramView::showDiagram, this, QOverload::of(&ProjectView::showDiagram));
connect(dv, &DiagramView::titleChanged, this, &ProjectView::updateTabTitle);
connect(dv, &DiagramView::findElementRequired, this, &ProjectView::findElementRequired);
connect(&dv->diagram()->border_and_titleblock , &BorderTitleBlock::titleBlockFolioChanged, [this, dv]() {this->updateTabTitle(dv);});
// signal diagram view was added
emit(diagramAdded(dv));
m_project->setModified(true);
showDiagram(dv);
}
/**
@brief ProjectView::updateTabTitle
Update the title of the tab which display the diagram view.
@param diagram_view : The diagram view.
*/
void ProjectView::updateTabTitle(DiagramView *diagram_view)
{
int diagram_tab_id = m_diagram_ids.key(diagram_view, -1);
if (diagram_tab_id != -1)
{
QSettings settings;
QString title;
Diagram *diagram = diagram_view->diagram();
if (settings.value("genericpanel/folio", false).toBool())
{
QString formula = diagram->border_and_titleblock.folio();
autonum::sequentialNumbers seq;
title = autonum::AssignVariables::formulaToLabel(
formula,
seq,
diagram);
}
else
title = QString::number(diagram->folioIndex() + 1);
title += " - ";
title += diagram->title();
m_tab->setTabText(diagram_tab_id ,title);
}
}
/**
@brief ProjectView::updateAllTabsTitle
Update all tabs title
*/
void ProjectView::updateAllTabsTitle()
{
for (DiagramView *dv : m_diagram_ids.values())
updateTabTitle(dv);
}
/**
@param from Index de l'onglet avant le deplacement
@param to Index de l'onglet apres le deplacement
*/
void ProjectView::tabMoved(int from, int to)
{
if (!m_project)
return;
m_project->diagramOrderChanged(from, to);
rebuildDiagramsMap();
//Rebuild the title of each diagram in range from - to
for (int i= qMin(from,to) ; i< qMax(from,to)+1 ; ++i)
{
DiagramView *dv = m_diagram_ids.value(i);
updateTabTitle(dv);
}
}
/**
@brief ProjectView::setDiagramPosition
Move \p diagram's tab to \p new_position (out-of-range values are
clamped by the underlying tab bar, same as a direct QTabBar::moveTab()
call). Used by MoveDiagramCommand for both redo and undo.
tabMoved() -- the slot that normally keeps the project's diagram list in
sync with the tab bar -- is connected with Qt::QueuedConnection (needed
so interactive drag-and-drop reordering settles before the model
updates). That queued call would run a second, redundant
diagramOrderChanged() after this method already performed it
synchronously, so that one connection is temporarily dropped around the
move (blocking all of the tab bar's signals instead would also suppress
QTabWidget's own internal tabMoved connection, which is what keeps its
page stack in the same order as the tab bar -- breaking the move
visually).
@param diagram
@param new_position
*/
void ProjectView::setDiagramPosition(Diagram *diagram, int new_position)
{
if (!m_project)
return;
DiagramView *diagram_view = findDiagram(diagram);
if (!diagram_view)
return;
int current_position = m_diagram_ids.key(diagram_view, -1);
if (current_position < 0)
return;
disconnect(m_tab->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int)));
m_tab->tabBar()->moveTab(current_position, new_position);
connect(m_tab->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int)), Qt::QueuedConnection);
rebuildDiagramsMap();
int actual_new_position = m_tab->indexOf(diagram_view);
if (actual_new_position != current_position)
m_project->diagramOrderChanged(current_position, actual_new_position);
for (int i = qMin(current_position, actual_new_position); i <= qMax(current_position, actual_new_position); ++i)
updateTabTitle(m_diagram_ids.value(i));
}
/**
@param diagram Schema a trouver
@return le DiagramView correspondant au schema passe en parametre, ou 0 si
le schema n'est pas trouve
*/
DiagramView *ProjectView::findDiagram(Diagram *diagram) {
foreach(DiagramView *diagram_view, diagram_views()) {
if (diagram_view -> diagram() == diagram) {
return(diagram_view);
}
}
return(nullptr);
}
/**
Reconstruit la map associant les index des onglets avec les DiagramView
*/
void ProjectView::rebuildDiagramsMap()
{
// vide la map
m_diagram_ids.clear();
foreach(DiagramView *diagram_view, m_diagram_view_list) {
int dv_idx = m_tab -> indexOf(diagram_view);
if (dv_idx == -1) continue;
m_diagram_ids.insert(dv_idx, diagram_view);
}
}
/**
@brief ProjectView::tabChanged
Manage the tab change.
If tab_id == -1 (there is no diagram opened),
we display the fallback widget.
@param tab_id
*/
void ProjectView::tabChanged(int tab_id)
{
if (tab_id == -1)
setDisplayFallbackWidget(true);
else if(m_tab->count() == 1)
setDisplayFallbackWidget(false);
emit(diagramActivated(m_diagram_ids[tab_id]));
if (m_diagram_ids[tab_id] != nullptr)
m_diagram_ids[tab_id]->diagram()->diagramActivated();
//Clear the event interface of the previous diagram
if (DiagramView *dv = m_diagram_ids[m_previous_tab_index])
dv->diagram()->clearEventInterface();
m_previous_tab_index = tab_id;
}
/**
Gere le double-clic sur un onglet : edite les proprietes du schema
@param tab_id Index de l'onglet concerne
*/
void ProjectView::tabDoubleClicked(int tab_id) {
// repere le schema concerne
DiagramView *diagram_view = m_diagram_ids[tab_id];
if (!diagram_view) return;
diagram_view -> editDiagramProperties();
}
/**
@param fallback true pour afficher le widget de fallback, false pour
afficher les onglets.
Le widget de Fallback est le widget affiche lorsque le projet ne comporte
aucun schema.
*/
void ProjectView::setDisplayFallbackWidget(bool fallback) {
fallback_widget_ -> setVisible(fallback);
m_tab -> setVisible(!fallback);
}