/*
	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 "qetelementeditor.h"
#include "ui_qetelementeditor.h"
#include "../elementscene.h"
#include "../../qeticons.h"
#include "../elementview.h"
#include "../../qetmessagebox.h"
#include "../../qetapp.h"
#include "../../recentfiles.h"
#include "../graphicspart/customelementpart.h"
#include "../elementitemeditor.h"
#include "../styleeditor.h"
#include "../esevent/eseventaddline.h"
#include "../esevent/eseventaddrect.h"
#include "../esevent/eseventaddellipse.h"
#include "../esevent/eseventaddpolygon.h"
#include "../esevent/eseventaddarc.h"
#include "../esevent/eseventaddtext.h"
#include "../esevent/eseventaddterminal.h"
#include "../esevent/eseventadddynamictextfield.h"
#include "../../elementdialog.h"
#include "../graphicspart/partterminal.h"
#include "../arceditor.h"
#include "ellipseeditor.h"
#include "lineeditor.h"
#include "polygoneditor.h"
#include "rectangleeditor.h"
#include "terminaleditor.h"
#include "texteditor.h"
#include "dynamictextfieldeditor.h"
#include "../../newelementwizard.h"
#include "../editorcommands.h"
#include "../../dxf/dxftoelmt.h"
#include "../UndoCommand/openelmtcommand.h"

#include <QSettings>
#include <QActionGroup>

/**
  Number max of primitive displayed by the parts list widget
  */
#define QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST 200

/**
 * @brief QETElementEditor::QETElementEditor
 * @param parent
 */
QETElementEditor::QETElementEditor(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::QETElementEditor)
{
	ui->setupUi(this);
	initGui();
	setupActions();
	setupConnection();

	auto menu = createPopupMenu();
	menu->setTearOffEnabled(true);
	menu->setTitle(tr("Afficher", "menu entry"));
	menu->setIcon(QET::Icons::ConfigureToolbars);
	ui->m_display_menu->addMenu(menu);
	//ui->m_display_menu->insertMenu(ui->m_zoom_in_action, menu);

	setWindowState(Qt::WindowMaximized);
	readSettings();
	show();
}

/**
 * @brief QETElementEditor::~QETElementEditor
 */
QETElementEditor::~QETElementEditor()
{
	delete ui;
	qDeleteAll(m_editors.begin(), m_editors.end());
}

/**
 * @brief QETElementEditor::contextMenu
 * Display a context menu, with all available action.
 * @param p : the pos of the menu, in screen coordinate
 * @param actions : a list of actions who can be prepended to the context menu.
 */
void QETElementEditor::contextMenu(QPoint p, QList<QAction *> actions)
{
	QMenu menu(this);
	menu.addActions(std::move(actions));
	menu.addSeparator();
	menu.addAction(m_undo_action);
	menu.addAction(m_redo_action);
	menu.addAction(ui->m_select_all_act);
	menu.addAction(ui->m_deselect_all_action);
	menu.addAction(ui->m_revert_selection_action);
	menu.addSeparator();
	menu.addAction(ui->m_delete_action);
	menu.addAction(ui->m_cut_action);
	menu.addAction(ui->m_copy_action);
    menu.addAction((ui->m_rotate_action));
	menu.addSeparator();
	menu.addAction(ui->m_paste_action);
	menu.addAction(ui->m_paste_in_area_action);
	menu.addMenu(ui->m_paste_from_menu);
	menu.addSeparator();
	menu.addActions(m_depth_action_group -> actions());

		//Remove from the context menu the actions which are disabled.
	const QList<QAction *>menu_actions = menu.actions();
	for(QAction *action : menu_actions) {
		if(!action -> isEnabled()) {
			menu.removeAction(action);
		}
	}
	menu.exec(p);
}

/**
 * @brief QETElementEditor::setNames
 * Set the names list of the edited element
 * @param name_list
 */
void QETElementEditor::setNames(const NamesList &name_list)
{
	auto data = m_elmt_scene->elementData();
	data.m_names_list = name_list;
	m_elmt_scene->setElementData(data);
}

/**
 * @brief QETElementEditor::setLocation
 * Set the location to edit
 * @param location
 */
void QETElementEditor::setLocation(const ElementsLocation &location)
{
	m_location = location;
	m_opened_from_file = false;
	setReadOnly(!location.isWritable());
	updateTitle();

}

/**
 * @brief QETElementEditor::location
 * @return The location of the edited element
 */
ElementsLocation QETElementEditor::location() const {
	return m_location;
}

/**
 * @brief QETElementEditor::setFileName
 * Set the file name of the edited element
 * @param file_name
 */
void QETElementEditor::setFileName(const QString &file_name)
{
	m_file_name = file_name;
	m_opened_from_file = true;
	bool must_be_read_only = !QFileInfo(file_name).isWritable();
	if (isReadOnly() != must_be_read_only) {
		setReadOnly(must_be_read_only);
	}
	updateTitle();
}

/**
 * @brief QETElementEditor::fileName
 * @return the file name of this element
 */
QString QETElementEditor::fileName() const {
	return m_file_name;
}

void QETElementEditor::setReadOnly(bool ro)
{
	m_read_only = ro;
	m_view->setInteractive(!ro);
	updateAction();
}

bool QETElementEditor::isReadOnly() const {
	return m_read_only;
}

/**
 * @brief QETElementEditor::fromFile
 * Open an element from @filepath
 * @param filepath
 */
void QETElementEditor::fromFile(const QString &filepath)
{
	bool state_ = true;
	QString error_message;


	QFileInfo infos_file(filepath);
	if (!infos_file.exists() || !infos_file.isFile())
	{
		state_ = false;
		error_message = QString(tr("Le fichier %1 n'existe pas.", "message box content")).arg(filepath);
	}

	QFile file(filepath);
	if (state_ && !file.open(QIODevice::ReadOnly)) {
		state_ = false;
		error_message = QString(tr("Impossible d'ouvrir le fichier %1.", "message box content")).arg(filepath);
	}

	QDomDocument document_xml;
	if (state_) {
		if (!document_xml.setContent(&file)) {
			state_ = false;
			error_message = tr("Ce fichier n'est pas un document XML valide", "message box content");
		}
		file.close();
	}

	if (!state_) {
		QET::QetMessageBox::critical(this, tr("Erreur", "toolbar title"), error_message);
		return;
	}

	m_elmt_scene -> fromXml(document_xml);
	fillPartsList();

	if (!infos_file.isWritable())
	{
		QET::QetMessageBox::warning(
			this,
			tr("Édition en lecture seule", "message box title"),
			tr("Vous n'avez pas les privilèges nécessaires pour modifier cet élement. Il sera donc ouvert en lecture seule.", "message box content")
		);
		setReadOnly(true);
	}
	else {
		setReadOnly(false);
	}

	// memorise le fichier
	setFileName(filepath);
	QETApp::elementsRecentFiles() -> fileWasOpened(filepath);
	updateAction();
}

