/*
	Copyright 2006-2023 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 <http://www.gnu.org/licenses/>.
*/
#include "qettemplateeditor.h"

#include "../qetapp.h"
#include "../qeticons.h"
#include "../qetmessagebox.h"
#include "../qetproject.h"
#include "templatecellwidget.h"
#include "templatecommands.h"
#include "templatelocationsaver.h"
#include "templatelogomanager.h"
#include "templateview.h"

/**
	@param parent parent QWidget of this window
*/
QETTitleBlockTemplateEditor::QETTitleBlockTemplateEditor(QWidget *parent) :
	QETMainWindow(parent),
	opened_from_file_(false),
	read_only_(false),
	duplicate_(false),
	tb_template_(nullptr),
	logo_manager_(nullptr)
{
	setWindowIcon(QET::Icons::QETLogo);
	setAttribute(Qt::WA_DeleteOnClose);

	initWidgets();
	initActions();
	initMenus();
	initToolbars();
	readSettings();
}

/**
	Destructor
*/
QETTitleBlockTemplateEditor::~QETTitleBlockTemplateEditor()
{}

/**
	@return the location of the currently edited template
*/
TitleBlockTemplateLocation QETTitleBlockTemplateEditor::location() const
{
	return(location_);
}

/**
	@return true if the provided filepath matches the currently edited template.
	@param filepath path of a title block template on the filesystem
*/
bool QETTitleBlockTemplateEditor::isEditing(const QString &filepath)
{
	QString current_filepath;
	if (opened_from_file_) {
		current_filepath = filepath_;
	} else {
		current_filepath = QETApp::realPath(location_.toString());
	}

	return(
		QET::compareCanonicalFilePaths(
			current_filepath,
			filepath
		)
	);
}

/**
	@brief QETTitleBlockTemplateEditor::setOpenForDuplication
	@param duplicate : true for this editor to prompt the user for a
	new template name as soon as the window appears in order to duplicate
	the edited one.
*/
void QETTitleBlockTemplateEditor::setOpenForDuplication(bool duplicate)
{
	duplicate_ = duplicate;
}

/**
	@return true if this editor will prompt the user for a new template
	name as soon as the window appears in order to duplicate the edited one.
*/
bool QETTitleBlockTemplateEditor::openForDuplication() const
{
	return(duplicate_);
}

/**
	@return true if the currently edited template can be closed. A template can be
	closed if it has not been modified. If the template has been modified, this
	method asks the user what he wants to do.
*/
bool QETTitleBlockTemplateEditor::canClose()
{
	if (undo_stack_ -> isClean()) return(true);
	// ask the user whether he wants to save the current template
	QMessageBox::StandardButton answer = QET::QetMessageBox::question(
		this,
		tr("Enregistrer le modèle en cours ?", "dialog title"),
		QString(
			tr(
				"Voulez-vous enregistrer le modèle %1 ?",
				"dialog content - %1 is a title block template name"
			)
		).arg(location_.name()),
		QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
		QMessageBox::Cancel
	);
	bool result;
	switch(answer) {
		case QMessageBox::Cancel: result = false;  break; // the user hits Cancel or closes the dialog: abort the closing
		case QMessageBox::Yes:    result = save(); break; // the user hits Yes: la reussite depend de l'enregistrement
		default:                  result = true;          // the user hits no: the editor can be closed
	}
	return(result);
}

/**
	@param event Object describing the received event.
*/
void QETTitleBlockTemplateEditor::firstActivation(QEvent *event)
{
	Q_UNUSED(event)
	if (duplicate_ && !opened_from_file_ && location_.parentCollection()) {
		// this editor is supposed to duplicate its current location
		QTimer::singleShot(250, this, SLOT(duplicateCurrentLocation()));
	}
}

/**
	Handle the closing of the main window
	@param qce The QCloseEvent event
*/
void QETTitleBlockTemplateEditor::closeEvent(QCloseEvent *qce)
{
	if (canClose()) {
		writeSettings();
		setAttribute(Qt::WA_DeleteOnClose);
		qce -> accept();
	} else qce -> ignore();
}

/**
	Ask the user for a new template name in order to duplicate the currently
	edited template.
*/
void QETTitleBlockTemplateEditor::duplicateCurrentLocation()
{
	// this method does not work for templates edited from the filesystem
	if (opened_from_file_) return;

	QString proposed_name;
	if (location_.name().isEmpty()) {
		proposed_name = tr("nouveau_modele",
				   "template name suggestion when duplicating the default one");
	} else {
		proposed_name = QString("%1_copy").arg(location_.name());
	}

	bool accepted = false;
	QString new_template_name = QInputDialog::getText(
		this,
		tr("Dupliquer un modèle de cartouche", "input dialog title"),
		tr("Pour dupliquer ce modèle, entrez le nom voulu pour sa copie",
		   "input dialog text"),
		QLineEdit::Normal,
		proposed_name,
		&accepted
	);
	if (accepted) {
		TitleBlockTemplateLocation new_template_location(
					new_template_name,
					location_.parentCollection());
		saveAs(new_template_location);
	}
}

