/*
	Copyright 2006-2009 Xavier Guerrin
	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 "projectview.h"
#include "qetproject.h"
#include "diagramview.h"
#include "diagram.h"
#include "diagramprintdialog.h"
#include "exportdialog.h"
#include "qetapp.h"
#include "qettabwidget.h"
#include "qetelementeditor.h"
#include "interactivemoveelementshandler.h"
#include "borderpropertieswidget.h"
#include "insetpropertieswidget.h"
#include "conductorpropertieswidget.h"
#include "qeticons.h"

/**
	Constructeur
	@param project projet a visualiser
	@param parent Widget parent
*/
ProjectView::ProjectView(QETProject *project, QWidget *parent) :
	QWidget(parent),
	project_(0)
{
	setObjectName("ProjectView");
	setWindowIcon(QET::Icons::Project);
	
	// construit le widget "fallback"
	fallback_widget_ = new QWidget();
	QVBoxLayout *fallback_widget_layout_ = new QVBoxLayout(fallback_widget_);
	QLabel *label_widget = new QLabel(tr("Ce projet ne contient aucun sch\351ma"));
	label_widget -> setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
	fallback_widget_layout_ -> addWidget(label_widget);
	
	tabs_ = new QETTabWidget();
	tabs_ -> setTabsMovable(true);
	connect(tabs_, SIGNAL(currentChanged(int)),   this, SLOT(tabChanged(int)));
	connect(tabs_, SIGNAL(tabDoubleClicked(int)), this, SLOT(tabDoubleClicked(int)));
	connect(tabs_, SIGNAL(firstTabInserted()),    this, SLOT(firstTabInserted()));
	connect(tabs_, SIGNAL(lastTabRemoved()),      this, SLOT(lastTabRemoved()));
	connect(tabs_, SIGNAL(tabMoved(int, int)),    this, SLOT(tabMoved(int, int)));
	
	layout_ = new QVBoxLayout(this);
	layout_ -> setMargin(0);
	layout_ -> setSpacing(0);
	layout_ -> addWidget(fallback_widget_);
	layout_ -> addWidget(tabs_);
	
	fallback_widget_ -> setVisible(false);
	tabs_ -> setVisible(false);
	
	setProject(project);
}

/**
	Destructeur
	Supprime les DiagramView embarquees
*/
ProjectView::~ProjectView() {
	// qDebug() << "Suppression du ProjectView" << ((void *)this);
	foreach(int id, diagram_ids_.keys()) {
		DiagramView *diagram_view = diagram_ids_.take(id);
		delete diagram_view;
	}
}

/**
	@return le projet actuellement visualise par le ProjectView
*/
QETProject *ProjectView::project() {
	return(project_);
}

/**
	Definit le projet visualise par le ProjectView. Ne fait rien si le projet a
	deja ete defini.
	@param project projet a visualiser
*/
void ProjectView::setProject(QETProject *project) {
	if (!project_) {
		project_ = project;
		connect(project_, SIGNAL(projectTitleChanged(QETProject *, const QString &)), this, SLOT(updateWindowTitle()));
		connect(project_, SIGNAL(readOnlyChanged    (QETProject *, bool)),            this, SLOT(updateWindowTitle()));
		updateWindowTitle();
		loadDiagrams();
	}
}

/**
	@return la liste des schemas ouverts dans le projet
*/
QList<DiagramView *> ProjectView::diagrams() const {
	return(diagrams_);
}

/**
	@return le schema actuellement active
*/
DiagramView *ProjectView::currentDiagram() const {
	int current_tab_index = tabs_ -> currentIndex();
	return(diagram_ids_[current_tab_index]);
}