/**
 * @brief QETElementEditor::toFile
 * Save to a file the current edited element
 * @param filepath : path of the file
 * @return true if success
 */
bool QETElementEditor::toFile(const QString &filepath)
{
	m_elmt_scene -> clearEventInterface();
	m_elmt_scene -> clearSelection();
	UncheckAddPrimitive();

	QDomDocument element_xml = m_elmt_scene -> toXml();
	bool writing = QET::writeXmlFile(element_xml, filepath);
	if (!writing) {
		QET::QetMessageBox::warning(
			this,
			tr("Erreur", "message box title"),
			tr("Impossible d'écrire dans ce fichier", "message box content")
		);
	}
	return(writing);
}

/**
 * @brief QETElementEditor::fromLocation
 * Location of an element to edit.
 */
void QETElementEditor::fromLocation(const ElementsLocation &location)
{
	if (!location.isElement()) {
		QET::QetMessageBox::critical(this,
									 tr("Élément inexistant.", "message box title"),
									 tr("Le chemin virtuel choisi ne correspond pas à un élément.", "message box content"));
		return;
	}
	if (!location.exist()) {
		QET::QetMessageBox::critical(this,
									 tr("Élément inexistant.", "message box title"),
									 tr("L'élément n'existe pas.", "message box content"));
		return;
	}

		//The file must be an xml document
	QDomDocument document_xml;
	QDomNode node = document_xml.importNode(location.xml(), true);
	document_xml.appendChild(node);

		//Load the element
	m_elmt_scene -> fromXml(document_xml);
	fillPartsList();

		//location is read only
	if (!location.isWritable()) {
		QET::QetMessageBox::warning(this,
									tr("Édition en lecture seule", "message box title"),
									tr("Vous n'avez pas les privilèges nécessaires pour modifier cet élement. Il sera donc ouvert en lecture seule.", "message box content"));
		setReadOnly(true);
	}
	else {
		setReadOnly(false);
	}

	setLocation(location);
	updateAction();
}

/**
 * @brief QETElementEditor::toLocation
 * Save the edited element into @location
 * @param location
 * @return true if successfully saved
 */
bool QETElementEditor::toLocation(const ElementsLocation &location)
{
	m_elmt_scene -> clearEventInterface();
	m_elmt_scene -> clearSelection();
	UncheckAddPrimitive();

	if (!location.setXml(m_elmt_scene -> toXml())) {
		QET::QetMessageBox::critical(this,
									 tr("Erreur", "message box title"),
									 tr("Impossible d'enregistrer l'élément", "message box content"));
		return(false);
	}
	return(true);
}

/**
 * @brief QETElementEditor::isEditing
 * @param location
 * @return true if this editor is currently
 * editing element of location
 */
bool QETElementEditor::isEditing(const ElementsLocation &location)
{
	if (m_opened_from_file)
	{
		auto r = QET::compareCanonicalFilePaths(
					 m_file_name,
					 QETApp::realPath(location.toString()));
		return r;
	}
	else {
		return(location == m_location);
	}
}

/**
 * @brief QETElementEditor::isEditing
 * @param filepath
 * @return true if this editor is currently
 * editing element at filepath.
 */
bool QETElementEditor::isEditing(const QString &filepath)
{
	QString current_filepath;
	if (m_opened_from_file) {
		current_filepath = m_file_name;
	} else {
		current_filepath = QETApp::realPath(m_location.toString());
	}

	auto r = QET::compareCanonicalFilePaths(
				 current_filepath,
				 filepath);
	return r;
}

/**
 * @brief QETElementEditor::elementScene
 * @return
 */
ElementScene *QETElementEditor::elementScene() const {
	return m_elmt_scene;
}

/**
 * @brief QETElementEditor::elementView
 * @return
 */
ElementView *QETElementEditor::elementView() const {
	return m_view;
}

/**
 * @brief QETElementEditor::pasteOffset
 * @return the vertical and horizontal paste offset
 */
QPointF QETElementEditor::pasteOffset()
{
	QPointF paste_offset(5.0, 0.0);
	return(paste_offset);
}

/**
 * @brief QETElementEditor::getOpenElementFileName
 * Ask user to open a file who must be an element.
 * @param parent
 * @param dir
 * @return
 */
QString QETElementEditor::getOpenElementFileName(QWidget *parent, const QString &dir)
{
	QString user_filename = QFileDialog::getOpenFileName(
		parent,
		tr("Ouvrir un fichier", "dialog title"),
		dir.isEmpty() ? QETApp::customElementsDir() : dir,
		tr(
			"Éléments QElectroTech (*.elmt);;"
			"Fichiers XML (*.xml);;"
			"Tous les fichiers (*)",
			"filetypes allowed when opening an element file"
		)
	);
	return(user_filename);
}

/**
 * @brief QETElementEditor::updateTitle
 * Update the title of the windows
 */
void QETElementEditor::updateTitle()
{
	QString title = m_min_title;
	title += " - " + m_elmt_scene->elementData().m_names_list.name() + " ";
	if (!m_file_name.isEmpty() || !m_location.isNull()) {
		if (!m_elmt_scene -> undoStack().isClean()) {
			title += tr("[Modifié]", "window title tag");
		}
	}
	if (isReadOnly()) {
		title += tr(" [lecture seule]", "window title tag");
	}
	setWindowTitle(title);
}

/**
 * @brief QETElementEditor::fillPartsList
 */