/**
	@param location Location of the tile block template to be edited.
*/
bool QETTitleBlockTemplateEditor::edit(
		const TitleBlockTemplateLocation &location)
{
	// the template name may be empty to create a new one
	const TitleBlockTemplate *tb_template_orig;
	if (location.name().isEmpty()) {
		// loads the default title block template provided by the application
		// it will be used as a start point to design the title block
		tb_template_orig = QETApp::defaultTitleBlockTemplate();
	} else {
		tb_template_orig = location.getTemplate();
	}
	if (!tb_template_orig) {
		/// TODO The TBT does not exist, manage error
#if TODO_LIST
#pragma message("@TODO The TBT does not exist, manage error")
#endif
		return(false);
	}

	opened_from_file_ = false;
	location_ = location;
	setReadOnly(location.isReadOnly());
	editCopyOf(tb_template_orig);
	return(true);
}

/**
	Edit the given template.
	@param project Parent project of the template to edit.
	@param template_name Name of the template to edit within its parent project.
	@return true if this editor was able to edit the given template, false otherwise
*/
bool QETTitleBlockTemplateEditor::edit(
		QETProject *project, const QString &template_name)
{
		// we require a project we will rattach templates to
	if (!project) return(false);

		// the template name may be empty to create a new one
	const TitleBlockTemplate *tb_template_orig;
	if (template_name.isEmpty())
	{
			// loads the default title block template provided by the application
			// it will be used as a start point to design the title block
		tb_template_orig = QETApp::defaultTitleBlockTemplate();
	}
	else
	{
		tb_template_orig = project->embeddedTitleBlockTemplatesCollection()->getTemplate(template_name);
	}

	if (!tb_template_orig) {
		/// TODO The TBT does not exist, manage error
#if TODO_LIST
#pragma message("@TODO The TBT does not exist, manage error")
#endif
		return(false);
	}

	opened_from_file_ = false;
	location_.setParentCollection(project -> embeddedTitleBlockTemplatesCollection());
	location_.setName(template_name);
	setReadOnly(project -> isReadOnly());
	return(editCopyOf(tb_template_orig));
}

/**
	@param file_path Path of the template file to edit.
	@return false if a problem occurred while opening the template, true otherwise.
*/
bool QETTitleBlockTemplateEditor::edit(const QString &file_path)
{
	// get title block template object from the file, edit it
	TitleBlockTemplate *tbt = new TitleBlockTemplate();
	bool loading = tbt -> loadFromXmlFile(file_path);
	if (!loading) {
#if TODO_LIST
#pragma message("@TODO the file opening failed, warn the user?")
#endif
		/// TODO the file opening failed, warn the user?
		return(false);
	}

	bool editing = edit(tbt);
	if (!editing) {
#if TODO_LIST
#pragma message("@TODO the file editing failed, warn the user?")
#endif
		/// TODO the file editing failed, warn the user?
		return(false);
	}

	QFileInfo file_path_info(file_path);
	filepath_ = file_path;
	opened_from_file_ = true;
	setReadOnly(!file_path_info.isWritable());
	return(true);
}

/**
	@param tbt Title block template to be edited
	@return false if a problem occurred while opening the template, true otherwise.
*/
bool QETTitleBlockTemplateEditor::editCopyOf(const TitleBlockTemplate *tbt)
{
	if (!tbt) return(false);
	return(edit(tbt -> clone()));
}

/**
	@param tbt Title block template to be directly edited
	@return false if a problem occurred while opening the template, true otherwise.
*/
bool QETTitleBlockTemplateEditor::edit(TitleBlockTemplate *tbt)
{
	if (!tbt) return(false);
	tb_template_ = tbt;
	template_edition_area_view_ -> setTitleBlockTemplate(tb_template_);
	template_cell_editor_widget_ -> updateLogosComboBox(tb_template_);
	updateEditorTitle();
	return(true);
}

/**
	Launches the logo manager widget, which allows the user to manage the
	logos embedded within the edited template.
*/
void QETTitleBlockTemplateEditor::editLogos()
{
	if (tb_template_) {
		if (!logo_manager_) {
			initLogoManager();
		}

		logo_manager_ -> layout() -> setContentsMargins(0, 0, 0, 0);
		QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close);

		QVBoxLayout *vlayout0 = new QVBoxLayout();
		vlayout0 -> addWidget(logo_manager_);
		vlayout0 -> addWidget(buttons);

		QDialog d(this);
		d.setWindowTitle(logo_manager_ -> windowTitle());
		d.setLayout(vlayout0);
		connect(buttons, SIGNAL(rejected()), &d, SLOT(reject()));
		d.exec();

		// prevent the logo manager from being deleted along with the dialog
		logo_manager_ -> setParent(this);
	}
}