/**
	Gere la fermeture du schema.
	@param event Le QCloseEvent decrivant l'evenement
*/
void ProjectView::closeEvent(QCloseEvent *qce) {
	// si la vue n'est pas liee a un projet, on ferme directement
	if (!project_) {
		qce -> accept();
		emit(projectClosed(this));
		return;
	}
	
	// si le projet est comme neuf et n'est pas enregistre, on ferme directement
	if (!project_ -> projectWasModified() && project_ -> filePath().isEmpty()) {
		qce -> accept();
		emit(projectClosed(this));
		return;
	}
	
	bool can_close_project = true;
	if (!tryClosing()) {
		// l'utilisateur a refuse la fermeture du projet - on arrete la
		can_close_project = false;
	} else {
		// a ce stade, l'utilisateur a accepte la fermeture de tout le contenu du projet
		if (!project_ -> filePath().isEmpty()) {
			// si le projet a un chemin specifie, on l'enregistre et on le ferme
			can_close_project = project_ -> write();
		} else {
			// l'utilisateur n'enregistre pas son projet
			can_close_project = true;
		}
	}
	
	if (can_close_project) {
		qce -> accept();
		emit(projectClosed(this));
	} else {
		qce -> ignore();
	}
}

/**
	Cette methode essaye de fermer successivement les editeurs d'element puis
	les schemas du projet. L'utilisateur peut refuser de fermer un schema ou un
	editeur.
	@return true si tout a pu etre ferme, false sinon
	@see tryClosingElementEditors()
	@see tryClosingDiagrams()
*/
bool ProjectView::tryClosing() {
	if (!tryClosingElementEditors()) {
		return(false);
	}
	
	if (!tryClosingDiagrams()) {
		return(false);
	}
	
	// a ce stade, l'utilisateur a accepte de fermer tous les editeurs
	// d'elements et tous les schemas
	// on regarde s'il reste du contenu dans le projet
	if (project_ -> projectWasModified() && project_ -> filePath().isEmpty()) {
		// si oui, on propose a l'utilisateur d'enregistrer le projet
		QMessageBox::StandardButton answer = QMessageBox::question(
			this,
			tr("Enregistrer le projet en cours ?", "message box title"),
			QString(tr("Voulez-vous enregistrer le projet ?", "message box content")),
			QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
			QMessageBox::Cancel
		);
		if (answer == QMessageBox::Cancel) {
			return(false);
		} else if (answer == QMessageBox::Yes) {
			return(save());
		}
	}
	
	return(true);
}

/**
	Un projet comporte des elements integres. Cette methode ferme les editeurs
	d'elements associes a ce projet. L'utilisateur peut refuser la fermeture
	d'un editeur d'element.
	@return true si tous les editeurs d'element ont pu etre fermes, false sinon
*/
bool ProjectView::tryClosingElementEditors() {
	if (!project_) return(true);
	/*
		La QETApp permet d'acceder rapidement aux editeurs d'element
		editant un element du projet.
	*/
	QList<QETElementEditor *> editors = QETApp::elementEditors(project_);
	foreach(QETElementEditor *editor, editors) {
		if (!editor -> close()) return(false);
	}
	return(true);
}

/**
	Un projet comporte 0 a n schemas.
	Cette methode parcourt les schemas et demande a l'utilisateur s'il veut
	enregistrer les schemas modifies afin de les fermer. L'utilisateur peut
	refuser la fermeture d'un schema.
	Si un schema a ete ajoute sans jamais etre modifie, cette methode demande a
	l'utilisateur s'il souhaite l'enlever.
	@return true si tous les schemas peuvent etre fermes, false sinon
*/
bool ProjectView::tryClosingDiagrams() {
	if (!project_) return(true);
	
	foreach(DiagramView *diagram_view, diagrams()) {
		if (!diagram_view -> diagram() -> undoStack().isClean()) {
			// ce schema a ete modifie - on demande a l'utilisateur s'il veut l'enregistrer
			showDiagram(diagram_view -> diagram());
			QMessageBox::StandardButton answer = QMessageBox::question(
				this,
				tr("Enregistrer le sch\351ma en cours ?", "message box title"),
				QString(tr("Voulez-vous enregistrer le sch\351ma %1 ?", "message box content - %1 is a diagram title")).arg(diagram_view -> windowTitle()),
				QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
				QMessageBox::Cancel
			);
			if (answer == QMessageBox::Cancel) {
				return(false);
			} else if (answer == QMessageBox::Yes) {
				if (!save()) {
					return(false);
				}
			}
		} else if (!diagram_view -> diagram() -> wasWritten()) {
			// ce schema a ete ajoute mais pas modifie - on demande a l'utilisateur s'il veut le conserver
			showDiagram(diagram_view -> diagram());
			QMessageBox::StandardButton answer = QMessageBox::question(
				this,
				tr("Enregistrer le nouveau sch\351ma ?", "message box title"),
				tr("Ce sch\351ma a \351t\351 ajout\351 mais n'a \351t\351 ni modifi\351 ni enregistr\351. Voulez-vous le conserver ?", "message box content"),
				QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
				QMessageBox::Cancel
			);
			if (answer == QMessageBox::Cancel) {
				return(false);
			} else if (answer == QMessageBox::Yes) {
				if (!save()) {
					return(false);
				}
			} else if (answer == QMessageBox::No) {
				removeDiagram(diagram_view);
			}
		}
	}
	return(true);
}