void QETElementEditor::fillPartsList()
{
	m_parts_list -> blockSignals(true);
	m_parts_list -> clear();
	QList<QGraphicsItem *> qgis = m_elmt_scene -> zItems();

	if (qgis.count() <= QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST)
	{
		for (int j = qgis.count() - 1 ; j >= 0 ; -- j)
		{
			QGraphicsItem *qgi = qgis[j];
			if (CustomElementPart *cep = dynamic_cast<CustomElementPart *>(qgi))
			{
				QString part_desc = cep -> name();
				QListWidgetItem *qlwi = new QListWidgetItem(part_desc);
				QVariant v;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)	// ### Qt 6: remove
				v.setValue<QGraphicsItem *>(qgi);
#else
#if TODO_LIST
#pragma message("@TODO remove code for QT 6 or later")
#endif
				qDebug()<<"Help code for QT 6 or later";
#endif
				qlwi -> setData(42, v);
				m_parts_list -> addItem(qlwi);
				qlwi -> setSelected(qgi -> isSelected());
			}
		}
	}
	else {
		m_parts_list -> addItem(new QListWidgetItem(tr("Trop de primitives, liste non générée.")));
	}
	m_parts_list -> blockSignals(false);
}

/**
 * @brief QETElementEditor::UncheckAddPrimitive
 */
void QETElementEditor::UncheckAddPrimitive() {
	for (auto action : m_add_part_action_grp->actions()) {
		action->setChecked(false);
	}
}

/**
 * @brief QETElementEditor::updateCurrentPartEditor
 */
void QETElementEditor::updateCurrentPartEditor()
{
	if (!m_tools_dock_stack -> currentIndex()) {
		return;
	}

	if (ElementItemEditor *current_editor = dynamic_cast<ElementItemEditor *>(m_tools_dock_stack -> widget(1))) {
		current_editor -> updateForm();
	}
}

/**
 * @brief QETElementEditor::updateInformations
 * Update the information and editor dock.
 */
void QETElementEditor::updateInformations()
{
	QList<QGraphicsItem *> selected_qgis = m_elmt_scene -> selectedItems();
	if (selected_qgis.isEmpty())
	{
		clearToolsDock();
		m_default_informations -> setText(tr("%n partie(s) sélectionnée(s).",
											 "",
											 selected_qgis.size()));
		m_default_informations -> setAlignment(Qt::AlignHCenter | Qt::AlignTop);
		m_tools_dock_stack -> setCurrentIndex(0);
		return;
	}

	QList<CustomElementPart *> cep_list;

	CustomElementPart* part = dynamic_cast<CustomElementPart *>(selected_qgis.first());
	QString selection_xml_name = part -> xmlName();
	bool same_xml_name = true;
	bool style_editable = true;
	for (QGraphicsItem *qgi: selected_qgis)
	{
		if (CustomElementPart *cep = dynamic_cast<CustomElementPart *>(qgi)) {
			cep_list << cep;
			if (cep -> xmlName() != selection_xml_name) {
				same_xml_name = false;
			}
		}
		else {
			style_editable = false;
			same_xml_name = false;
		}
	}
	if (style_editable) {
		style_editable = StyleEditor::isStyleEditable(cep_list);
	}

	if (same_xml_name)
	{
		if (   selection_xml_name == "text"
			|| selection_xml_name == "dynamic_text"
			|| selection_xml_name == "line"
			|| selection_xml_name == "rect"
			|| selection_xml_name == "ellipse"
			|| selection_xml_name == "arc")
		{
			clearToolsDock();
			//We add the editor widget
			ElementItemEditor *editor = static_cast<ElementItemEditor*>(m_editors[selection_xml_name]);

#if TODO_LIST
#pragma message("@TODO Check if it takes longer than setting the parts again to the editor.")
#endif
			// TODO: Check if it takes longer than setting the parts again to the editor.
			bool equal = true;
			QList<CustomElementPart*> parts = editor -> currentParts();
			if (parts.length() == cep_list.length()) {
				for (auto cep: cep_list) {
					bool part_found = false;
					for (auto part: parts) {
						if (part == cep) {
							part_found = true;
							break;
						}
					}
					if (!part_found) {
						equal = false;
						break;
					}
				}
			}
			else {
				equal = false;
			}

			if (editor) {
				bool success = true;
				if (equal == false) {
					success = editor -> setParts(cep_list);
				}
				if (success) {
					m_tools_dock_stack -> insertWidget(1, editor);
					m_tools_dock_stack -> setCurrentIndex(1);
				}
				else {
					qDebug() << "Editor refused part.";
				}
			}
			return;
		}
		else if (cep_list.length() == 1 &&
				 (selection_xml_name == "polygon" || selection_xml_name == "terminal"))
		{
#if TODO_LIST
#pragma message("@TODO maybe allowing multipart edit when number of points is the same?")
#endif
			// multi edit for polygons makes no sense
			// TODO: maybe allowing multipart edit when number of points is the same?
			//We add the editor widget
			clearToolsDock();
			ElementItemEditor *editor = static_cast<ElementItemEditor*>(m_editors[selection_xml_name]);
			CustomElementPart* part = editor -> currentPart();
			bool equal = part == cep_list.first();

			if (editor) {
				bool success = true;
				if (equal == false) {
					success = editor -> setPart(cep_list.first());
				}
				if (success) {
					m_tools_dock_stack -> insertWidget(1, editor);
					m_tools_dock_stack -> setCurrentIndex(1);
				}
				else {
					qDebug() << "Editor refused part.";
				}
			}
			return;

		}
		else {
			qDebug() << "Multiedit not supported for: " << cep_list.first() -> xmlName();
		}

	}

	//There's several parts selecteds and all can be edited by style editor.
	if (style_editable) {
		clearToolsDock();
		ElementItemEditor *selection_editor = m_editors["style"];
		if (selection_editor) {
			if (selection_editor -> setParts(cep_list)) {
				m_tools_dock_stack -> insertWidget(1, selection_editor);
				m_tools_dock_stack -> setCurrentIndex(1);
			}
			else {
				qDebug() << "Editor refused part.";
			}
		}
	}
	//Else we only display the number of selected items
	else {
		clearToolsDock();
		m_default_informations -> setText(tr("%n partie(s) sélectionnée(s).",
											 "",
											 selected_qgis.size()));
		m_default_informations -> setAlignment(Qt::AlignHCenter | Qt::AlignTop);
		m_tools_dock_stack -> setCurrentIndex(0);
	}
}