/**
	Launch a new title block template editor.
*/
void QETTitleBlockTemplateEditor::newTemplate()
{
	QETTitleBlockTemplateEditor *qet_template_editor = new QETTitleBlockTemplateEditor();
	qet_template_editor -> edit(TitleBlockTemplateLocation());
	qet_template_editor -> show();
}

/**
	Initialize the various actions.
*/
void QETTitleBlockTemplateEditor::initActions()
{
	new_            = new QAction(QET::Icons::DocumentNew,          tr("&Nouveau",                     "menu entry"), this);
	open_           = new QAction(QET::Icons::DocumentOpen,         tr("&Ouvrir",                      "menu entry"), this);
	open_from_file_ = new QAction(QET::Icons::DocumentOpen,         tr("Ouvrir depuis un fichier",     "menu entry"), this);
	save_           = new QAction(QET::Icons::DocumentSave,         tr("&Enregistrer",                 "menu entry"), this);
	save_as_        = new QAction(QET::Icons::DocumentSaveAs,       tr("Enregistrer sous",             "menu entry"), this);
	save_as_file_   = new QAction(QET::Icons::DocumentSaveAs,       tr("Enregistrer vers un fichier",  "menu entry"), this);
	quit_           = new QAction(QET::Icons::ApplicationExit,      tr("&Quitter",                     "menu entry"), this);
	undo_           = undo_stack_ -> createUndoAction(this);
	redo_           = undo_stack_ -> createRedoAction(this);
	cut_            = new QAction(QET::Icons::EditCut,              tr("Co&uper", "menu entry"),                      this);
	copy_           = new QAction(QET::Icons::EditCopy,             tr("Cop&ier", "menu entry"),                      this);
	paste_          = new QAction(QET::Icons::EditPaste,            tr("C&oller", "menu entry"),                      this);
	edit_logos_     = new QAction(QET::Icons::InsertImage,          tr("Gérer les logos", "menu entry"),           this);
	edit_info_      = new QAction(QET::Icons::UserInformations,     tr("Éditer les informations complémentaires", "menu entry"), this);
	zoom_in_        = new QAction(QET::Icons::ZoomIn,               tr("Zoom avant",                   "menu entry"), this);
	zoom_out_       = new QAction(QET::Icons::ZoomOut,              tr("Zoom arrière",              "menu entry"), this);
	zoom_fit_       = new QAction(QET::Icons::ZoomFitBest,          tr("Zoom adapté",               "menu entry"), this);
	zoom_reset_     = new QAction(QET::Icons::ZoomOriginal,         tr("Pas de zoom",                  "menu entry"), this);
	add_row_        = new QAction(QET::Icons::EditTableInsertRowAbove,    tr("Ajouter une &ligne",      "menu entry"), this);
	add_col_        = new QAction(QET::Icons::EditTableInsertColumnRight, tr("Ajouter une &colonne",    "menu entry"), this);
	merge_cells_    = new QAction(QET::Icons::EditTableCellMerge,   tr("&Fusionner les cellules",      "menu entry"), this);
	split_cell_     = new QAction(QET::Icons::EditTableCellSplit,   tr("&Séparer les cellules",     "menu entry"), this);

	undo_ -> setIcon(QET::Icons::EditUndo);
	redo_ -> setIcon(QET::Icons::EditRedo);

	new_              -> setShortcut(QKeySequence::New);
	open_             -> setShortcut(QKeySequence::Open);
	open_from_file_   -> setShortcut(tr("Ctrl+Shift+O", "shortcut to open a template from a file"));
	save_             -> setShortcut(QKeySequence::Save);
	save_as_file_     -> setShortcut(tr("Ctrl+Shift+S", "shortcut to save a template to a file"));
	quit_             -> setShortcut(QKeySequence(tr("Ctrl+Q", "shortcut to quit")));
	undo_             -> setShortcut(QKeySequence::Undo);
	redo_             -> setShortcut(QKeySequence::Redo);
	cut_              -> setShortcut(QKeySequence::Cut);
	copy_             -> setShortcut(QKeySequence::Copy);
	paste_            -> setShortcut(QKeySequence::Paste);
	edit_logos_       -> setShortcut(QKeySequence(tr("Ctrl+T", "shortcut to manage embedded logos")));
	edit_info_        -> setShortcut(QKeySequence(tr("Ctrl+Y", "shortcut to edit extra information")));
	merge_cells_      -> setShortcut(QKeySequence(tr("Ctrl+J", "shortcut to merge cells")));
	split_cell_       -> setShortcut(QKeySequence(tr("Ctrl+K", "shortcut to split merged cell")));
	zoom_in_          -> setShortcut(QKeySequence::ZoomIn);
	zoom_out_         -> setShortcut(QKeySequence::ZoomOut);
	zoom_fit_         -> setShortcut(QKeySequence(tr("Ctrl+9", "shortcut to enable fit zoom")));
	zoom_reset_       -> setShortcut(QKeySequence(tr("Ctrl+0", "shortcut to reset zoom")));

	connect(new_,             SIGNAL(triggered()), this,     SLOT(newTemplate()));
	connect(open_,            SIGNAL(triggered()), this,     SLOT(open()));
	connect(open_from_file_,  SIGNAL(triggered()), this,     SLOT(openFromFile()));
	connect(save_,            SIGNAL(triggered()), this,     SLOT(save()));
	connect(save_as_,         SIGNAL(triggered()), this,     SLOT(saveAs()));
	connect(save_as_file_,    SIGNAL(triggered()), this,     SLOT(saveAsFile()));
	connect(quit_,            SIGNAL(triggered()), this,     SLOT(quit()));
	connect(cut_,             SIGNAL(triggered()), template_edition_area_view_, SLOT(cut()));
	connect(copy_,            SIGNAL(triggered()), template_edition_area_view_, SLOT(copy()));
	connect(paste_,           SIGNAL(triggered()), template_edition_area_view_, SLOT(paste()));
	connect(zoom_in_,         SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomIn()));
	connect(zoom_out_,        SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomOut()));
	connect(zoom_fit_,        SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomFit()));
	connect(zoom_reset_,      SIGNAL(triggered()), template_edition_area_view_, SLOT(zoomReset()));
	connect(edit_logos_,      SIGNAL(triggered()), this, SLOT(editLogos()));
	connect(edit_info_,       SIGNAL(triggered()), this, SLOT(editTemplateInformation()));
	connect(add_row_,         SIGNAL(triggered()), template_edition_area_view_, SLOT(addRowAtEnd()));
	connect(add_col_,         SIGNAL(triggered()), template_edition_area_view_, SLOT(addColumnAtEnd()));
	connect(merge_cells_,     SIGNAL(triggered()), template_edition_area_view_, SLOT(mergeSelectedCells()));
	connect(split_cell_,      SIGNAL(triggered()), template_edition_area_view_, SLOT(splitSelectedCell()));
}