/**
	Ajoute un nouveau schema au ProjectView
*/
void ProjectView::addNewDiagram() {
	if (project_ -> isReadOnly()) return;
	
	Diagram *new_diagram = project_ -> addNewDiagram();
	DiagramView *new_diagram_view = new DiagramView(new_diagram);
	addDiagram(new_diagram_view);
	showDiagram(new_diagram_view);
}

/**
	Ajoute un schema au ProjectView
	@param diagram Schema a ajouter
*/
void ProjectView::addDiagram(DiagramView *diagram) {
	if (!diagram) return;
	
	// verifie que le schema n'est pas deja present dans le projet
	if (diagram_ids_.values().contains(diagram)) return;
	
	// ajoute un nouvel onglet pour le nouveau schema
	tabs_ -> addTab(diagram, QET::Icons::Diagram, diagram -> title());
	diagrams_ << diagram;
	rebuildDiagramsMap();
	connect(diagram, SIGNAL(titleChanged(DiagramView *, const QString &)), this, SLOT(updateTabTitle(DiagramView *, const QString &)));
	connect(diagram, SIGNAL(findElementRequired(const ElementsLocation &)), this, SIGNAL(findElementRequired(const ElementsLocation &)));
	
	// signale l'ajout du schema
	emit(diagramAdded(diagram));
}

/**
	Enleve un schema du ProjectView
	@param diagram_view Schema a enlever
*/
void ProjectView::removeDiagram(DiagramView *diagram_view) {
	if (!diagram_view) return;
	if (project_ -> isReadOnly()) return;
	
	// verifie que le schema est bien present dans le projet
	if (!diagram_ids_.values().contains(diagram_view)) return;
	
	// demande confirmation a l'utilisateur
	if (
		diagram_view -> diagram() -> wasWritten() ||\
		!diagram_view -> diagram() -> undoStack().isClean()
	) {
		int answer = QMessageBox::question(
			this,
			tr("Supprimer le sch\351ma ?", "message box title"),
			tr("\312tes-vous s\373r de vouloir supprimer ce sch\351ma du projet ? Ce changement est irr\351versible.", "message box content"),
			QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
			QMessageBox::No
		);
		if (answer != QMessageBox::Yes) {
			return;
		}
	}
	
	// notifie le reste du monde que le DiagramView va disparaitre
	emit(diagramAboutToBeRemoved(diagram_view));
	
	// enleve le DiagramView des onglets
	int diagram_tab_id = diagram_ids_.key(diagram_view);
	tabs_ -> removeTab(diagram_tab_id);
	diagrams_.removeAll(diagram_view);
	rebuildDiagramsMap();
	
	// supprime le DiagramView, puis le Diagram
	project_ -> removeDiagram(diagram_view -> diagram());
	delete diagram_view;
	
	// signale le retrait du schema
	emit(diagramRemoved(diagram_view));
	
	// rend definitif le retrait du schema
	project_ -> write();
}

/**
	Enleve un schema du ProjectView
	@param diagram_view Schema a enlever
*/
void ProjectView::removeDiagram(Diagram *diagram) {
	if (!diagram) return;
	
	if (DiagramView *diagram_view = findDiagram(diagram)) {
		removeDiagram(diagram_view);
	}
}