/**
 * @brief QETElementEditor::updatePartsList
 */
void QETElementEditor::updatePartsList()
{
	int items_count = m_elmt_scene -> items().count();
	if (m_parts_list -> count() != items_count) {
		fillPartsList();
	}
	else if (items_count <= QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST) {
		m_parts_list -> blockSignals(true);
		int i = 0;
		QList<QGraphicsItem *> items = m_elmt_scene -> zItems();
		for (int j = items.count() - 1 ; j >= 0 ; -- j) {
			QGraphicsItem *qgi = items[j];
			QListWidgetItem *qlwi = m_parts_list -> item(i);
			if (qlwi) qlwi -> setSelected(qgi -> isSelected());
			++ i;
		}
		m_parts_list -> blockSignals(false);
	}
}

/**
 * @brief QETElementEditor::updateSelectionFromPartsList
 */
void QETElementEditor::updateSelectionFromPartsList()
{
	m_elmt_scene  -> blockSignals(true);
	m_parts_list -> blockSignals(true);
	for (int i = 0 ; i < m_parts_list -> count() ; ++ i) {
		QListWidgetItem *qlwi = m_parts_list -> item(i);
		QGraphicsItem *qgi = qlwi -> data(42).value<QGraphicsItem *>();
		if (qgi) {
			qgi -> setSelected(qlwi -> isSelected());
		}
	}
	m_parts_list -> blockSignals(false);
	m_elmt_scene -> blockSignals(false);
	updateInformations();
	updateAction();
}

/**
 * @brief QETElementEditor::checkElement
 * Do several check about element
 * @return true if there is no error
 */
bool QETElementEditor::checkElement()
{
		//List of warning and error
	typedef QPair<QString, QString> QETWarning;
	QList<QETWarning> warnings;
	QList<QETWarning> errors;

		// Warning #1: Element haven't got terminal
		// (except for report, because report must have one terminal and this checking is do below)
	if (!m_elmt_scene -> containsTerminals() &&
		!(m_elmt_scene->elementData().m_type & ElementData::AllReport)) {
		warnings << qMakePair(
						tr("Absence de borne", "warning title"),
						tr(
							"<br>En l'absence de borne, l'élément ne pourra être"
			" relié à d'autres éléments par l'intermédiaire de conducteurs.",
							"warning description"
		)
						);
	}

		// Check folio report element
	if (m_elmt_scene->elementData().m_type & ElementData::AllReport)
	{
		int terminal =0;

		for(auto qgi : m_elmt_scene -> items()) {
			if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
				terminal ++;
			}
		}

			//Error folio report must have only one terminal
		if (terminal != 1) {
			errors << qMakePair (tr("Absence de borne"),
								 tr("<br><b>Erreur</b> :"
								"<br>Les reports de folio doivent posséder une seul borne."
								"<br><b>Solution</b> :"
								"<br>Verifier que l'élément ne possède qu'une seul borne"));
		}
	}

	if (!errors.count() && !warnings.count()) {
		return(true);
	}

		// Display warnings
	QString dialog_message = tr("La vérification de cet élément a généré", "message box content");

	if (errors.size()) {
		dialog_message += QString(tr(" %n erreur(s)", "errors", errors.size()));
	}

	if (warnings.size()) {
		if (errors.size()) {
			dialog_message += QString (tr(" et"));
		}
		dialog_message += QString (tr(" %n avertissement(s)", "warnings", warnings.size()));
	}
	dialog_message += " :";

	dialog_message += "<ol>";
	QList<QETWarning> total = warnings << errors;
	for(QETWarning warning : total)
	{
		dialog_message += "<li>";
		dialog_message += QString(
							  tr("<b>%1</b> : %2", "warning title: warning description")
							  ).arg(warning.first).arg(warning.second);
		dialog_message += "</li>";
	}
	dialog_message += "</ol>";

	if (errors.size()) {
		QMessageBox::critical(this, tr("Erreurs"), dialog_message);
	}
	else {
		QMessageBox::warning(this, tr("Avertissements"), dialog_message);
	}

		//if error == 0 that means they are only warning, we return true.
	if (errors.count() == 0) {
		return(true);
	}
	return false;
}

/**
 * @brief QETElementEditor::event
 * @param event
 * @return
 */
bool QETElementEditor::event(QEvent *event)
{
	if (m_first_activation && event->type() == QEvent::WindowActivate) {
		m_first_activation = false;
		QTimer::singleShot(250, m_view, SLOT(zoomFit()));
	}

	return QMainWindow::event(event);
}

/**
 * @brief QETElementEditor::openElement
 * OPen an element from @filepath
 * @param filepath
 */
void QETElementEditor::openElement(const QString &filepath)
{
	if (filepath.isEmpty()) {
		return;
	}
		// we have to test the file existence here because QETApp::openElementFiles()
		// will discard non-existent files through QFileInfo::canonicalFilePath()
	if (!QFile::exists(filepath)) {
		QET::QetMessageBox::critical(
			this,
			tr("Impossible d'ouvrir le fichier", "message box title"),
			QString(
				tr("Il semblerait que le fichier %1 que vous essayez d'ouvrir"
				" n'existe pas ou plus.")
			).arg(filepath)
		);
	}
	QETApp::instance() -> openElementFiles(QStringList() << filepath);
}

/**
 * @brief QETElementEditor::closeEvent
 * @param qce
 */
void QETElementEditor::closeEvent(QCloseEvent *qce)
{
	if (canClose()) {
		writeSettings();
		setAttribute(Qt::WA_DeleteOnClose);
		m_elmt_scene -> reset();
		qce -> accept();
	}
	else {
		qce -> ignore();
	}
}

/**
 * @brief QETElementEditor::canClose
 * @return
 */
bool QETElementEditor::canClose()
{
	if (m_elmt_scene -> undoStack().isClean()) {
		return(true);
	}
		//First ask user to save
	QMessageBox::StandardButton answer = QET::QetMessageBox::question(
		this,
		tr("Enregistrer l'élément en cours ?", "dialog title"),
		QString(
			tr(
				"Voulez-vous enregistrer l'élément %1 ?",
				"dialog content - %1 is an element name"
			)
		).arg(m_elmt_scene->elementData().m_names_list.name()),
		QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
		QMessageBox::Cancel
	);

	bool result;
	switch(answer) {
		case QMessageBox::Cancel: {
			result = false;
			break;
		}
		case QMessageBox::Yes: {
			result = on_m_save_action_triggered();
			break;
		}
		default: {
			result = true;
		}
	}
	return(result);
}