/**
	Initialize the various menus.
*/
void QETTitleBlockTemplateEditor::initMenus()
{
	file_menu_    = new QMenu(tr("&Fichier", "menu title"), this);
	edit_menu_    = new QMenu(tr("&Édition", "menu title"), this);
	display_menu_ = new QMenu(tr("Afficha&ge", "menu title"), this);

	file_menu_    -> addAction(new_);
	file_menu_    -> addAction(open_);
	file_menu_    -> addAction(open_from_file_);
	file_menu_    -> addAction(save_);
	file_menu_    -> addAction(save_as_);
	file_menu_    -> addAction(save_as_file_);
	file_menu_    -> addSeparator();
	file_menu_    -> addAction(quit_);

	edit_menu_   -> addAction(undo_);
	edit_menu_   -> addAction(redo_);
	edit_menu_   -> addSeparator();
	edit_menu_   -> addAction(cut_);
	edit_menu_   -> addAction(copy_);
	edit_menu_   -> addAction(paste_);
	edit_menu_   -> addSeparator();
	edit_menu_   -> addAction(add_row_);
	edit_menu_   -> addAction(add_col_);
	edit_menu_   -> addAction(merge_cells_);
	edit_menu_   -> addAction(split_cell_);
	edit_menu_   -> addSeparator();
	edit_menu_   -> addAction(edit_logos_);
	edit_menu_   -> addAction(edit_info_);
	display_menu_ -> addAction(zoom_in_);
	display_menu_ -> addAction(zoom_out_);
	display_menu_ -> addAction(zoom_fit_);
	display_menu_ -> addAction(zoom_reset_);

	insertMenu(settings_menu_, file_menu_);
	insertMenu(settings_menu_, edit_menu_);
	insertMenu(settings_menu_, display_menu_);
}

