Simon De Backer c958d54d01 Fix Qt 6 deprecated QDockWidget::AllDockWidgetFeatures
(Deprecated) The dock widget can be closed, moved, and floated.
Since new features might be added in future releases,
the look and behavior of dock widgets might change if you use this flag.
Please specify individual flags instead.
2020-09-21 21:16:05 +02:00

1721 lines
56 KiB
C++

/*
Copyright 2006-2020 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 "qet.h"
#include "qetapp.h"
#include "elementscene.h"
#include "elementview.h"
#include "customelementpart.h"
#include "newelementwizard.h"
#include "elementitemeditor.h"
#include "elementdialog.h"
#include "recentfiles.h"
#include "qeticons.h"
#include "qetmessagebox.h"
#include "editorcommands.h"
// editeurs de primitives
#include "arceditor.h"
#include "ellipseeditor.h"
#include "lineeditor.h"
#include "polygoneditor.h"
#include "rectangleeditor.h"
#include "terminaleditor.h"
#include "texteditor.h"
#include "partterminal.h"
#include "styleeditor.h"
#include "dynamictextfieldeditor.h"
#include "eseventaddline.h"
#include "eseventaddrect.h"
#include "eseventaddellipse.h"
#include "eseventaddpolygon.h"
#include "eseventaddarc.h"
#include "eseventaddtext.h"
#include "eseventaddterminal.h"
#include "eseventadddynamictextfield.h"
#include <QMessageBox>
#include <QTextStream>
#include <QFileDialog>
#include <QFile>
#include <QModelIndex>
#include <utility>
/*
Nombre maximum de primitives affichees par la "liste des parties"
Au-dela, un petit message est affiche, indiquant que ce nombre a ete depasse
et que la liste ne sera donc pas mise a jour.
*/
#define QET_MAX_PARTS_IN_ELEMENT_EDITOR_LIST 200
/**
Constructeur
@param parent QWidget parent
*/
QETElementEditor::QETElementEditor(QWidget *parent) :
QETMainWindow(parent),
read_only(false),
min_title(tr("QElectroTech - Éditeur d'élément", "window title")),
opened_from_file(false) {
setWindowTitle(min_title);
setWindowIcon(QET::Icons::QETLogo);
setupInterface();
setupActions();
setupMenus();
// la fenetre est maximisee par defaut
setMinimumSize(QSize(500, 350));
setWindowState(Qt::WindowMaximized);
// lecture des parametres
readSettings();
slot_updateMenus();
// affichage
show();
}
/// Destructeur
QETElementEditor::~QETElementEditor()
{
/*
retire le widget d'edition de primitives affiche par le dock
cela evite qu'il ne soit supprime par son widget parent
*/
clearToolsDock();
// supprime les editeurs de primitives
qDeleteAll(m_editors.begin(), m_editors.end());
m_editors.clear();
}
/**
@brief QETElementEditor::setLocation
The new location to edit
@param el
*/
void QETElementEditor::setLocation(const ElementsLocation &el) {
location_ = el;
opened_from_file = false;
setReadOnly(!location_.isWritable());
slot_updateTitle();
}
/**
@param fn Le nouveau nom de fichier de l'element edite
*/
void QETElementEditor::setFileName(const QString &fn) {
filename_ = fn;
opened_from_file = true;
// modifie le mode lecture seule si besoin
bool must_be_read_only = !QFileInfo(filename_).isWritable();
if (isReadOnly() != must_be_read_only) {
setReadOnly(must_be_read_only);
}
slot_updateTitle();
}
/**
@brief QETElementEditor::setupActions
Create action used in Element editor
*/
void QETElementEditor::setupActions()
{
new_element = new QAction(QET::Icons::DocumentNew, tr("&Nouveau"), this);
open = new QAction(QET::Icons::FolderOpen, tr("&Ouvrir"), this);
open_file = new QAction(QET::Icons::FolderOpen, tr("&Ouvrir depuis un fichier"), this);
open_dxf = new QAction(QET::Icons::RunDxf, tr("&Lancer le plugin convertisseur DXF"), this);
save = new QAction(QET::Icons::DocumentSave, tr("&Enregistrer"), this);
save_as = new QAction(QET::Icons::DocumentSaveAs, tr("Enregistrer sous"), this);
save_as_file = new QAction(QET::Icons::DocumentSaveAs, tr("Enregistrer dans un fichier"), this);
reload = new QAction(QET::Icons::ViewRefresh, tr("Recharger"), this);
quit = new QAction(QET::Icons::ApplicationExit, tr("&Quitter"), this);
selectall = new QAction(QET::Icons::EditSelectAll, tr("Tout sélectionner"), this);
deselectall = new QAction(QET::Icons::EditSelectNone, tr("Désélectionner tout"), this);
cut = new QAction(QET::Icons::EditCut, tr("Co&uper"), this);
copy = new QAction(QET::Icons::EditCopy, tr("Cop&ier"), this);
paste = new QAction(QET::Icons::EditPaste, tr("C&oller"), this);
paste_in_area = new QAction(QET::Icons::EditPaste, tr("C&oller dans la zone..."), this);
paste_from_file = new QAction(QET::Icons::XmlTextFile, tr("un fichier"), this);
paste_from_elmt = new QAction(QET::Icons::Element, tr("un élément"), this);
inv_select = new QAction(QET::Icons::EditSelectInvert, tr("Inverser la sélection"), this);
edit_delete = new QAction(QET::Icons::EditDelete, tr("&Supprimer"), this);
edit_names = new QAction(QET::Icons::Names, tr("Éditer le nom et les traductions de l'élément"), this);
edit_author = new QAction(QET::Icons::UserInformations, tr("Éditer les informations sur l'auteur"), this);
m_edit_properties = new QAction(QET::Icons::ElementEdit, tr("Éditer les propriétés de l'élément"), this);
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
open_dxf -> 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)
open_dxf -> 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
open_dxf -> 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
open_dxf -> 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"
));
undo = m_elmt_scene -> undoStack().createUndoAction(this, tr("Annuler"));
redo = m_elmt_scene -> undoStack().createRedoAction(this, tr("Refaire"));
undo -> setIcon(QET::Icons::EditUndo);
redo -> setIcon(QET::Icons::EditRedo);
undo -> setShortcuts(QKeySequence::Undo);
redo -> setShortcuts(QKeySequence::Redo);
new_element -> setShortcut(QKeySequence::New);
open -> setShortcut(QKeySequence::Open);
open_file -> setShortcut(tr("Ctrl+Shift+O"));
save -> setShortcut(QKeySequence::Save);
save_as_file -> setShortcut(tr("Ctrl+Shift+S"));
quit -> setShortcut(QKeySequence(tr("Ctrl+Q")));
selectall -> setShortcut(QKeySequence::SelectAll);
deselectall -> setShortcut(QKeySequence(tr("Ctrl+Shift+A")));
inv_select -> setShortcut(QKeySequence(tr("Ctrl+I")));
cut -> setShortcut(QKeySequence::Cut);
copy -> setShortcut(QKeySequence::Copy);
paste -> setShortcut(QKeySequence::Paste);
paste_in_area -> setShortcut(tr("Ctrl+Shift+V"));
#ifndef Q_OS_MAC
edit_delete -> setShortcut(QKeySequence(Qt::Key_Delete));
#else
edit_delete -> setShortcut(QKeySequence(tr("Backspace")));
#endif
edit_names -> setShortcut(QKeySequence(tr("Ctrl+E")));
edit_author -> setShortcut(tr("Ctrl+Y"));
connect(new_element, SIGNAL(triggered()), this, SLOT(slot_new()));
connect(open, SIGNAL(triggered()), this, SLOT(slot_open()));
connect(open_dxf, SIGNAL(triggered()), this, SLOT(slot_openDxf()));
connect(open_file, SIGNAL(triggered()), this, SLOT(slot_openFile()));
connect(save, SIGNAL(triggered()), this, SLOT(slot_save()));
connect(save_as, SIGNAL(triggered()), this, SLOT(slot_saveAs()));
connect(save_as_file, SIGNAL(triggered()), this, SLOT(slot_saveAsFile()));
connect(reload, SIGNAL(triggered()), this, SLOT(slot_reload()));
connect(quit, SIGNAL(triggered()), this, SLOT(close()));
connect(selectall, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_selectAll()));
connect(deselectall, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_deselectAll()));
connect(inv_select, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_invertSelection()));
connect(cut, SIGNAL(triggered()), m_view, SLOT(cut()));
connect(copy, SIGNAL(triggered()), m_view, SLOT(copy()));
connect(paste, SIGNAL(triggered()), m_view, SLOT(paste()));
connect(paste_in_area, SIGNAL(triggered()), m_view, SLOT(pasteInArea()));
connect(paste_from_file, SIGNAL(triggered()), this, SLOT(pasteFromFile()));
connect(paste_from_elmt, SIGNAL(triggered()), this, SLOT(pasteFromElement()));
connect(edit_delete, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_delete()));
connect(edit_names, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_editNames()));
connect(edit_author, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_editAuthorInformations()));
connect(m_edit_properties, SIGNAL(triggered()), m_elmt_scene, SLOT(slot_editProperties()));
//Action related to change depth of primitive
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());
});
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);
/*
Action related to zoom
*/
m_zoom_ag = new QActionGroup(this);
QAction *zoom_in = new QAction(QET::Icons::ZoomIn, tr("Zoom avant"), m_zoom_ag);
QAction *zoom_out = new QAction(QET::Icons::ZoomOut, tr("Zoom arrière"), m_zoom_ag);
QAction *zoom_fit = new QAction(QET::Icons::ZoomFitBest, tr("Zoom adapté"), m_zoom_ag);
QAction *zoom_reset = new QAction(QET::Icons::ZoomOriginal, tr("Pas de zoom"), m_zoom_ag);
zoom_in -> setShortcut(QKeySequence::ZoomIn);
zoom_out -> setShortcut(QKeySequence::ZoomOut);
zoom_fit -> setShortcut(QKeySequence(tr("Ctrl+9")));
zoom_reset -> setShortcut(QKeySequence(tr("Ctrl+0")));
connect(zoom_in, SIGNAL(triggered()), m_view, SLOT(zoomIn() ));
connect(zoom_out, SIGNAL(triggered()), m_view, SLOT(zoomOut() ));
connect(zoom_fit, SIGNAL(triggered()), m_view, SLOT(zoomFit() ));
connect(zoom_reset, SIGNAL(triggered()), m_view, SLOT(zoomReset() ));
/*
Action related to primitive creation
*/
connect (m_elmt_scene, SIGNAL(partsAdded()), this, SLOT(UncheckAddPrimitive()));
parts = new QActionGroup(this);
QAction *add_line = new QAction(QET::Icons::PartLine, tr("Ajouter une ligne"), parts);
QAction *add_rectangle = new QAction(QET::Icons::PartRectangle, tr("Ajouter un rectangle"), parts);
QAction *add_ellipse = new QAction(QET::Icons::PartEllipse, tr("Ajouter une ellipse"), parts);
QAction *add_polygon = new QAction(QET::Icons::PartPolygon, tr("Ajouter un polygone"), parts);
QAction *add_text = new QAction(QET::Icons::PartText, tr("Ajouter du texte"), parts);
QAction *add_arc = new QAction(QET::Icons::PartArc, tr("Ajouter un arc de cercle"), parts);
QAction *add_terminal = new QAction(QET::Icons::Terminal, tr("Ajouter une borne"), parts);
QAction *add_dynamic_text_field = new QAction(QET::Icons::PartTextField, tr("Ajouter un champ texte dynamique"), parts);
foreach (QAction *action, parts -> actions()) action -> setCheckable(true);
connect(add_line, SIGNAL(triggered()), this, SLOT(addLine() ));
connect(add_rectangle, SIGNAL(triggered()), this, SLOT(addRect() ));
connect(add_ellipse, SIGNAL(triggered()), this, SLOT(addEllipse() ));
connect(add_polygon, SIGNAL(triggered()), this, SLOT(addPolygon() ));
connect(add_text, SIGNAL(triggered()), this, SLOT(addText() ));
connect(add_arc, SIGNAL(triggered()), this, SLOT(addArc() ));
connect(add_terminal, SIGNAL(triggered()), this, SLOT(addTerminal() ));
connect(add_dynamic_text_field, &QAction::triggered, this, &QETElementEditor::addDynamicTextField);
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"));
parts_toolbar = addToolBar(tr("Parties", "toolbar title"));
parts_toolbar -> setAllowedAreas(Qt::AllToolBarAreas);
parts_toolbar -> setObjectName("parts");
parts_toolbar -> addActions(parts -> actions());
addToolBar(Qt::LeftToolBarArea, parts_toolbar);
main_toolbar = new QToolBar(tr("Outils", "toolbar title"), this);
main_toolbar -> setObjectName("main_toolbar");
view_toolbar = new QToolBar(tr("Affichage", "toolbar title"), this);
view_toolbar -> setObjectName("display");
element_toolbar = new QToolBar(tr("Élément", "toolbar title"), this);
element_toolbar -> setObjectName("element_toolbar");
main_toolbar -> addAction(new_element);
main_toolbar -> addAction(open);
main_toolbar -> addAction(save);
main_toolbar -> addAction(save_as);
main_toolbar -> addAction(reload);
main_toolbar -> addSeparator();
main_toolbar -> addAction(undo);
main_toolbar -> addAction(redo);
main_toolbar -> addSeparator();
main_toolbar -> addAction(edit_delete);
view_toolbar -> addAction(zoom_fit);
view_toolbar -> addAction(zoom_reset);
element_toolbar -> addAction(edit_names);
element_toolbar -> addAction(m_edit_properties);
addToolBar(Qt::TopToolBarArea, main_toolbar);
addToolBar(Qt::TopToolBarArea, view_toolbar);
addToolBar(Qt::TopToolBarArea, element_toolbar);
connect(m_elmt_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updateInformations()), Qt::QueuedConnection);
connect(m_elmt_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updateMenus()));
connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(slot_updateMenus()));
connect(&(m_elmt_scene -> undoStack()), SIGNAL(cleanChanged(bool)), this, SLOT(slot_updateMenus()));
connect(&(m_elmt_scene -> undoStack()), SIGNAL(cleanChanged(bool)), this, SLOT(slot_updateTitle()));
// Annuler ou refaire une action met a jour la liste des primitives ; cela sert notamment pour les
// ajouts et suppressions de primitives ainsi que pour les actions entrainant un change
connect(&(m_elmt_scene -> undoStack()), SIGNAL(indexChanged(int)), this, SLOT(slot_updatePartsList()));
// Annuler ou refaire une action met a jour les informations affichees sur les primitives selectionnees,
// celles-ci etant potentiellement impactees
connect(&(m_elmt_scene -> undoStack()), SIGNAL(indexChanged(int)), this, SLOT(slot_updateInformations()));
}
/**
@brief QETElementEditor::setupMenus
*/
void QETElementEditor::setupMenus()
{
file_menu = new QMenu(tr("&Fichier"), this);
edit_menu = new QMenu(tr("&Édition"), this);
display_menu = new QMenu(tr("Afficha&ge"), this);
tools_menu = new QMenu(tr("O&utils"), this);
file_menu -> setTearOffEnabled(true);
edit_menu -> setTearOffEnabled(true);
display_menu -> setTearOffEnabled(true);
tools_menu -> setTearOffEnabled(true);
file_menu -> addAction(new_element);
file_menu -> addAction(open);
file_menu -> addAction(open_file);
file_menu -> addAction(open_dxf);
QMenu *recentfile = file_menu -> addMenu(QET::Icons::DocumentOpenRecent, tr("&Récemment ouverts"));
recentfile -> addActions(QETApp::elementsRecentFiles() -> menu() -> actions());
connect(QETApp::elementsRecentFiles(), SIGNAL(
fileOpeningRequested(const QString &)), this, SLOT(openRecentFile(const QString &)));
file_menu -> addAction(save);
file_menu -> addAction(save_as);
file_menu -> addAction(save_as_file);
file_menu -> addSeparator();
file_menu -> addAction(reload);
file_menu -> addSeparator();
file_menu -> addAction(quit);
paste_from_menu = new QMenu(tr("Coller depuis..."));
paste_from_menu -> setIcon(QET::Icons::EditPaste);
paste_from_menu -> addAction(paste_from_file);
paste_from_menu -> addAction(paste_from_elmt);
edit_menu -> addAction(undo);
edit_menu -> addAction(redo);
edit_menu -> addSeparator();
edit_menu -> addAction(selectall);
edit_menu -> addAction(deselectall);
edit_menu -> addAction(inv_select);
edit_menu -> addSeparator();
edit_menu -> addAction(cut);
edit_menu -> addAction(copy);
edit_menu -> addAction(paste);
edit_menu -> addAction(paste_in_area);
edit_menu -> addMenu(paste_from_menu);
edit_menu -> addSeparator();
edit_menu -> addAction(edit_delete);
edit_menu -> addSeparator();
edit_menu -> addAction(edit_names);
edit_menu -> addAction(edit_author);
edit_menu -> addAction(m_edit_properties);
edit_menu -> addSeparator();
edit_menu -> addActions(m_depth_action_group -> actions());
display_menu -> addActions(m_zoom_ag -> actions());
insertMenu(settings_menu_, file_menu);
insertMenu(settings_menu_, edit_menu);
insertMenu(settings_menu_, display_menu);
}
/**
@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(undo);
menu.addAction(redo);
menu.addAction(selectall);
menu.addAction(deselectall);
menu.addAction(inv_select);
menu.addSeparator();
menu.addAction(edit_delete);
menu.addAction(cut);
menu.addAction(copy);
menu.addSeparator();
menu.addAction(paste);
menu.addAction(paste_in_area);
menu.addMenu(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);
}
/**
Met a jour les menus
*/
void QETElementEditor::slot_updateMenus()
{
bool selected_items = !read_only && !m_elmt_scene -> selectedItems().isEmpty();
bool clipboard_elmt = !read_only && ElementScene::clipboardMayContainElement();
// actions dependant seulement de l'etat "lecture seule" de l'editeur
foreach (QAction *action, parts -> actions()) {
action -> setEnabled(!read_only);
}
selectall -> setEnabled(!read_only);
inv_select -> setEnabled(!read_only);
paste_from_file -> setEnabled(!read_only);
paste_from_elmt -> setEnabled(!read_only);
m_parts_list -> setEnabled(!read_only);
// Action enabled if primitive selected
deselectall -> setEnabled(selected_items);
cut -> setEnabled(selected_items);
copy -> setEnabled(selected_items);
edit_delete -> setEnabled(selected_items);
foreach (QAction *action, m_depth_action_group -> actions()) {
action -> setEnabled(selected_items);
}
// actions dependant du contenu du presse-papiers
paste -> setEnabled(clipboard_elmt);
paste_in_area -> setEnabled(clipboard_elmt);
// actions dependant de l'etat de la pile d'annulation
save -> setEnabled(!read_only && !m_elmt_scene -> undoStack().isClean());
undo -> setEnabled(!read_only && m_elmt_scene -> undoStack().canUndo());
redo -> setEnabled(!read_only && m_elmt_scene -> undoStack().canRedo());
}
/**
Met a jour le titre de la fenetre
*/
void QETElementEditor::slot_updateTitle()
{
QString title = min_title;
title += " - " + m_elmt_scene -> names().name() + " ";
if (!filename_.isEmpty() || !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::setupInterface
*/
void QETElementEditor::setupInterface()
{
// editeur
m_elmt_scene = new ElementScene(this, this);
m_view = new ElementView(m_elmt_scene, this);
slot_setRubberBandToView();
setCentralWidget(m_view);
// widget par defaut dans le QDockWidget
m_default_informations = new QLabel();
// ScrollArea pour accueillir un widget d'edition (change a la volee)
// m_tools_dock_scroll_area = new QScrollArea();
// m_tools_dock_scroll_area -> setFrameStyle(QFrame::NoFrame);
// m_tools_dock_scroll_area -> setAlignment(Qt::AlignHCenter|Qt::AlignTop);
// Pile de widgets pour accueillir les deux widgets precedents
m_tools_dock_stack = new QStackedWidget();
m_tools_dock_stack -> insertWidget(0, m_default_informations);
// m_tools_dock_stack -> insertWidget(1, m_tools_dock_scroll_area);
// widgets d'editions pour les parties
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);
// panel sur le cote pour editer les parties
m_tools_dock = new QDockWidget(tr("Informations", "dock title"), this);
m_tools_dock -> setObjectName("informations");
m_tools_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
m_tools_dock -> setFeatures(
QDockWidget::DockWidgetClosable
|QDockWidget::DockWidgetMovable
|QDockWidget::DockWidgetFloatable);
//m_tools_dock -> setMinimumWidth(380);
addDockWidget(Qt::RightDockWidgetArea, m_tools_dock);
m_tools_dock -> setWidget(m_tools_dock_stack);
// panel sur le cote pour les annulations
m_undo_dock = new QDockWidget(tr("Annulations", "dock title"), this);
m_undo_dock -> setObjectName("undo");
m_undo_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
m_undo_dock -> setFeatures(
QDockWidget::DockWidgetClosable
|QDockWidget::DockWidgetMovable
|QDockWidget::DockWidgetFloatable);
m_undo_dock -> setMinimumWidth(290);
addDockWidget(Qt::RightDockWidgetArea, m_undo_dock);
QUndoView* undo_view = new QUndoView(&(m_elmt_scene -> undoStack()), this);
undo_view -> setEmptyLabel(tr("Aucune modification"));
m_undo_dock -> setWidget(undo_view);
// panel sur le cote pour la liste des parties
m_parts_list = new QListWidget(this);
m_parts_list -> setSelectionMode(QAbstractItemView::ExtendedSelection);
connect(m_elmt_scene, SIGNAL(partsAdded()), this, SLOT(slot_createPartsList()));
connect(m_elmt_scene, SIGNAL(partsRemoved()), this, SLOT(slot_createPartsList()));
connect(m_elmt_scene, SIGNAL(partsZValueChanged()), this, SLOT(slot_createPartsList()));
connect(m_elmt_scene, SIGNAL(selectionChanged()), this, SLOT(slot_updatePartsList()));
connect(m_parts_list, SIGNAL(itemSelectionChanged()), this, SLOT(slot_updateSelectionFromPartsList()));
m_parts_dock = new QDockWidget(tr("Parties", "dock title"), this);
m_parts_dock -> setObjectName("parts_list");
m_parts_dock -> setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
m_parts_dock -> setFeatures(
QDockWidget::DockWidgetClosable
|QDockWidget::DockWidgetMovable
|QDockWidget::DockWidgetFloatable);
m_parts_dock -> setMinimumWidth(290);
tabifyDockWidget(m_undo_dock, m_parts_dock);
m_parts_dock -> setWidget(m_parts_list);
slot_updateInformations();
slot_createPartsList();
// barre d'etat
statusBar() -> showMessage(tr("Éditeur d'éléments", "status bar message"));
}
/**
Passe l'editeur d'element en mode selection : le pointeur deplace les
elements selectionnes et il est possible d'utiliser un rectangle de selection.
*/
void QETElementEditor::slot_setRubberBandToView()
{
m_view -> setDragMode(QGraphicsView::RubberBandDrag);
}
/**
Passe l'editeur d'element en mode immobile (utilise pour la lecture seule)
*/
void QETElementEditor::slot_setNoDragToView()
{
m_view -> setDragMode(QGraphicsView::NoDrag);
}
/**
Met a jour la zone d'information et d'edition des primitives.
Si plusieurs primitives sont selectionnees, seule leur quantite est
affichee. Sinon, un widget d'edition approprie est mis en place.
*/
void QETElementEditor::slot_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 == "terminal" ||
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]);
// 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 (selection_xml_name == "polygon" && cep_list.length() == 1) {
// 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::checkElement
Do several check about element.
If error is occurred return false
*/
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 -> elementType().contains("report")) {
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 -> elementType().contains("report")) {
int terminal =0;
foreach(QGraphicsItem *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;
foreach(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;
}
/**
Charge un fichier
@param filepath Chemin du fichier a charger
*/
void QETElementEditor::fromFile(const QString &filepath) {
bool state = true;
QString error_message;
// le fichier doit exister
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);
}
// le fichier doit etre lisible
QFile file(filepath);
if (state) {
if (!file.open(QIODevice::ReadOnly)) {
state = false;
error_message = QString(tr("Impossible d'ouvrir le fichier %1.", "message box content")).arg(filepath);
}
}
// le fichier doit etre un document XML
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;
}
// chargement de l'element
m_elmt_scene -> fromXml(document_xml);
slot_createPartsList();
// gestion de la lecture seule
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);
slot_updateMenus();
}
/**
@brief QETElementEditor::toFile
Save to file the drawed element.
@param fn : path of the file
@return : true if succesfully save.
*/
bool QETElementEditor::toFile(const QString &fn) {
m_elmt_scene -> clearEventInterface();
m_elmt_scene -> clearSelection();
UncheckAddPrimitive();
QDomDocument element_xml = m_elmt_scene -> toXml();
bool writing = QET::writeXmlFile(element_xml, fn);
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::toLocation
Save the element to Location
@param location : location where we must save the current element
@return true if succesfully 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);
}
/**
@param provided_location Emplacement d'un element
@return true si cet editeur est en train d'editer l'element dont
l'emplacement est location, false sinon
*/
bool QETElementEditor::isEditing(const ElementsLocation &provided_location) {
if (opened_from_file) {
return(
QET::compareCanonicalFilePaths(
filename_,
QETApp::realPath(provided_location.toString())
)
);
}
else {
return(provided_location == location_);
}
}
/**
@param provided_filepath Chemin d'un element sur un filesystem
@return true si cet editeur est en train d'editer l'element dont
le chemin est filepath, false sinon
*/
bool QETElementEditor::isEditing(const QString &provided_filepath) {
// determine le chemin canonique de l'element actuelle edite, si applicable
QString current_filepath;
if (opened_from_file) {
current_filepath = filename_;
}
else {
current_filepath = QETApp::realPath(location_.toString());
}
return(
QET::compareCanonicalFilePaths(
current_filepath,
provided_filepath
)
);
}
/**
specifie si l'editeur d'element doit etre en mode lecture seule
@param ro true pour activer le mode lecture seule, false pour le desactiver
*/
void QETElementEditor::setReadOnly(bool ro) {
read_only = ro;
// active / desactive les interactions avec la scene
m_view -> setInteractive(!ro);
slot_updateMenus();
}
/**
@return true si l'editeur d'element est en mode lecture seule
*/
bool QETElementEditor::isReadOnly() const
{
return(read_only);
}
/**
@brief QETElementEditor::addLine
Set line creation interface to scene
*/
void QETElementEditor::addLine()
{
m_elmt_scene -> setEventInterface(new ESEventAddLine(m_elmt_scene));
}
/**
@brief QETElementEditor::addRect
Set rectangle creation interface to scene
*/
void QETElementEditor::addRect()
{
m_elmt_scene -> setEventInterface(new ESEventAddRect(m_elmt_scene));
}
/**
@brief QETElementEditor::addEllipse
Set ellipse creation interface to scene
*/
void QETElementEditor::addEllipse()
{
m_elmt_scene -> setEventInterface(new ESEventAddEllipse(m_elmt_scene));
}
/**
@brief QETElementEditor::addPolygon
Set polygon creation interface to scene
*/
void QETElementEditor::addPolygon()
{
m_elmt_scene -> setEventInterface(new ESEventAddPolygon(m_elmt_scene));
}
/**
@brief QETElementEditor::addArc
Set arc creation interface to scene
*/
void QETElementEditor::addArc()
{
m_elmt_scene -> setEventInterface(new ESEventAddArc(m_elmt_scene));
}
/**
@brief QETElementEditor::addText
Set text creation interface to scene
*/
void QETElementEditor::addText()
{
m_elmt_scene -> setEventInterface(new ESEventAddText(m_elmt_scene));
}
/**
@brief QETElementEditor::addTerminal
Set terminal creation interface to scene
*/
void QETElementEditor::addTerminal()
{
m_elmt_scene -> setEventInterface(new ESEventAddTerminal(m_elmt_scene));
}
/**
@brief QETElementEditor::addDynamicTextField
Set dynamic text field creation interface to scene
*/
void QETElementEditor::addDynamicTextField()
{
m_elmt_scene -> setEventInterface(new ESEventAddDynamicTextField(m_elmt_scene));
}
/**
@brief QETElementEditor::UncheckAddPrimitive
Uncheck all action related to primitive
*/
void QETElementEditor::UncheckAddPrimitive()
{
foreach(QAction *action, parts -> actions()) {
action -> setChecked(false);
}
}
/**
Lance l'assistant de creation d'un nouvel element.
*/
void QETElementEditor::slot_new()
{
NewElementWizard new_element_wizard(this);
new_element_wizard.exec();
}
/**
Ouvre un element
*/
void QETElementEditor::slot_open()
{
// demande le chemin virtuel de l'element a ouvrir a l'utilisateur
ElementsLocation location = ElementDialog::getOpenElementLocation(this);
if (location.isNull()) {
return;
}
QETApp::instance() -> openElementLocations(QList<ElementsLocation>() << location);
}
/**
Ouvre un fichier
Demande un fichier a l'utilisateur et ouvre ce fichier
*/
void QETElementEditor::slot_openFile()
{
// repertoire a afficher initialement dans le dialogue
QString open_dir = filename_.isEmpty() ? QETApp::customElementsDir() : QDir(filename_).absolutePath();
// demande un nom de fichier a ouvrir a l'utilisateur
QString user_filename = QETElementEditor::getOpenElementFileName(this, open_dir);
// ouvre l'element
openElement(user_filename);
}
/**
Slot utilise pour ouvrir un fichier recent.
Transfere filepath au slot openElement seulement si cet editeur est actif
@param filepath Fichier a ouvrir
@see openElement
*/
void QETElementEditor::openRecentFile(const QString &filepath) {
// small hack to prevent all element editors from trying to topen the required
// recent file at the same time
if (qApp -> activeWindow() != this) {
return;
}
openElement(filepath);
}
/**
@brief QETElementEditor::slot_openDxf
*/
void QETElementEditor::slot_openDxf ()
{
#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);
}
/**
Ouvre un fichier element dans un nouvel editeur
Cette methode ne controle pas si le fichier est deja ouvert
@param filepath Fichier a ouvrir
@see fromFile
@see QETApp::openElementFiles
*/
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::slot_reload
Reload the element from the file or location
*/
void QETElementEditor::slot_reload()
{
//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 (opened_from_file) {
fromFile(filename_);
}
else {
fromLocation(location_);
}
}
/**
@brief QETElementEditor::slot_save
Save the current editing element.
If the filepath or location is unknown, use save_as instead
@return true if save with success
*/
bool QETElementEditor::slot_save()
{
// Check element befor writing
if (checkElement()) {
//If we don't know the name of the current file, use save as instead
if (opened_from_file) {
if (filename_.isEmpty()) {
return(slot_saveAsFile());
}
//Else wa save to the file at filename_ path
bool result_save = toFile(filename_);
if (result_save) m_elmt_scene -> undoStack().setClean();
return(result_save);
}
else {
if (location_.isNull()) {
return(slot_saveAs());
}
//Else save to the known location
bool result_save = toLocation(location_);
if (result_save) {
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;
}
/**
@brief QETElementEditor::slot_saveAs
Ask a location to user and save the current edited element
to this location
@return true if save with success
*/
bool QETElementEditor::slot_saveAs()
{
// Check element befor 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);
}
/**
@brief QETElementEditor::slot_saveAsFile
Ask a file to user and save the current edited element to this file
@return true if save with success
*/
bool QETElementEditor::slot_saveAsFile()
{
// Check element befor writing
if (checkElement()) {
//Ask a filename to user, for save the element
QString fn = QFileDialog::getSaveFileName(
this,
tr("Enregistrer sous", "dialog title"),
filename_.isEmpty() ? QETApp::customElementsDir() : QDir(filename_).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;
}
/**
@return true si l'element peut etre ferme.
Un element peut etre ferme s'il ne comporte aucune modification.
Si l'element comporte des modifications, la question est posee a
l'utilisateur.
*/
bool QETElementEditor::canClose()
{
if (m_elmt_scene -> undoStack().isClean()) {
return(true);
}
// demande d'abord a l'utilisateur s'il veut enregistrer l'element en cours
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 -> names().name()),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
QMessageBox::Cancel
);
bool result;
switch(answer) {
case QMessageBox::Cancel: {
result = false;
break; // l'utilisateur annule : echec de la fermeture
}
case QMessageBox::Yes: {
result = slot_save();
break; // l'utilisateur dit oui : la reussite depend de l'enregistrement
}
default: {
result = true; // l'utilisateur dit non ou ferme le dialogue: c'est reussi
}
}
return(result);
}
/**
Enleve et cache le widget affiche par le dock permettant d'editer les
parties.
@return le widget enleve, ou 0 s'il n'y avait pas de widget a enlever
*/
QWidget *QETElementEditor::clearToolsDock()
{
if (QWidget *previous_widget = m_tools_dock_stack -> widget(1)) {
m_tools_dock_stack -> removeWidget(previous_widget);
previous_widget -> setParent(nullptr);
previous_widget -> hide();
return(previous_widget);
}
return(nullptr);
}
/**
Exporte le document XML xml_document vers le presse-papier puis declenche
son collage dans l'editeur courant, avec selection de la zone de collage
@param xml_document Document XML a copier/coller
@see ElementView::pasteInArea
*/
void QETElementEditor::copyAndPasteXml(const QDomDocument &xml_document) {
// accede au presse-papier
QClipboard *clipboard = QApplication::clipboard();
// genere la description XML de la selection
QString clipboard_content = xml_document.toString(4);
// met la description XML dans le presse-papier
if (clipboard -> supportsSelection()) {
clipboard -> setText(clipboard_content, QClipboard::Selection);
}
clipboard -> setText(clipboard_content);
m_view -> pasteInArea();
}
/**
Permet de quitter l'editeur lors de la fermeture de la fenetre principale
@param qce Le QCloseEvent correspondant a l'evenement de fermeture
*/
void QETElementEditor::closeEvent(QCloseEvent *qce) {
if (canClose()) {
writeSettings();
setAttribute(Qt::WA_DeleteOnClose);
m_elmt_scene -> reset();
qce -> accept();
}
else {
qce -> ignore();
}
}
/**
Executed the first time the window editor is displayed.
*/
void QETElementEditor::firstActivation(QEvent *event) {
Q_UNUSED(event)
QTimer::singleShot(250, m_view, SLOT(zoomFit()));
}
/**
Remplit la liste des parties
*/
void QETElementEditor::slot_createPartsList()
{
m_parts_list -> blockSignals(true);
m_parts_list -> clear();
QList<QGraphicsItem *> qgis = m_elmt_scene -> zItems();
// on ne construit plus la liste a partir de 200 primitives
// c'est ingerable : la maj de la liste prend trop de temps et le resultat
// est inexploitable
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;
v.setValue<QGraphicsItem *>(qgi);
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);
}
/**
Met a jour la selection dans la liste des parties
*/
void QETElementEditor::slot_updatePartsList()
{
int items_count = m_elmt_scene -> items().count();
if (m_parts_list -> count() != items_count) {
slot_createPartsList();
}
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);
}
}
/**
Met a jour la selection des parties de l'element a partir de la liste des
parties
*/
void QETElementEditor::slot_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);
slot_updateInformations();
slot_updateMenus();
}
/**
@brief QETElementEditor::readSettings
Read settings
*/
void QETElementEditor::readSettings()
{
QSettings settings;
// dimensions et position de la fenetre
QVariant geometry = settings.value("elementeditor/geometry");
if (geometry.isValid()) {
restoreGeometry(geometry.toByteArray());
}
// etat de la fenetre (barres d'outils, docks...)
QVariant state = settings.value("elementeditor/state");
if (state.isValid()) {
restoreState(state.toByteArray());
}
// informations complementaires de l'element : valeur par defaut
m_elmt_scene -> setInformations(settings.value("elementeditor/default-informations", "").toString());
}
/**
@brief QETElementEditor::writeSettings
Write the settings
*/
void QETElementEditor::writeSettings()
{
QSettings settings;
settings.setValue("elementeditor/geometry", saveGeometry());
settings.setValue("elementeditor/state", saveState());
}
/**
@return les decalages horizontaux et verticaux (sous la forme d'un point) a
utiliser lors d'un copier/coller avec decalage.
*/
QPointF QETElementEditor::pasteOffset()
{
QPointF paste_offset(5.0, 0.0);
return(paste_offset);
}
/**
Demande a l'utilisateur d'ouvrir un fichier sense etre un element.
@param parent QWidget parent du dialogue d'ouverture de fichier
@param initial_dir Repertoire a afficher initialement - si une chaine vide
est fournie, QETApp::customElementsDir() sera utilise.
@return Le chemin du fichier choisi ou une chaine vide si l'utilisateur a
clique sur le bouton "Annuler".
@see QETApp::customElementsDir()
*/
QString QETElementEditor::getOpenElementFileName(QWidget *parent, const QString &initial_dir) {
// demande un nom de fichier a ouvrir a l'utilisateur
QString user_filename = QFileDialog::getOpenFileName(
parent,
tr("Ouvrir un fichier", "dialog title"),
initial_dir.isEmpty() ? QETApp::customElementsDir() : initial_dir,
tr(
"Éléments QElectroTech (*.elmt);;"
"Fichiers XML (*.xml);;"
"Tous les fichiers (*)",
"filetypes allowed when opening an element file"
)
);
return(user_filename);
}
/**
@brief QETElementEditor::fromLocation
Location of the element to edit
@param location
*/
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);
slot_createPartsList();
//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);
slot_updateMenus();
}
/**
Demande un fichier a l'utilisateur, l'ouvre en tant que fichier element,
met son contenu dans le presse-papiers, et appelle ElementView::PasteInArea
*/
void QETElementEditor::pasteFromFile()
{
// demande le chemin du fichier a ouvrir a l'utilisateur
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);
}
/**
@brief QETElementEditor::pasteFromElement
Ask an element to user, copy the xml definition of the element
to the clipboard and call ElementView::PasteInArea
*/
void QETElementEditor::pasteFromElement()
{
//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);
}
/**
Met a jour l'editeur de primitive actuellement visible.
Si aucun editeur de primitive n'est visible, ce slot ne fait rien.
*/
void QETElementEditor::updateCurrentPartEditor()
{
// si aucun widget d'edition n'est affiche, on ne fait rien
if (!m_tools_dock_stack -> currentIndex()) {
return;
}
// s'il y a un widget d'edition affiche, on le met a jour
if (ElementItemEditor *current_editor = dynamic_cast<ElementItemEditor *>(m_tools_dock_stack -> widget(1))) {
current_editor -> updateForm();
}
}