/**
 * @brief QETElementEditor::readSettings
 * Read some setting about this widget in the QSetting
 * of qelectrotech
 */
void QETElementEditor::readSettings()
{
	QSettings settings;

	QVariant geometry = settings.value("elementeditor/geometry");
	if (geometry.isValid()) {
		restoreGeometry(geometry.toByteArray());
	}

	QVariant state = settings.value("elementeditor/state");
	if (state.isValid()) {
		restoreState(state.toByteArray());
	}

	auto data = m_elmt_scene->elementData();
	data.m_drawing_information = settings.value("elementeditor/default-informations", "").toString();
	m_elmt_scene->setElementData(data);
}

/**
 * @brief QETElementEditor::writeSettings
 * Write some setting of this widget in the
 * QSetting of qelectrotech
 */
void QETElementEditor::writeSettings() const
{
	QSettings settings;
	settings.setValue("elementeditor/geometry", saveGeometry());
	settings.setValue("elementeditor/state", saveState());
}

/**
 * @brief QETElementEditor::setupActions
 */
void QETElementEditor::setupActions()
{
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
	ui->m_open_dxf_action -> setStatusTip(tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
					 "\n"
					 ">> Install on Windows\n"
					 "Put DXFtoQET.exe binary on C:\\Users\\user_name\\AppData\\Roaming\\qet\\ directory \n"
					   ));
#elif defined(Q_OS_MAC)
	ui->m_open_dxf_action -> setStatusTip(tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
					 "\n"
					 ">> Install on macOSX\n"
					 "Put DXFtoQET.app binary on /Users/user_name/.qet/ directory \n"
					  ));
#else
	ui->m_open_dxf_action -> setStatusTip(tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
					 "\n"
					 ">> Install on Linux\n"
					 "Put DXFtoQET binary on your /home/user_name/.qet/ directory\n"
					 "make it executable : chmod +x ./DXFtoQET\n"
					  ));
#endif

	ui->m_open_dxf_action -> setWhatsThis (tr("To install the plugin DXFtoQET\nVisit https://download.tuxfamily.org/qet/builds/dxf_to_elmt/\n"
					 "\n"
					 ">> Install on Linux\n"
					 "Put DXFtoQET binary on your /home/user_name/.qet/ directory\n"
					 "make it executable : chmod +x ./DXFtoQET\n"
					 ">> Install on Windows\n"
					 "Put DXFtoQET.exe binary on C:\\Users\\user_name\\AppData\\Roaming\\qet\\ directory \n"
					 "\n"
					 ">> Install on macOSX\n"
					 "Put DXFtoQET.app binary on /Users/user_name/.qet/ directory \n"
					  ));

	m_undo_action = m_elmt_scene -> undoStack().createUndoAction(this, tr("Annuler"));
	m_redo_action = m_elmt_scene -> undoStack().createRedoAction(this, tr("Refaire"));
	m_undo_action -> setIcon(QET::Icons::EditUndo);
	m_redo_action -> setIcon(QET::Icons::EditRedo);
	m_undo_action -> setShortcuts(QKeySequence::Undo);
	m_redo_action -> setShortcuts(QKeySequence::Redo);
	ui->m_undo_toolbar->addAction(m_undo_action);
	ui->m_undo_toolbar->addAction(m_redo_action);

	ui->m_new_action              -> setShortcut(QKeySequence::New);
	ui->m_open_action             -> setShortcut(QKeySequence::Open);
	ui->m_open_from_file_action   -> setShortcut(tr("Ctrl+Shift+O"));
	ui->m_save_action             -> setShortcut(QKeySequence::Save);
	ui->m_save_as_file_action     -> setShortcut(tr("Ctrl+Shift+S"));
	ui->m_quit_action             -> setShortcut(QKeySequence(tr("Ctrl+Q")));
	ui->m_select_all_act          -> setShortcut(QKeySequence::SelectAll);
	ui->m_deselect_all_action     -> setShortcut(QKeySequence(tr("Ctrl+Shift+A")));
	ui->m_revert_selection_action -> setShortcut(QKeySequence(tr("Ctrl+I")));
	ui->m_cut_action              -> setShortcut(QKeySequence::Cut);
	ui->m_copy_action             -> setShortcut(QKeySequence::Copy);
	ui->m_paste_action            -> setShortcut(QKeySequence::Paste);
	ui->m_paste_in_area_action    -> setShortcut(tr("Ctrl+Shift+V"));
	ui->m_edit_names_action       -> setShortcut(QKeySequence(tr("Ctrl+E")));
	ui->m_edit_author_action      -> setShortcut(tr("Ctrl+Y"));

#ifndef Q_OS_MAC
	ui->m_delete_action -> setShortcut(QKeySequence(Qt::Key_Delete));
#else
	ui->m_delete_action -> setShortcut(QKeySequence(tr("Backspace")));