/**
	Initialize toolbars.
*/
void QETTitleBlockTemplateEditor::initToolbars()
{
	QToolBar *main_toolbar = new QToolBar(tr("Outils", "toolbar title"), this);
	main_toolbar -> setObjectName("tbt_main_toolbar");
	main_toolbar -> addAction(new_);
	main_toolbar -> addAction(open_);
	main_toolbar -> addAction(save_);
	main_toolbar -> addAction(save_as_);
	addToolBar(Qt::TopToolBarArea, main_toolbar);

	QToolBar *edit_toolbar = new QToolBar(tr("Édition", "toolbar title"), this);
	edit_toolbar -> setObjectName("tbt_edit_toolbar");
	edit_toolbar -> addAction(undo_);
	edit_toolbar -> addAction(redo_);
	edit_toolbar -> addSeparator();
	edit_toolbar -> addAction(merge_cells_);
	edit_toolbar -> addAction(split_cell_);
	addToolBar(Qt::TopToolBarArea, edit_toolbar);

	QToolBar *display_toolbar = new QToolBar(tr("Affichage", "toolbar title"), this);
	display_toolbar -> setObjectName("tbt_display_toolbar");
	display_toolbar -> addAction(zoom_in_);
	display_toolbar -> addAction(zoom_out_);
	display_toolbar -> addAction(zoom_fit_);
	display_toolbar -> addAction(zoom_reset_);
	addToolBar(Qt::TopToolBarArea, display_toolbar);
}

/**
	Initialize layouts and widgets
*/
void QETTitleBlockTemplateEditor::initWidgets()
{
	QSettings settings;

	// undo list on the right
	undo_stack_ = new QUndoStack(this);
	undo_view_ = new QUndoView(undo_stack_);
	undo_view_ -> setEmptyLabel(tr("Aucune modification", "label displayed in the undo list when empty"));

	undo_dock_widget_ = new QDockWidget(tr("Annulations", "dock title"));
	undo_dock_widget_ -> setObjectName("tbt_undo_dock");
	undo_dock_widget_ -> setFeatures(
				QDockWidget::DockWidgetClosable
				|QDockWidget::DockWidgetMovable
				|QDockWidget::DockWidgetFloatable);
	undo_dock_widget_ -> setWidget(undo_view_);
	undo_dock_widget_ -> setMinimumWidth(290);
	addDockWidget(Qt::RightDockWidgetArea, undo_dock_widget_);

	// WYSIWYG editor as central widget
	template_edition_area_scene_ = new QGraphicsScene(this);
	template_edition_area_view_  = new TitleBlockTemplateView(template_edition_area_scene_);
	bool conv_ok;
	int conf_preview_width = settings.value("titleblocktemplateeditor/preview_width", -1).toInt(&conv_ok);
	if (conv_ok && conf_preview_width != -1) {
		template_edition_area_view_ -> setPreviewWidth(conf_preview_width);
	}
	setCentralWidget(template_edition_area_view_);

	// cell edition widget at the bottom
	template_cell_editor_widget_ = new TitleBlockTemplateCellWidget(tb_template_);
	template_cell_editor_dock_widget_ = new QDockWidget(tr("Propriétés de la cellule", "dock title"), this);
	template_cell_editor_dock_widget_ -> setObjectName("tbt_celleditor_dock");
	template_cell_editor_dock_widget_ -> setFeatures(
				QDockWidget::DockWidgetClosable
				|QDockWidget::DockWidgetMovable
				|QDockWidget::DockWidgetFloatable);
	template_cell_editor_dock_widget_ -> setWidget(template_cell_editor_widget_);
	template_cell_editor_dock_widget_ -> setMinimumWidth(180);
	template_cell_editor_dock_widget_ -> setMinimumHeight(250);
	addDockWidget(Qt::BottomDockWidgetArea, template_cell_editor_dock_widget_);
	template_cell_editor_widget_ -> setVisible(false);

	connect(
		template_edition_area_view_,
		SIGNAL(selectedCellsChanged(QList<TitleBlockCell *>)),
		this,
		SLOT(selectedCellsChanged(QList<TitleBlockCell *>))
	);
	connect(template_cell_editor_widget_, SIGNAL(logoEditionRequested()),
		this, SLOT(editLogos()));
	connect(
		template_cell_editor_widget_,
		SIGNAL(cellModified(ModifyTitleBlockCellCommand *)),
		this,
		SLOT(pushCellUndoCommand(ModifyTitleBlockCellCommand *))
	);
	connect(
		template_edition_area_view_,
		SIGNAL(gridModificationRequested(TitleBlockTemplateCommand *)),
		this,
		SLOT(pushGridUndoCommand(TitleBlockTemplateCommand *))
	);
	connect(
		template_edition_area_view_,
		SIGNAL(previewWidthChanged(int,int)),
		this,
		SLOT(savePreviewWidthToApplicationSettings(int, int))
	);
	connect(undo_stack_, SIGNAL(cleanChanged(bool)),
		this, SLOT(updateEditorTitle()));
	connect(QApplication::clipboard(), SIGNAL(dataChanged()),
		this, SLOT(updateActions()));
}