/**
	Active l'onglet adequat pour afficher le schema passe en parametre
	@param diagram Schema a afficher
*/
void ProjectView::showDiagram(DiagramView *diagram) {
	if (!diagram) return;
	tabs_ -> 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)) {
		tabs_ -> setCurrentWidget(diagram_view);
	}
}

/**
	Affiche un dialogue permettant a l'utilisateur d'editer les proprietes du
	projet.
*/
void ProjectView::editProjectProperties() {
	if (!project_) return;
	
	// dialogue d'edition des proprietes du projet
	QDialog properties_dialog(parentWidget());
	properties_dialog.setMinimumWidth(786);
	properties_dialog.setMinimumHeight(585);
	properties_dialog.setWindowTitle(tr("Propri\351t\351s du projet", "window title"));
	
	// titre du projet
	QLabel *title_label = new QLabel(tr("Titre du projet :"));
	QLineEdit *title_field = new QLineEdit(project_ -> title());
	
	// proprietes des nouveaux schemas
	QLabel *new_diagrams_prop = new QLabel(tr("Propri\351t\351s \340 utiliser lors de l'ajout d'un nouveau sch\351ma au projet :"));
	
	// dimensions par defaut d'un schema
	BorderPropertiesWidget *bpw = new BorderPropertiesWidget(project_ -> defaultBorderProperties());
	
	// proprietes par defaut d'un cartouche
	InsetPropertiesWidget *ipw = new InsetPropertiesWidget(project_ -> defaultInsetProperties(), true);
	
	// proprietes par defaut des conducteurs
	ConductorPropertiesWidget *cpw = new ConductorPropertiesWidget(project_ -> defaultConductorProperties());
	cpw -> setContentsMargins(0, 0, 0, 0);
	
	// boutons pour valider le dialogue
	QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
	
	connect(buttons, SIGNAL(accepted()), &properties_dialog, SLOT(accept()));
	connect(buttons, SIGNAL(rejected()), &properties_dialog, SLOT(reject()));
	
	// agencement avec deux layouts : un vertical, un horizontal
	QHBoxLayout *horiz_layout = new QHBoxLayout();
	horiz_layout -> addWidget(title_label);
	horiz_layout -> addWidget(title_field);
	
	QVBoxLayout *vlayout1 = new QVBoxLayout();
	
	vlayout1 -> addWidget(new_diagrams_prop);
	
	QHBoxLayout *hlayout1 = new QHBoxLayout();
	QVBoxLayout *vlayout2 = new QVBoxLayout();
	
	vlayout2 -> addWidget(bpw);
	vlayout2 -> addWidget(ipw);
	vlayout2 -> setSpacing(5);
	hlayout1 -> addLayout(vlayout2);
	hlayout1 -> addWidget(cpw);
	vlayout1 -> addLayout(hlayout1);
	vlayout1 -> addStretch(1);
	hlayout1 -> setAlignment(cpw, Qt::AlignTop);
	
	QVBoxLayout *vert_layout = new QVBoxLayout(&properties_dialog);
	vert_layout -> addLayout(horiz_layout);
	vert_layout -> addLayout(vlayout1);
	vert_layout -> addStretch();
	vert_layout -> addWidget(buttons);
	
	// si le dialogue est accepte
	if (properties_dialog.exec() == QDialog::Accepted && !project_ -> isReadOnly()) {
		project_ -> setTitle(title_field -> text());
		project_ -> setDefaultBorderProperties(bpw -> borderProperties());
		project_ -> setDefaultInsetProperties(ipw -> insetProperties());
		project_ -> setDefaultConductorProperties(cpw -> conductorProperties());
	}
}