#endif

		//Depth action
	m_depth_action_group = QET::depthActionGroup(this);
	connect(m_depth_action_group, &QActionGroup::triggered, [this](QAction *action) {
		this -> elementScene() -> undoStack().push(
			new ChangeZValueCommand(this -> elementScene(), action -> data().value<QET::DepthOption>()));
		emit(this -> elementScene() -> partsZValueChanged());
	});
	auto depth_toolbar = addToolBar(tr("Profondeur", "toolbar title"));
	depth_toolbar -> setObjectName("depth_toolbar");
	depth_toolbar -> addActions(m_depth_action_group -> actions());
	addToolBar(Qt::TopToolBarArea, depth_toolbar);

        //Rotate action
	connect(ui->m_rotate_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new RotateElementsCommand(this->elementScene()));});

        //Zoom action
	ui->m_zoom_in_action       -> setShortcut(QKeySequence::ZoomIn);
	ui->m_zoom_out_action      -> setShortcut(QKeySequence::ZoomOut);
	ui->m_zoom_fit_best_action -> setShortcut(QKeySequence(tr("Ctrl+9")));
	ui->m_zoom_original_action -> setShortcut(QKeySequence(tr("Ctrl+0")));

		//Add primitive actions
	m_add_part_action_grp = new QActionGroup(this);

	auto *add_line               = new QAction(QET::Icons::PartLine,      tr("Ajouter une ligne"),                m_add_part_action_grp);
	auto *add_rectangle          = new QAction(QET::Icons::PartRectangle, tr("Ajouter un rectangle"),             m_add_part_action_grp);
	auto *add_ellipse            = new QAction(QET::Icons::PartEllipse,   tr("Ajouter une ellipse"),              m_add_part_action_grp);
	auto *add_polygon            = new QAction(QET::Icons::PartPolygon,   tr("Ajouter un polygone"),              m_add_part_action_grp);
	auto *add_text               = new QAction(QET::Icons::PartText,      tr("Ajouter du texte"),                 m_add_part_action_grp);
	auto *add_arc                = new QAction(QET::Icons::PartArc,       tr("Ajouter un arc de cercle"),         m_add_part_action_grp);
	auto *add_terminal           = new QAction(QET::Icons::Terminal,      tr("Ajouter une borne"),                m_add_part_action_grp);
	auto *add_dynamic_text_field = new QAction(QET::Icons::PartTextField, tr("Ajouter un champ texte dynamique"), m_add_part_action_grp);

	for (auto action : m_add_part_action_grp->actions()) {
		action -> setCheckable(true);
	}

	connect(add_line,      &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddLine(m_elmt_scene));});
	connect(add_rectangle, &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddRect(m_elmt_scene));});
	connect(add_ellipse,   &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddEllipse(m_elmt_scene));});
	connect(add_polygon,   &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddPolygon(m_elmt_scene));});
	connect(add_text,      &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddText(m_elmt_scene));});
	connect(add_arc,       &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddArc(m_elmt_scene));});
	connect(add_terminal,  &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddTerminal(m_elmt_scene));});
	connect(add_dynamic_text_field, &QAction::triggered, [this]() {m_elmt_scene->setEventInterface(new ESEventAddDynamicTextField(m_elmt_scene));});

	add_polygon -> setStatusTip(tr("Double-click pour terminer la forme, Click droit pour annuler le dernier point"));
	add_text    -> setStatusTip(tr("Ajouter un texte d'élément non éditable dans les schémas"));
	add_dynamic_text_field -> setStatusTip(tr("Ajouter un texte d'élément pouvant être édité dans les schémas"));

	auto parts_toolbar =  addToolBar(tr("Parties", "toolbar title"));
	parts_toolbar -> setAllowedAreas(Qt::AllToolBarAreas);
	parts_toolbar -> setObjectName("parts");
	parts_toolbar -> addActions(m_add_part_action_grp -> actions());
	addToolBar(Qt::LeftToolBarArea, parts_toolbar);
}

/**
 * @brief QETElementEditor::updateAction
 * Update actions
 */
void QETElementEditor::updateAction()
{
		//Action disabled if read only
	auto ro_list = m_add_part_action_grp->actions();
	ro_list << ui->m_select_all_act
			<< ui->m_revert_selection_action
			<< ui->m_paste_from_file_action
			<< ui->m_paste_from_element_action;
	for (auto action : qAsConst(ro_list)) {
		action->setDisabled(m_read_only);
	}

		//Action enabled if a primitive is selected
	auto select_list = m_depth_action_group->actions();
	select_list << ui->m_deselect_all_action
				<< ui->m_cut_action
				<< ui->m_copy_action
				<< ui->m_delete_action
				<< ui->m_rotate_action;
	auto items_selected = !m_read_only && m_elmt_scene->selectedItems().count();
	for (auto action : qAsConst(select_list)) {
		action->setEnabled(items_selected);
	}

		//Action about clipboard
	auto clipboard_contain_elmt = !m_read_only && ElementScene::clipboardMayContainElement();
	ui->m_paste_action->setEnabled(clipboard_contain_elmt);
	ui->m_paste_in_area_action->setEnabled(clipboard_contain_elmt);

		//Action about undo stack status
	ui->m_save_action-> setEnabled(!m_read_only && !m_elmt_scene->undoStack().isClean());
	m_undo_action    -> setEnabled(!m_read_only && m_elmt_scene -> undoStack().canUndo());
	m_redo_action    -> setEnabled(!m_read_only && m_elmt_scene -> undoStack().canRedo());

}

/**
 * @brief QETElementEditor::setupConnection
 */
void QETElementEditor::setupConnection()
{
	connect(m_elmt_scene, &ElementScene::partsAdded,          this, &QETElementEditor::UncheckAddPrimitive);
	connect(m_elmt_scene, &ElementScene::partsAdded,          this, &QETElementEditor::fillPartsList);
	connect(m_elmt_scene, &ElementScene::partsRemoved,        this, &QETElementEditor::fillPartsList);
	connect(m_elmt_scene, &ElementScene::partsZValueChanged,  this, &QETElementEditor::fillPartsList);
	connect(m_parts_list, &QListWidget::itemSelectionChanged, this, &QETElementEditor::updateSelectionFromPartsList);
	connect(QApplication::clipboard(),  &QClipboard::dataChanged, this, &QETElementEditor::updateAction);

	connect(m_elmt_scene, &ElementScene::selectionChanged, [this]() {
		this->updateInformations();
		this->updateAction();
		this->updatePartsList();
	});

	connect(&(m_elmt_scene -> undoStack()), &QUndoStack::cleanChanged, [this]() {
		this->updateAction();
		this->updateTitle();
	});

	connect(&(m_elmt_scene -> undoStack()), &QUndoStack::indexChanged, [this]() {
		this->updateInformations();
	});
}

/**
 * @brief QETElementEditor::initGui
 */