/**
	Initialize the logo manager
*/
void QETTitleBlockTemplateEditor::initLogoManager()
{
	logo_manager_ = new TitleBlockTemplateLogoManager(tb_template_, this);
	logo_manager_ -> setReadOnly(read_only_);
	connect(
		logo_manager_,
		SIGNAL(logosChanged(const TitleBlockTemplate *)),
		template_cell_editor_widget_,
		SLOT(updateLogosComboBox(const TitleBlockTemplate *))
	);
}

/**
	@return a string describing what is being edited, along with [Changed] or
	[Read only] tags. Useful to compose the window title.
*/
QString QETTitleBlockTemplateEditor::currentlyEditedTitle() const
{
	QString titleblock_title;
	if (opened_from_file_) {
		titleblock_title = filepath_;
	} else {
		titleblock_title = location_.name();
	}

	// if a (file)name has been added, also add a "[Changed]" tag if needed
	if (!titleblock_title.isEmpty()) {
		QString tag;
		if (!undo_stack_ -> isClean()) {
			tag = tr("[Modifié]", "window title tag");
		}
		if (read_only_) {
			tag = tr("[Lecture seule]", "window title tag");
		}
		titleblock_title = QString(
			tr(
				"%1 %2",
				"part of the window title - %1 is the filepath or template name, %2 is the [Changed] or [Read only] tag"
			)
		).arg(titleblock_title).arg(tag);
	}

	return(titleblock_title);
}

/**
	@brief QETTitleBlockTemplateEditor::readSettings
	Read settings
*/
void QETTitleBlockTemplateEditor::readSettings()
{
	QSettings settings;

	// window size and position
	QVariant geometry = settings.value("titleblocktemplateeditor/geometry");
	if (geometry.isValid()) restoreGeometry(geometry.toByteArray());

	// window state (toolbars, docks...)
	QVariant state = settings.value("titleblocktemplateeditor/state");
	if (state.isValid()) restoreState(state.toByteArray());
}

/**
	@brief QETTitleBlockTemplateEditor::writeSettings
	Write the settings
*/
void QETTitleBlockTemplateEditor::writeSettings()
{
	QSettings settings;
	settings.setValue("titleblocktemplateeditor/geometry", saveGeometry());
	settings.setValue("titleblocktemplateeditor/state", saveState());
}

/**
	Update various things when user changes the selected cells.
	@param selected_cells List of selected cells.
*/
void QETTitleBlockTemplateEditor::selectedCellsChanged(
		const QList<TitleBlockCell *>& selected_cells)
{
	if (selected_cells.count() == 1) {
		template_cell_editor_widget_ -> edit(selected_cells.at(0));
		template_cell_editor_widget_ -> setVisible(true);
	} else {
		template_cell_editor_widget_ -> setVisible(false);
	}
	updateActions();
}

/**
	Configure an undo Command before adding it to the undo stack.
	@param command to be added to the undo stack
*/
void QETTitleBlockTemplateEditor::pushCellUndoCommand(
		ModifyTitleBlockCellCommand *command)
{
	command -> setView(template_edition_area_view_);
	pushUndoCommand(command);
}

/**
	Add an undo Command to the undo stack.
	@param command QUndoCommand to be added to the undo stack
*/
void QETTitleBlockTemplateEditor::pushGridUndoCommand(
		TitleBlockTemplateCommand *command)
{
	pushUndoCommand(command);
}

/**
	Add an undo Command to the undo stack.
	@param command QUndoCommand to be added to the undo stack
*/
void QETTitleBlockTemplateEditor::pushUndoCommand(QUndoCommand *command) {
	undo_stack_ -> push(command);
}

/**
	Set the title of this editor.
*/
void QETTitleBlockTemplateEditor::updateEditorTitle()
{
	// base title
	QString min_title(
		tr(
			"QElectroTech - Éditeur de modèle de cartouche",
			"titleblock template editor: base window title"
		)
	);

	// get the currently edited template (file)name
	QString titleblock_title = currentlyEditedTitle();

	// generate the final window title
	QString title;
	if (titleblock_title.isEmpty()) {
		title = min_title;
	} else {
		title = QString(
			tr(
				"%1 - %2",
				"window title: %1 is the base window title, %2 is a template name"
			)
		).arg(min_title).arg(titleblock_title);
	}
	setWindowTitle(title);
}

/**
	Ensure the user interface remains consistent by enabling or disabling
	adequate actions.
*/
void QETTitleBlockTemplateEditor::updateActions()
{
	save_ -> setEnabled(!read_only_);

	bool can_merge = true;
	bool can_split = true;
	int count = 0;
	if (!read_only_) {
		template_edition_area_view_ -> analyzeSelectedCells(&can_merge, &can_split, &count);
	}
	cut_ -> setEnabled(!read_only_ && count);
	copy_ -> setEnabled(count);
	paste_ -> setEnabled(!read_only_ && count && template_edition_area_view_ -> mayPaste());
	add_row_ -> setEnabled(!read_only_);
	add_col_ -> setEnabled(!read_only_);
	merge_cells_ -> setEnabled(!read_only_ && can_merge);
	split_cell_ -> setEnabled(!read_only_ && can_split);
}