/**
	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));
}

/**
	Ce slot demarre un dialogue permettant a l'utilisateur de parametrer et de
	lancer l'impression de toute ou partie du projet.
*/
void ProjectView::printProject() {
	if (!project_) return;
	
	// transforme le titre du projet en nom utilisable pour le document
	QString doc_name;
	if (!(project_ -> title().isEmpty())) {
		doc_name = project_ -> title();
	} else if (!project_ -> filePath().isEmpty()) {
		doc_name = QFileInfo(project_ -> filePath()).baseName();
	}
	doc_name = QET::stringToFileName(doc_name);
	if (doc_name.isEmpty()) {
		doc_name = tr("projet", "string used to generate a filename");
	}
	
	// recupere le dossier contenant le fichier courant
	QString dir_path = project_ -> filePath();
	if (dir_path.isEmpty()) {
		dir_path = QDir::homePath();
	} else {
		dir_path = QFileInfo(dir_path).absolutePath();
	}
	
	// determine un chemin pour le pdf / ps
	QString file_name = QDir::toNativeSeparators(dir_path + "/" + doc_name);
	
	DiagramPrintDialog print_dialog(project_, this);
	print_dialog.setDocName(doc_name);
	print_dialog.setFileName(file_name);
	print_dialog.exec();
}

/**
	Exporte le schema.
*/
void ProjectView::exportProject() {
	if (!project_) return;
	
	ExportDialog ed(project_, parentWidget());
	ed.exec();
}

/**
	Enregistre le projet dans un fichier.
	@see filePath()
	@see setFilePath()
	@return true si l'enregistrement a reussi, false sinon
*/
bool ProjectView::save() {
	if (project_) {
		if (project_ -> filePath().isEmpty()) {
			// le projet n'est pas encore enregistre dans un fichier
			// save() equivaut alors a saveAs()
			return(saveAs());
		}
		// on enregistre le schema en cours
		if (DiagramView *current_view = currentDiagram()) {
			if (Diagram *diagram = current_view -> diagram()) {
				diagram -> write();
				updateWindowTitle();
				return(true);
			}
		} else {
			// s'il n'y a pas de schema, on appelle directement la methode write()
			project_ -> write();
		}
		return(true);
	}
	return(false);
}

/**
	Enregistre tous les schemas du projet.
	@see filePath()
	@see setFilePath()
	@return true si l'enregistrement a reussi, false sinon
*/
bool ProjectView::saveAll() {
	if (project_) {
		// on fait deja un appel a save
		if (!save()) {
			return(false);
		} else {
			// a ce stade, on suppose que l'on a un fichier, et que l'ecriture du schema en cours a reussi
			// on enregistre les schemas
			foreach(Diagram *diagram, project_ -> diagrams()) {
				diagram -> write();
			}
			updateWindowTitle();
			return(true);
		}
	}
	return(false);
}

/**
	Propose a l'utilisateur de nettoyer le projet ; cela inclut la possibilite :
	  * de supprimer les elements inutilises dans le projet
	  * de supprimer les categories vides
	@return le nombre de traitements effectues (0 si rien n'a ete fait, 1 ou
	2 sinon)
*/
int ProjectView::cleanProject() {
	if (!project_) return(0);
	
	// s'assure que le schema n'est pas en lecture seule
	if (project_ -> isReadOnly()) {
		QMessageBox::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_elements   = new QCheckBox(tr("Supprimer les \351l\351ments inutilis\351s dans le projet"));
	QCheckBox *clean_categories = new QCheckBox(tr("Supprimer les cat\351gories vides"));
	QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
	
	clean_elements   -> setChecked(true);
	clean_categories -> setChecked(true);
	
	QDialog clean_dialog;
	clean_dialog.setWindowTitle(tr("Nettoyer le projet", "window title"));
	QVBoxLayout *clean_dialog_layout = new QVBoxLayout();
	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_elements -> isChecked()) {
			InteractiveMoveElementsHandler *handler = new InteractiveMoveElementsHandler(this);
			project_ -> cleanUnusedElements(handler);
			delete handler;
			++ clean_count;
		}
		if (clean_categories -> isChecked()) {
			InteractiveMoveElementsHandler *handler = new InteractiveMoveElementsHandler(this);
			project_ -> cleanEmptyCategories(handler);
			delete handler;
			++ clean_count;
		}
	}
	return(clean_count);
}