void QETElementEditor::initGui()
{
	m_editors["arc"]          = new ArcEditor(this);
	m_editors["ellipse"]      = new EllipseEditor(this);
	m_editors["line"]         = new LineEditor(this);
	m_editors["polygon"]      = new PolygonEditor(this);
	m_editors["rect"]         = new RectangleEditor(this);
	m_editors["terminal"]     = new TerminalEditor(this);
	m_editors["text"]         = new TextEditor(this);
	m_editors["style"]        = new StyleEditor(this);
	m_editors["dynamic_text"] = new DynamicTextFieldEditor(this);

	m_elmt_scene = new ElementScene(this, this);
	m_view = new ElementView(m_elmt_scene, this);
	m_view->setDragMode(QGraphicsView::RubberBandDrag);
	setCentralWidget(m_view);

		//Part dock editor
	m_default_informations = new QLabel(this);
	m_tools_dock_stack = new QStackedWidget(this);
	m_tools_dock_stack->insertWidget(0, m_default_informations);
	ui->m_tools_dock->setWidget(m_tools_dock_stack);

		//Undo dock
	auto undo_view = new QUndoView(&(m_elmt_scene->undoStack()), this);
	undo_view->setEmptyLabel(tr("Aucune modification"));
	ui->m_undo_dock->setWidget(undo_view);

		//parts list dock
	m_parts_list = new QListWidget(this);
	m_parts_list->setSelectionMode(QAbstractItemView::ExtendedSelection);
	tabifyDockWidget(ui->m_undo_dock, ui->m_parts_dock);
	ui->m_parts_dock->setWidget(m_parts_list);

	updateInformations();
	fillPartsList();

	statusBar()->showMessage(tr("Éditeur d'éléments", "status bar message"));
}

/**
 * @brief QETElementEditor::clearToolsDock
 * Remove and hide the widget displayed by
 * the dock used to edit primitives
 * @return Return the removed widget or nullptr if there is no widget to remove.
 */
QWidget *QETElementEditor::clearToolsDock()
{
	if (QWidget *previous_widget = m_tools_dock_stack -> widget(1)) {
		m_tools_dock_stack -> removeWidget(previous_widget);
		previous_widget -> hide();
		return(previous_widget);
	}
	return(nullptr);
}

/**
 * @brief QETElementEditor::copyAndPasteXml
 * Copy the content of @xml_document to the clipboard
 * and call pasteInArea method of elementView.
 * @param xml_document
 */
void QETElementEditor::copyAndPasteXml(const QDomDocument &xml_document)
{
	QClipboard *clipboard = QApplication::clipboard();
	QString clipboard_content = xml_document.toString(4);

	if (clipboard -> supportsSelection()) {
		clipboard -> setText(clipboard_content, QClipboard::Selection);
	}
	clipboard -> setText(clipboard_content);

	m_view -> pasteInArea();
}

/**
 * @brief QETElementEditor::on_m_save_action_triggered
 * @return
 */
bool QETElementEditor::on_m_save_action_triggered()
{
	if (checkElement())
	{
		if (m_opened_from_file)
		{
			if (m_file_name.isEmpty()) {
				return(on_m_save_as_action_triggered());
			}

			//Else wa save to the file at filename_ path
			bool result_save = toFile(m_file_name);
			if (result_save) m_elmt_scene -> undoStack().setClean();
			return(result_save);
		}
		else
		{
			if (m_location.isNull()) {
				return(on_m_save_as_action_triggered());
			}

			//Else save to the known location
			bool result_save = toLocation(m_location);
			if (result_save) {
				m_elmt_scene -> undoStack().setClean();
				emit saveToLocation(m_location);
			}
			return(result_save);
		}
	}

	QMessageBox::critical(this, tr("Echec de l'enregistrement"), tr("L'enregistrement à échoué,\nles conditions requises ne sont pas valides"));
	return false;
}

/**
 * @brief QETElementEditor::on_m_save_as_action_triggered
 * Ask a location to user and save the current edited element
 * to this location
 * @return true is success
 */
bool QETElementEditor::on_m_save_as_action_triggered()
{
	// Check element before writing
	if (checkElement()) {
		//Ask a location to user
		ElementsLocation location = ElementDialog::getSaveElementLocation(this);
		if (location.isNull()) {
			return(false);
		}

		bool result_save = toLocation(location);
		if (result_save) {
			setLocation(location);
			m_elmt_scene -> undoStack().setClean();
			emit saveToLocation(location);
		}

		return(result_save);
	}
	QMessageBox::critical(this, tr("Echec de l'enregistrement"), tr("L'enregistrement à échoué,\nles conditions requises ne sont pas valides"));
	return (false);
}

void QETElementEditor::on_m_select_all_act_triggered() {
	m_elmt_scene->slot_selectAll();
}

void QETElementEditor::on_m_edit_element_properties_action_triggered() { m_elmt_scene->slot_editProperties(); }

void QETElementEditor::on_m_new_action_triggered()
{
	NewElementWizard new_element_wizard(this);
	new_element_wizard.exec();
}

void QETElementEditor::on_m_open_action_triggered()
{
	ElementsLocation location = ElementDialog::getOpenElementLocation(this);
	if (location.isNull()) {
		return;
	}
	QETApp::instance() -> openElementLocations(QList<ElementsLocation>() << location);
}

void QETElementEditor::on_m_open_from_file_action_triggered()
{
	QString open_dir = m_file_name.isEmpty() ? QETApp::customElementsDir() : QDir(m_file_name).absolutePath();
	QString user_filename = QETElementEditor::getOpenElementFileName(this, open_dir);
	openElement(user_filename);
}

void QETElementEditor::on_m_open_dxf_action_triggered()
{
#ifdef TODO_LIST
#	pragma message("@TODO Merge 'DXF to GET-2020' code in to Qet")
#	pragma message("https://github.com/qelectrotech/DXFtoQET-2020")
#endif
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
	QString program = (QDir::homePath() + "/Application Data/qet/DXFtoQET.exe");
#elif defined(Q_OS_MAC)
	QString program = (QDir::homePath() + "/.qet/DXFtoQET.app");
#else
	QString program = (QDir::homePath() + "/.qet/DXFtoQET");
#endif
	QStringList arguments;
	QProcess *DXF = new QProcess(qApp);
	DXF -> start(program,arguments);
}