/**
	Save the template under the provided location.
	@see QETProject::setTemplateXmlDescription()
	@param location Location where the title block template should be saved.
*/
bool QETTitleBlockTemplateEditor::saveAs(const TitleBlockTemplateLocation &location) {
	TitleBlockTemplatesCollection *collection = location.parentCollection();
	if (!collection) return(false);

	QDomDocument doc;
	QDomElement elmt = doc.createElement("root");
	tb_template_ -> saveToXmlElement(elmt);
	elmt.setAttribute("name", location.name());
	doc.appendChild(elmt);

	collection -> setTemplateXmlDescription(location.name(), elmt);

	opened_from_file_ = false;
	location_ = location;
	undo_stack_ -> setClean();
	setReadOnly(false);
	return(true);
}

/**
	Save the template in the provided filepath.
	@see TitleBlockTemplate::saveToXmlFile()
	@param filepath location Location where the title block template should be saved.
*/
bool QETTitleBlockTemplateEditor::saveAs(const QString &filepath) {
	bool saving = tb_template_ -> saveToXmlFile(filepath);
	if (!saving) return(false);

	opened_from_file_ = true;
	filepath_ = filepath;
	undo_stack_ -> setClean();
	setReadOnly(false);
	return(true);
}

/**
	Ask the user to choose a title block template from the known collections
	then open it for edition.
*/
void QETTitleBlockTemplateEditor::open()
{
	TitleBlockTemplateLocation location = getTitleBlockTemplateLocationFromUser(
		tr("Ouvrir un modèle", "File > open dialog window title"),
		true
	);
	if (location.isValid()) {
		QETApp::instance() -> openTitleBlockTemplate(location);
	}
}

/**
	Ask the user to choose a file supposed to contain a title block template,
	then open it for edition.
*/
void QETTitleBlockTemplateEditor::openFromFile()
{
	// directory to show
	QString initial_dir = filepath_.isEmpty()
			? QETApp::customTitleBlockTemplatesDir()
			: QDir(filepath_).absolutePath();

	// ask the user to choose a filepath
	QString user_filepath = QFileDialog::getOpenFileName(
		this,
		tr("Ouvrir un fichier", "dialog title"),
		initial_dir,
		tr(
			"Modèles de cartouches QElectroTech (*%1);;"
			"Fichiers XML (*.xml);;"
			"Tous les fichiers (*)",
			"filetypes allowed when opening a title block template file"
			" - %1 is the .titleblock extension"
		).arg(QString(TITLEBLOCKS_FILE_EXTENSION))
	);


	if (!user_filepath.isEmpty()) QETApp::instance() -> openTitleBlockTemplate(user_filepath);
}

/**
	Save the currently edited title block template back to its parent project.
*/
bool QETTitleBlockTemplateEditor::save()
{
	if (opened_from_file_) {
		if (!filepath_.isEmpty()) {
			QFileInfo file_path_info(filepath_);
			if (file_path_info.isWritable()) {
				return(saveAs(filepath_));
			}
		}
		return(saveAsFile());
	} else {
		if (location_.isValid()) {
			if (!location_.isReadOnly()) {
				return(saveAs(location_));
			}
		}
		return(saveAs());
	}
}

/**
	Ask the user where he wishes to save the currently edited template.
*/
bool QETTitleBlockTemplateEditor::saveAs()
{
	TitleBlockTemplateLocation location = getTitleBlockTemplateLocationFromUser(
		tr("Enregistrer le modèle sous", "dialog window title"),
		false
	);
	if (location.isValid()) {
		return(saveAs(location));
	}
	return(false);
}

/**
	Ask the user where on the filesystem he wishes to save the currently edited template.
*/
bool QETTitleBlockTemplateEditor::saveAsFile()
{
	// directory to show
	QString initial_dir = filepath_.isEmpty()
			? QETApp::customTitleBlockTemplatesDir()
			: QDir(filepath_).absolutePath();

	// ask the user to choose a target file
	QString filepath = QFileDialog::getSaveFileName(
		this,
		tr("Enregistrer sous", "dialog title"),
		initial_dir,
		tr(
			"Modèles de cartouches QElectroTech (*%1)",
			"filetypes allowed when saving a title block template file - %1 is the .titleblock extension"
		).arg(QString(TITLEBLOCKS_FILE_EXTENSION))
	);

	// if no name was entered, return false
	if (filepath.isEmpty()) return(false);

	// if the name does not end with ".titleblock", add it
	if (!filepath.endsWith(".titleblock", Qt::CaseInsensitive)) filepath += ".titleblock";

	// attempts to save the file
	bool saving = saveAs(filepath);

	// retourne un booleen representatif de la reussite de l'enregistrement
	return(saving);
}