/**
	Demande un nom de fichier a l'utilisateur pour enregistrer le projet
	Si aucun nom n'est entre, elle renvoie faux.
	Si le nom ne se termine pas par l'extension .qet, celle-ci est ajoutee.
	Si l'enregistrement reussit, le nom du fichier est conserve et la fonction renvoie true.
	Sinon, faux est renvoye.
	@return true si l'enregistrement a reussi, false sinon
*/
bool ProjectView::saveAs() {
	// demande un nom de fichier a l'utilisateur pour enregistrer le projet
	QString filepath = QFileDialog::getSaveFileName(
		this,
		tr("Enregistrer sous", "dialog title"),
		(project_ -> filePath().isEmpty() ? QDir::homePath() : QDir(project_ -> filePath())).absolutePath(),
		tr("Sch\351ma QElectroTech (*.qet)", "filetypes allowed when saving a diagram file")
	);
	
	// si aucun nom n'est entre, renvoie faux.
	if (filepath.isEmpty()) return(false);
	
	// si le nom ne se termine pas par l'extension .qet, celle-ci est ajoutee
	if (!filepath.endsWith(".qet", Qt::CaseInsensitive)) filepath += ".qet";
	
	// le fichier est assigne au projet
	project_ -> setFilePath(filepath);
	
	return(save());
}

/**
	Charge les schemas du projet
*/
void ProjectView::loadDiagrams() {
	if (!project_) return;
	
	setDisplayFallbackWidget(project_ -> diagrams().isEmpty());
	
	foreach(Diagram *diagram, project_ -> diagrams()) {
		DiagramView *sv = new DiagramView(diagram);
		addDiagram(sv);
	}
}

/**
	Met a jour le titre du ProjectView
*/
void ProjectView::updateWindowTitle() {
	QString title;
	if (project_) {
		title = project_ -> pathNameTitle();
	} else {
		title = tr("Projet", "window title for a project-less ProjectView");
	}
	setWindowTitle(title);
}

/**
	Met a jour le titre d'un onglet
	@param diagram Schema
	@param diagram_title Titre du schema
*/
void ProjectView::updateTabTitle(DiagramView *diagram, const QString &diagram_title) {
	int diagram_tab_id = diagram_ids_.key(diagram, -1);
	if (diagram_tab_id != -1) {
		tabs_ -> setTabText(diagram_tab_id, diagram_title);
	}
}

/**
	@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 (!project_) return;
	
	// signale au QETProject le changement d'ordre des schemas
	project_ -> diagramOrderChanged(from, to);
	
	// reconstruit la liste associant les index des onglets aux schemas
	rebuildDiagramsMap();
	
	// emet un signal pour informer le reste du monde que l'ordre des schemas a change
	emit(diagramOrderChanged(this, from, to));
}

/**
	@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, diagrams()) {
		if (diagram_view -> diagram() == diagram) {
			return(diagram_view);
		}
	}
	return(0);
}

/**
	Reconstruit la map associant les index des onglets avec les DiagramView
*/
void ProjectView::rebuildDiagramsMap() {
	// vide la map
	diagram_ids_.clear();
	
	foreach(DiagramView *diagram_view, diagrams_) {
		int dv_idx = tabs_ -> indexOf(diagram_view);
		if (dv_idx == -1) continue;
		diagram_ids_.insert(dv_idx, diagram_view);
	}
}

/**
	Gere les changements d'onglets
	@param tab_id Index de l'onglet actif
*/
void ProjectView::tabChanged(int tab_id) {
	emit(diagramActivated(diagram_ids_[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 = diagram_ids_[tab_id];
	if (!diagram_view) return;
	
	diagram_view -> editDiagramProperties();
}

/**
	Gere le fait que le premier schema d'un projet soit insere
*/
void ProjectView::firstTabInserted() {
	setDisplayFallbackWidget(false);
}

/**
	Gere le fait que le dernier schema d'un projet soit enleve
*/
void ProjectView::lastTabRemoved() {
	setDisplayFallbackWidget(true);
}

/**
	@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);
	tabs_ -> setVisible(!fallback);
}