bool QETElementEditor::on_m_save_as_file_action_triggered()
{
	// Check element before writing
	if (checkElement()) {
		//Ask a filename to user, for save the element
		QString fn = QFileDialog::getSaveFileName(
						 this,
						 tr("Enregistrer sous", "dialog title"),
						 m_file_name.isEmpty() ? QETApp::customElementsDir() : QDir(m_file_name).absolutePath(),
						 tr(
							 "Éléments QElectroTech (*.elmt)",
							 "filetypes allowed when saving an element file"
		)
						 );

		if (fn.isEmpty()) {
			return(false);
		}

		//If the name doesn't end by .elmt, we add it
		if (!fn.endsWith(".elmt", Qt::CaseInsensitive)) {
			fn += ".elmt";
		}

		bool result_save = toFile(fn);
		//If the save success, the filename is keep
		if (result_save) {
			setFileName(fn);
			QETApp::elementsRecentFiles() -> fileWasOpened(fn);
			m_elmt_scene -> undoStack().setClean();
		}

		return(result_save);
	}
	QMessageBox::critical(this, tr("Echec de l'enregistrement"), tr("L'enregistrement à échoué,\nles conditions requises ne sont pas valides"));
	return false;
}

void QETElementEditor::on_m_reload_action_triggered()
{
	//If user already edit the element, ask confirmation to reload
	if (!m_elmt_scene -> undoStack().isClean()) {
		QMessageBox::StandardButton answer = QET::QetMessageBox::question(this,
																		  tr("Recharger l'élément", "dialog title"),
																		  tr("Vous avez efffectué des modifications sur cet élément. Si vous le rechargez, ces modifications seront perdues. Voulez-vous vraiment recharger l'élément ?", "dialog content"),
																		  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
																		  QMessageBox::Cancel);
		if (answer != QMessageBox::Yes){
			return;
		}
	}

	//Reload the element
	m_elmt_scene -> reset();
	if (m_opened_from_file) {
		fromFile(m_file_name);
	}
	else {
		fromLocation(m_location);
	}
}

void QETElementEditor::on_m_quit_action_triggered() { close(); }

void QETElementEditor::on_m_deselect_all_action_triggered() { m_elmt_scene->slot_deselectAll(); }

void QETElementEditor::on_m_cut_action_triggered() { m_view->cut(); }

void QETElementEditor::on_m_copy_action_triggered() { m_view->copy(); }

void QETElementEditor::on_m_paste_action_triggered() { m_view->paste(); }

void QETElementEditor::on_m_paste_in_area_action_triggered() { m_view->pasteInArea(); }

void QETElementEditor::on_m_paste_from_file_action_triggered()
{
	QString element_file_path = getOpenElementFileName(this);
	if (element_file_path.isEmpty()) {
		return;
	}

	QString error_message;
	QDomDocument xml_document;
	QFile element_file(element_file_path);
	// le fichier doit etre lisible
	if (!element_file.open(QIODevice::ReadOnly)) {
		error_message = QString(tr("Impossible d'ouvrir le fichier %1.", "message box content")).arg(element_file_path);
	}
	else {
		// le fichier doit etre un document XML
		if (!xml_document.setContent(&element_file)) {
			error_message = tr("Ce fichier n'est pas un document XML valide", "message box content");
		}
		element_file.close();
	}

	if (!error_message.isEmpty()) {
		QET::QetMessageBox::critical(this, tr("Erreur", "toolbar title"), error_message);
	}
	copyAndPasteXml(xml_document);
}

void QETElementEditor::on_m_paste_from_element_action_triggered()
{
		//Ask for a location
	ElementsLocation location = ElementDialog::getOpenElementLocation(this);
	if (location.isNull()) {
		return;
	}

	if (!location.isElement()) {
		QET::QetMessageBox::critical(this,
									 tr("Élément inexistant.", "message box title"),
									 tr("Le chemin virtuel choisi ne correspond pas à un élément.", "message box content"));
		return;
	}
	if (!location.exist()) {
		QET::QetMessageBox::critical(this,
									 tr("Élément inexistant.", "message box title"),
									 tr("L'élément n'existe pas.", "message box content"));
		return;
	}

		//Create an xml document from the location xml
	QDomDocument document_xml;
	QDomNode node = document_xml.importNode(location.xml(), true);
	document_xml.appendChild(node);

	copyAndPasteXml(document_xml);
}

void QETElementEditor::on_m_revert_selection_action_triggered() { m_elmt_scene->slot_invertSelection(); }

void QETElementEditor::on_m_delete_action_triggered() { m_elmt_scene->slot_delete(); }

void QETElementEditor::on_m_edit_names_action_triggered() { m_elmt_scene->slot_editNames(); }

void QETElementEditor::on_m_edit_author_action_triggered() { m_elmt_scene->slot_editAuthorInformations(); }

void QETElementEditor::on_m_zoom_in_action_triggered() { m_view->zoomIn(); }

void QETElementEditor::on_m_zoom_out_action_triggered() { m_view->zoomOut(); }

void QETElementEditor::on_m_zoom_fit_best_action_triggered() { m_view->zoomFit(); }

void QETElementEditor::on_m_zoom_original_action_triggered() { m_view->zoomReset(); }

void QETElementEditor::on_m_about_qet_action_triggered() { QETApp::instance()->aboutQET(); }

void QETElementEditor::on_m_online_manual_triggered() {
	QString link = "https://download.tuxfamily.org/qet/manual_0.7/build/index.html";
	QDesktopServices::openUrl(QUrl(link));
}

void QETElementEditor::on_m_youtube_action_triggered() {
	QString link = "https://www.youtube.com/user/scorpio8101/videos";
	QDesktopServices::openUrl(QUrl(link));
}

void QETElementEditor::on_m_donate_action_triggered() {
	QString link = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZZHC9D7C3MDPC";
	QDesktopServices::openUrl(QUrl(link));
}

void QETElementEditor::on_m_about_qt_action_triggered() { qApp->aboutQt(); }

void QETElementEditor::on_m_import_dxf_triggered()
{
	if (dxf2ElmtIsPresent(true, this))
	{
		QString file_path{QFileDialog::getOpenFileName(this,
													   QObject::tr("Importer un fichier dxf"),
													   "/home",
													   "DXF (*.dxf)")};
		if (file_path.isEmpty()) {
			return;
		}

		QMessageBox::information(this, tr("Avertissement"), tr("L'import d'un dxf volumineux peut prendre du temps \n"
															   "veuillez patienter durant l'import..."));

		const QByteArray array_{dxfToElmt(file_path)};
		if (array_.isEmpty()) {
			return;
		}
		QDomDocument xml_;
		xml_.setContent(array_);

		m_elmt_scene->undoStack().push(new OpenElmtCommand(xml_, m_elmt_scene));
	}
}