/**
	@param read_only True to restrict this editor to visualization of the
	currently edited template, false to allow full edition.
*/
void QETTitleBlockTemplateEditor::setReadOnly(bool read_only) {
	if (read_only != read_only_) {
		read_only_ = read_only;
		if (logo_manager_) {
			logo_manager_ -> setReadOnly(read_only_);
		}
		template_cell_editor_widget_ -> setReadOnly(read_only_);
		template_edition_area_view_ -> setReadOnly(read_only_);
	}
	updateActions();
	updateEditorTitle();
}

/**
	Ask the user for a title block template location
	@param title Title displayed by the dialog window
	@param existing_only True for the user to be forced to choose an existing
	template, false if he may specify the template name
	@return The location chosen by the user, or an empty
	TitleBlockTemplateLocation if the user cancelled the dialog
*/
TitleBlockTemplateLocation QETTitleBlockTemplateEditor::getTitleBlockTemplateLocationFromUser(
		const QString &title, bool existing_only)
{
	TitleBlockTemplateLocationChooser *widget;
	if (existing_only) {
		widget = new TitleBlockTemplateLocationChooser(location());
	} else {
		widget = new TitleBlockTemplateLocationSaver(location());
	}
	QDialogButtonBox *buttons = new QDialogButtonBox(
				QDialogButtonBox::Ok | QDialogButtonBox::Cancel);

	QVBoxLayout *dialog_layout = new QVBoxLayout();
	dialog_layout -> addWidget(widget);
	dialog_layout -> addWidget(buttons);

	QDialog dialog;
	dialog.setWindowTitle(title);
	dialog.setLayout(dialog_layout);

	connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept()));
	connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject()));

	if (dialog.exec() == QDialog::Accepted) {
		return(widget -> location());
	}
	return TitleBlockTemplateLocation();
}

/**
	Close the current editor.
*/
void QETTitleBlockTemplateEditor::quit()
{
	close();
}

/**
	@brief QETTitleBlockTemplateEditor::savePreviewWidthToApplicationSettings
	Save the new preview width to application settings
	@param former_preview_width : former_preview_width Unused, former preview width
	@param new_preview_width : new_preview_width New preview width
*/
void QETTitleBlockTemplateEditor::savePreviewWidthToApplicationSettings(
		int former_preview_width, int new_preview_width)
{
	Q_UNUSED(former_preview_width)
	QSettings settings;
	settings.setValue("titleblocktemplateeditor/preview_width", new_preview_width);
}

/**
	Edit extra information attached to the template.
*/
void QETTitleBlockTemplateEditor::editTemplateInformation()
{
	if (!tb_template_) return;

	QDialog dialog_author(this);
	dialog_author.setModal(true);
#ifdef Q_OS_MACOS
	dialog_author.setWindowFlags(Qt::Sheet);
#endif
	dialog_author.setMinimumSize(400, 260);
	dialog_author.setWindowTitle(tr("Éditer les informations complémentaires", "window title"));
	QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog_author);

	// explanation label
	QLabel *information_label = new QLabel(tr("Vous pouvez utiliser ce champ libre pour mentionner les auteurs du cartouche, sa licence, ou tout autre renseignement que vous jugerez utile."));
	information_label -> setAlignment(Qt::AlignJustify | Qt::AlignVCenter);
	information_label -> setWordWrap(true);
	dialog_layout -> addWidget(information_label);

	// add a QTextEdit to the dialog
	QTextEdit *text_field = new QTextEdit();
	text_field -> setAcceptRichText(false);
	text_field -> setPlainText(tb_template_ -> information());
	text_field -> setReadOnly(read_only_);
	dialog_layout -> addWidget(text_field);

	// add two buttons to the dialog
	QDialogButtonBox *dialog_buttons = new QDialogButtonBox(
				read_only_
				? QDialogButtonBox::Ok
				: QDialogButtonBox::Ok
				  | QDialogButtonBox::Cancel);
	dialog_layout -> addWidget(dialog_buttons);
	connect(dialog_buttons, SIGNAL(accepted()),
		&dialog_author, SLOT(accept()));
	connect(dialog_buttons, SIGNAL(rejected()),
		&dialog_author, SLOT(reject()));

	// run the dialog
	if (dialog_author.exec() == QDialog::Accepted && !read_only_) {
		QString new_info = text_field -> toPlainText().remove(QChar(13)); // CR-less text
		if (new_info != tb_template_ -> information()) {
			pushUndoCommand(new ChangeTemplateInformationsCommand(
						tb_template_,
						tb_template_ -> information(),
						new_info));
		}
	}
}
