File indexing completed on 2024-05-12 05:39:39

0001 /***************************************************************************
0002  * Copyright (C) 2014 by Renaud Guezennec                                   *
0003  * http://www.rolisteam.org/                                                *
0004  *                                                                          *
0005  *  This file is part of rcse                                               *
0006  *                                                                          *
0007  * rcse is free software; you can redistribute it and/or modify             *
0008  * it under the terms of the GNU General Public License as published by     *
0009  * the Free Software Foundation; either version 2 of the License, or        *
0010  * (at your option) any later version.                                      *
0011  *                                                                          *
0012  * rcse is distributed in the hope that it will be useful,                  *
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of           *
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the             *
0015  * GNU General Public License for more details.                             *
0016  *                                                                          *
0017  * You should have received a copy of the GNU General Public License        *
0018  * along with this program; if not, write to the                            *
0019  * Free Software Foundation, Inc.,                                          *
0020  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.                 *
0021  ***************************************************************************/
0022 #include "mainwindow.h"
0023 #include "ui_mainwindow_rcse.h"
0024 
0025 #include "common/logcontroller.h"
0026 #include "common_widgets/logpanel.h"
0027 #include "dialog/aboutrcse.h"
0028 #include "dialog/codeeditordialog.h"
0029 #include "preferences/preferencesdialog.h"
0030 #include "qmlhighlighter.h"
0031 #include "serializerhelper.h"
0032 #include <QBuffer>
0033 #include <QButtonGroup>
0034 #include <QColorDialog>
0035 #include <QDebug>
0036 #include <QDesktopServices>
0037 #include <QDir>
0038 #include <QDockWidget>
0039 #include <QFileDialog>
0040 #include <QJsonArray>
0041 #include <QJsonDocument>
0042 #include <QJsonValue>
0043 #include <QJsonValueRef>
0044 #include <QMenu>
0045 #include <QMessageBox>
0046 #include <QMimeData>
0047 #include <QPagedPaintDevice>
0048 #include <QPrintDialog>
0049 #include <QPrinter>
0050 #include <QQmlContext>
0051 #include <QQmlError>
0052 #include <QQmlProperty>
0053 #include <QQuickItem>
0054 #include <QTemporaryFile>
0055 #include <QTimer>
0056 #include <QTransform>
0057 #include <QUrl>
0058 #include <QUuid>
0059 #include <QtConcurrent>
0060 
0061 // Controller
0062 #include "controllers/charactercontroller.h"
0063 #include "controllers/editorcontroller.h"
0064 #include "controllers/imagecontroller.h"
0065 #include "controllers/qmlgeneratorcontroller.h"
0066 
0067 #include "data/characterlist.h"
0068 #include "delegate/pagedelegate.h"
0069 
0070 #include "canvasfield.h"
0071 #include "itemeditor.h"
0072 
0073 // Undo
0074 #include "charactersheet/worker/ioworker.h"
0075 #include "diceparser_qobject/diceroller.h"
0076 #include "diceparser_qobject/qmltypesregister.h"
0077 #include "undo/addpagecommand.h"
0078 #include "undo/deletefieldcommand.h"
0079 #include "undo/deletepagecommand.h"
0080 #include "undo/setbackgroundimage.h"
0081 #include "undo/setfieldproperties.h"
0082 #include "undo/setpropertyonallcharacters.h"
0083 #include "version.h"
0084 
0085 #include "charactersheet/charactersheetmodel.h"
0086 
0087 #include "controllers/rcseapplicationcontroller.h"
0088 #include <common_widgets/busyindicatordialog.h>
0089 
0090 namespace rcse
0091 {
0092 constexpr int minimalColumnSize= 350;
0093 
0094 void registerRCSEQmlTypes()
0095 {
0096     qmlRegisterSingletonType<RcseApplicationController>("Helper", 1, 0, "AppCtrl",
0097                                                         [](QQmlEngine* engine, QJSEngine* scriptEngine) -> QObject* {
0098                                                             auto ctrl= new RcseApplicationController();
0099 
0100                                                             /*connect(ctrl,
0101                                                             &RcseApplicationController::zoomLevelChanged,
0102                                                             horizontalSlider, [horizontalSlider]() {
0103                                                                 horizontalSlider->setValue();
0104                                                             });*/
0105 
0106                                                             return ctrl;
0107                                                         });
0108 }
0109 
0110 MainWindow::MainWindow(QWidget* parent)
0111     : QMainWindow(parent), ui(new Ui::MainWindow), m_mainCtrl(new rcse::MainController()), m_counterZoom(0)
0112 {
0113     m_preferences= PreferencesManager::getInstance();
0114     setWindowModified(false);
0115     m_qmlGeneration= true;
0116     setAcceptDrops(true);
0117     ui->setupUi(this);
0118 
0119     m_separatorAction= ui->m_fileMenu->insertSeparator(ui->m_recentFileAct1);
0120     m_separatorAction->setVisible(false);
0121 
0122     m_recentActions= {ui->m_recentFileAct1, ui->m_recentFileAct2, ui->m_recentFileAct3, ui->m_recentFileAct4,
0123                       ui->m_recentFileAct5};
0124     for(auto act : m_recentActions)
0125     {
0126         connect(act, &QAction::triggered, this, &MainWindow::openRecentFile);
0127     }
0128 
0129     registerRCSEQmlTypes();
0130 
0131     connect(ui->m_codeEdit, &CodeEditor::textChanged, this,
0132             [this]() { m_mainCtrl->generatorCtrl()->setQmlCode(ui->m_codeEdit->toPlainText()); });
0133     connect(m_mainCtrl->generatorCtrl(), &QmlGeneratorController::qmlCodeChanged, this, [this]() {
0134         if(ui->m_codeEdit->toPlainText() != m_mainCtrl->generatorCtrl()->qmlCode())
0135             ui->m_codeEdit->setPlainText(m_mainCtrl->generatorCtrl()->qmlCode());
0136     });
0137 
0138     connect(ui->m_quickview->engine(), &QQmlEngine::warnings, m_mainCtrl->generatorCtrl(),
0139             &QmlGeneratorController::errors);
0140     connect(ui->m_quickview, &QQuickWidget::statusChanged, this, [this](QQuickWidget::Status status) {
0141         if(status == QQuickWidget::Error)
0142             m_mainCtrl->displayQmlError(ui->m_quickview->errors());
0143     });
0144 
0145     ui->m_imageList->setContextMenuPolicy(Qt::CustomContextMenu);
0146     connect(ui->m_imageList, &QTableView::customContextMenuRequested, this, &MainWindow::showContextMenuForImageTab);
0147     connect(ui->m_characterView, &QTreeView::customContextMenuRequested, this,
0148             &MainWindow::showContextMenuForCharacterTab);
0149 
0150     setUpActionForImageTab();
0151     setUpActionForCharacterTab();
0152 
0153     ui->m_view->setController(m_mainCtrl->editCtrl());
0154 
0155     ui->m_characterSelectBox->setModel(m_mainCtrl->characterCtrl()->characters());
0156     connect(m_mainCtrl->characterCtrl()->characters(), &CharacterList::dataChanged, this,
0157             [this]() { ui->m_characterSelectBox->setCurrentIndex(0); });
0158 
0159     connect(ui->m_characterSelectBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this]() {
0160         m_mainCtrl->generatorCtrl()->setUuidCharacter(ui->m_characterSelectBox->currentData().toString());
0161     });
0162 
0163     ui->m_characterSelectBox->setCurrentIndex(0);
0164 
0165     connect(m_mainCtrl->editCtrl(), &EditorController::pageCountChanged, this, &MainWindow::updatePageSelector);
0166 
0167     // LOG
0168     QDockWidget* wid= new QDockWidget(tr("Log panel"), this);
0169     wid->setObjectName(QStringLiteral("logpanel"));
0170     m_logPanel= new LogPanel(this);
0171     m_logPanel->setController(m_mainCtrl->logCtrl());
0172     wid->setWidget(m_logPanel);
0173     connect(m_mainCtrl->logCtrl(), &LogController::logLevelChanged, this, &MainWindow::writeSettings);
0174     addDockWidget(Qt::BottomDockWidgetArea, wid);
0175     auto showLogPanel= wid->toggleViewAction();
0176 
0177     ui->treeView->setController(m_mainCtrl.get());
0178 
0179     ui->m_codeToViewBtn->setDefaultAction(ui->m_codeToViewAct);
0180     ui->m_generateCodeBtn->setDefaultAction(ui->m_genarateCodeAct);
0181 
0182     //////////////////////////////////////
0183     // end of QAction for view
0184     //////////////////////////////////////
0185     /// TODO better management of scene options
0186     connect(ui->m_showItemIcon, &QAction::triggered, [=](bool triggered) {
0187         CanvasField::setShowImageField(triggered);
0188         QList<QRectF> list;
0189         list << ui->m_view->sceneRect();
0190         ui->m_view->updateScene(list);
0191     });
0192 
0193     ////////////////////
0194     // undo / redo
0195     ////////////////////
0196     QAction* undo= m_mainCtrl->undoStack().createUndoAction(this, tr("&Undo"));
0197     ui->menuEdition->insertAction(ui->m_genarateCodeAct, undo);
0198 
0199     QAction* redo= m_mainCtrl->undoStack().createRedoAction(this, tr("&Redo"));
0200     ui->menuEdition->insertAction(ui->m_genarateCodeAct, redo);
0201 
0202     ui->menuEdition->addSeparator();
0203     ui->menuEdition->addAction(showLogPanel);
0204 
0205     undo->setShortcut(QKeySequence::Undo);
0206     redo->setShortcut(QKeySequence::Redo);
0207 
0208     connect(ui->m_backgroundImageAct, &QAction::triggered, this, &MainWindow::openBackgroundImage);
0209     // ui->scrollArea->setWidget(m_view);
0210 
0211     ui->m_addCheckBoxAct->setData(Canvas::ADDCHECKBOX);
0212     ui->m_addTextAreaAct->setData(Canvas::ADDTEXTAREA);
0213     ui->m_addTextInputAct->setData(Canvas::ADDINPUT);
0214     ui->m_addTextFieldAct->setData(Canvas::ADDTEXTFIELD);
0215     ui->m_addImageAction->setData(Canvas::ADDIMAGE);
0216     ui->m_addLabelAction->setData(Canvas::ADDLABEL);
0217     ui->m_functionButtonAct->setData(Canvas::ADDFUNCBUTTON);
0218     ui->m_tableFieldAct->setData(Canvas::ADDTABLE);
0219     ui->m_webPageAct->setData(Canvas::ADDWEBPAGE);
0220     ui->m_nextPageAct->setData(Canvas::NEXTPAGE);
0221     ui->m_previousPageAct->setData(Canvas::PREVIOUSPAGE);
0222     ui->m_sliderAct->setData(Canvas::ADDSLIDER);
0223     ui->m_hiddenFieldAct->setData(Canvas::ADDHIDDEN);
0224 
0225     ui->m_moveAct->setData(Canvas::MOVE);
0226     ui->m_moveAct->setShortcut(QKeySequence(Qt::Key_Escape));
0227     ui->m_moveAct->setChecked(true);
0228     ui->m_deleteAct->setData(Canvas::DELETETOOL);
0229     ui->m_addButtonAct->setData(Canvas::BUTTON);
0230 
0231     ui->m_addTextInput->setDefaultAction(ui->m_addTextInputAct);
0232     ui->m_hiddenFieldBtn->setDefaultAction(ui->m_hiddenFieldAct);
0233     ui->m_addTextArea->setDefaultAction(ui->m_addTextAreaAct);
0234     ui->m_addTextField->setDefaultAction(ui->m_addTextFieldAct);
0235     ui->m_addCheckbox->setDefaultAction(ui->m_addCheckBoxAct);
0236     ui->m_addLabelBtn->setDefaultAction(ui->m_addLabelAction);
0237     ui->m_imageBtn->setDefaultAction(ui->m_addImageAction);
0238     ui->m_sliderBtn->setDefaultAction(ui->m_sliderAct);
0239     ui->m_functionBtn->setDefaultAction(ui->m_functionButtonAct);
0240     ui->m_tableFieldBtn->setDefaultAction(ui->m_tableFieldAct);
0241     ui->m_webPageBtn->setDefaultAction(ui->m_webPageAct);
0242     ui->m_nextPageBtn->setDefaultAction(ui->m_nextPageAct);
0243     ui->m_previousPageBtn->setDefaultAction(ui->m_previousPageAct);
0244 
0245     auto notifyDataChanged= [this]() { m_mainCtrl->setModified(true); };
0246 
0247     connect(m_mainCtrl->editCtrl(), &EditorController::pageAdded, this, [this, notifyDataChanged](Canvas* canvas) {
0248         canvas->setModel(m_mainCtrl->generatorCtrl()->fieldModel());
0249 
0250         connect(canvas, &Canvas::pixmapChanged, this, notifyDataChanged);
0251         connect(canvas, &Canvas::pageIdChanged, this, notifyDataChanged);
0252     });
0253 
0254     // m_editorCtrl->addPage();
0255 
0256     QButtonGroup* group= new QButtonGroup();
0257     group->addButton(ui->m_addTextInput);
0258     group->addButton(ui->m_addTextArea);
0259     group->addButton(ui->m_sliderBtn);
0260     group->addButton(ui->m_hiddenFieldBtn);
0261     group->addButton(ui->m_addTextField);
0262     group->addButton(ui->m_addTextInput);
0263     group->addButton(ui->m_addTextArea);
0264     group->addButton(ui->m_addCheckbox);
0265     group->addButton(ui->m_addLabelBtn);
0266     group->addButton(ui->m_addButtonBtn);
0267     group->addButton(ui->m_imageBtn);
0268     group->addButton(ui->m_deleteBtn);
0269     group->addButton(ui->m_moveBtn);
0270     group->addButton(ui->m_functionBtn);
0271     group->addButton(ui->m_tableFieldBtn);
0272     group->addButton(ui->m_webPageBtn);
0273     group->addButton(ui->m_nextPageBtn);
0274     group->addButton(ui->m_previousPageBtn);
0275 
0276     ui->m_moveBtn->setDefaultAction(ui->m_moveAct);
0277     ui->m_deleteBtn->setDefaultAction(ui->m_deleteAct);
0278     ui->m_addButtonBtn->setDefaultAction(ui->m_addButtonAct);
0279 
0280     QmlHighlighter* highlighter= new QmlHighlighter(ui->m_codeEdit->document());
0281     highlighter->setObjectName("HighLighterForQML");
0282 
0283     connect(ui->m_sheetProperties, &QAction::triggered, [=](bool) {
0284         SheetProperties sheetProperties(m_mainCtrl->generatorCtrl(), this);
0285         sheetProperties.exec();
0286     });
0287 
0288     connect(ui->m_quitAction, &QAction::triggered, this, &MainWindow::close);
0289 
0290     auto setCurrentTool= [this]() {
0291         QAction* action= dynamic_cast<QAction*>(sender());
0292         auto tool= static_cast<Canvas::Tool>(action->data().toInt());
0293         m_mainCtrl->editCtrl()->setCurrentTool(tool);
0294     };
0295 
0296     connect(ui->m_addCheckBoxAct, &QAction::triggered, this, setCurrentTool);
0297     connect(ui->m_importFromPdf, &QAction::triggered, this, &MainWindow::openPDF);
0298     connect(ui->m_addTextAreaAct, &QAction::triggered, this, setCurrentTool);
0299     connect(ui->m_hiddenFieldAct, &QAction::triggered, this, setCurrentTool);
0300     connect(ui->m_addLabelAction, &QAction::triggered, this, setCurrentTool);
0301     connect(ui->m_addTextFieldAct, &QAction::triggered, this, setCurrentTool);
0302     connect(ui->m_sliderAct, &QAction::triggered, this, setCurrentTool);
0303     connect(ui->m_addTextInputAct, &QAction::triggered, this, setCurrentTool);
0304     connect(ui->m_addImageAction, &QAction::triggered, this, setCurrentTool);
0305     connect(ui->m_functionButtonAct, &QAction::triggered, this, setCurrentTool);
0306     connect(ui->m_tableFieldAct, &QAction::triggered, this, setCurrentTool);
0307     connect(ui->m_webPageAct, &QAction::triggered, this, setCurrentTool);
0308     connect(ui->m_nextPageAct, &QAction::triggered, this, setCurrentTool);
0309     connect(ui->m_previousPageAct, &QAction::triggered, this, setCurrentTool);
0310     connect(ui->m_moveAct, &QAction::triggered, this, [this](bool triggered) { ui->m_view->setHandle(triggered); });
0311     connect(ui->m_exportPdfAct, &QAction::triggered, this, &MainWindow::exportPDF);
0312     connect(ui->m_moveAct, &QAction::triggered, this, setCurrentTool);
0313     connect(ui->m_deleteAct, &QAction::triggered, this, setCurrentTool);
0314     connect(ui->m_addButtonAct, &QAction::triggered, this, setCurrentTool);
0315 
0316     connect(ui->m_genarateCodeAct, &QAction::triggered, this, &MainWindow::generateAndShowQML);
0317     connect(ui->m_codeToViewAct, &QAction::triggered, this, &MainWindow::showQMLFromCode);
0318 
0319     ///////////////////////
0320     /// @brief action menu
0321     ///////////////////////
0322     connect(ui->m_saveAct, &QAction::triggered, this, &MainWindow::save);
0323     connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::saveAs);
0324     connect(ui->m_openAct, &QAction::triggered, this, &MainWindow::open);
0325     connect(ui->m_checkValidityAct, &QAction::triggered, this, [this]() {
0326         m_mainCtrl->characterCtrl()->checkCharacter(m_mainCtrl->generatorCtrl()->fieldModel()->getRootSection());
0327     });
0328     connect(ui->m_addPage, &QPushButton::clicked, this,
0329             [this]() { m_mainCtrl->processCommand(new AddPageCommand(m_mainCtrl->editCtrl())); });
0330 
0331     connect(ui->m_removePage, &QPushButton::clicked, this, &MainWindow::removePage);
0332     connect(ui->m_selectPageView, &QListWidget::currentRowChanged, m_mainCtrl->editCtrl(),
0333             &EditorController::setCurrentPage);
0334     connect(ui->m_resetIdAct, &QAction::triggered, m_mainCtrl->generatorCtrl()->fieldModel(), &FieldModel::resetAllId);
0335     connect(ui->m_preferencesAction, &QAction::triggered, this, &MainWindow::showPreferences);
0336 
0337     ui->m_characterView->setModel(m_mainCtrl->characterCtrl()->model());
0338 
0339     ui->m_characterView->setContextMenuPolicy(Qt::CustomContextMenu);
0340 
0341     auto header= ui->m_characterView->header();
0342     header->setSectionResizeMode(QHeaderView::Stretch);
0343     header->setSectionResizeMode(0, QHeaderView::ResizeToContents);
0344     header->setMinimumSectionSize(300);
0345 
0346     connect(ui->m_scaleSlider, &QSlider::valueChanged, this, [this](int val) {
0347         qreal scale= val / 100.0;
0348         QTransform transform(scale, 0, 0, scale, 0, 0);
0349         ui->m_view->setTransform(transform);
0350     });
0351 
0352     connect(ui->m_newAct, &QAction::triggered, m_mainCtrl.get(), [this]() { m_mainCtrl->cleanUpData(true); });
0353 
0354     connect(ui->m_openLiberapay, &QAction::triggered, this, [this] {
0355         if(!QDesktopServices::openUrl(QUrl("https://liberapay.com/Rolisteam/donate")))
0356         {
0357             QMessageBox* msgBox= new QMessageBox(
0358                 QMessageBox::Information, tr("Support"),
0359                 tr("The %1 donation page can be found online at :<br> <a "
0360                    "href=\"https://liberapay.com/Rolisteam/donate\">https://liberapay.com/Rolisteam/donate</a>")
0361                     .arg(m_preferences->value("Application_Name", "rolisteam").toString()),
0362                 QMessageBox::Ok);
0363             msgBox->exec();
0364         }
0365     });
0366 
0367     // Help Menu
0368     connect(ui->m_aboutRcseAct, &QAction::triggered, this, [this]() {
0369         AboutRcse dialog(version::version, this);
0370         dialog.exec();
0371     });
0372     connect(ui->m_onlineHelpAct, &QAction::triggered, this, &MainWindow::helpOnLine);
0373 
0374     ui->m_addImageBtn->setDefaultAction(ui->m_addImageAct);
0375     ui->m_removeImgBtn->setDefaultAction(ui->m_deleteImageAct);
0376 
0377     connect(ui->m_addImageAct, &QAction::triggered, this, &MainWindow::openImage);
0378     connect(ui->m_deleteImageAct, &QAction::triggered, this, [this]() {
0379         auto index= ui->m_imageList->currentIndex();
0380         m_mainCtrl->imageCtrl()->removeImage(index.row());
0381     });
0382 
0383     readSettings();
0384     m_logPanel->initSetting();
0385 
0386     m_mainCtrl->cleanUpData();
0387 
0388     ui->m_view->setHandle(ui->m_moveAct->isChecked());
0389     connect(m_mainCtrl.get(), &rcse::MainController::modifiedChanged, this, &MainWindow::setWindowModified);
0390     connect(m_mainCtrl.get(), &rcse::MainController::currentFileChanged, this, &MainWindow::updateTitle);
0391 
0392     updateTitle();
0393 }
0394 MainWindow::~MainWindow()
0395 {
0396     delete ui;
0397 }
0398 
0399 void MainWindow::readSettings()
0400 {
0401     QSettings settings("rolisteam", QString("rcse/preferences"));
0402     restoreState(settings.value("windowState").toByteArray());
0403     settings.value("Maximized", false).toBool();
0404     // if(!maxi)
0405     {
0406         restoreGeometry(settings.value("geometry").toByteArray());
0407     }
0408     m_recentFiles= settings.value("recentFile").toStringList();
0409     m_mainCtrl->logCtrl()->setLogLevel(settings.value("RcseLogLevel").value<LogController::LogLevel>());
0410     m_preferences->readSettings(settings);
0411 
0412     updateRecentFileAction();
0413 }
0414 void MainWindow::writeSettings()
0415 {
0416     QSettings settings("rolisteam", QString("rcse/preferences"));
0417     settings.setValue("geometry", saveGeometry());
0418     settings.setValue("windowState", saveState());
0419     settings.setValue("Maximized", isMaximized());
0420     settings.setValue("recentFile", m_recentFiles);
0421     settings.setValue("RcseLogLevel", m_mainCtrl->logCtrl()->logLevel());
0422     m_preferences->writeSettings(settings);
0423 }
0424 
0425 void MainWindow::updateTitle()
0426 {
0427     auto filename= m_mainCtrl->currentFile();
0428     auto shortName= tr("Untitled");
0429 
0430     if(!filename.isEmpty())
0431     {
0432         shortName= QFileInfo(filename).fileName();
0433         m_recentFiles.removeAll(filename);
0434         m_recentFiles.prepend(filename);
0435         updateRecentFileAction();
0436     }
0437     setWindowTitle(QStringLiteral("%1[*] - %2").arg(shortName).arg("RCSE"));
0438 }
0439 
0440 bool MainWindow::eventFilter(QObject* obj, QEvent* event)
0441 {
0442     if((obj == ui->m_view) && (event->type() == QEvent::Wheel))
0443     {
0444         return wheelEventForView(dynamic_cast<QWheelEvent*>(event));
0445     }
0446     else
0447         return QMainWindow::eventFilter(obj, event);
0448 }
0449 void MainWindow::closeEvent(QCloseEvent* event)
0450 {
0451     if(mayBeSaved())
0452     {
0453         writeSettings();
0454         event->accept();
0455     }
0456     else
0457     {
0458         event->ignore();
0459     }
0460 }
0461 void MainWindow::helpOnLine()
0462 {
0463     if(!QDesktopServices::openUrl(QUrl(version::documation_site)))
0464     {
0465         QMessageBox* msgBox= new QMessageBox(QMessageBox::Information, tr("Help"),
0466                                              tr("Documentation of Rcse can be found online at :<br> <a "
0467                                                 "href=\"%1\">%1</a>")
0468                                                  .arg(version::documation_site));
0469         msgBox->exec();
0470     }
0471 }
0472 bool MainWindow::mayBeSaved()
0473 {
0474     if(isWindowModified())
0475     {
0476         QMessageBox msgBox(this);
0477 
0478         QString message(tr("The charactersheet has unsaved changes."));
0479         QString msg= QStringLiteral("RCSE");
0480 
0481         msgBox.setIcon(QMessageBox::Question);
0482         msgBox.addButton(QMessageBox::Cancel);
0483         msgBox.addButton(QMessageBox::Save);
0484         msgBox.addButton(QMessageBox::Discard);
0485         msgBox.setWindowTitle(tr("Quit %1 ").arg(msg));
0486 
0487         msgBox.setText(message);
0488         int value= msgBox.exec();
0489         if(QMessageBox::Cancel == value)
0490         {
0491             return false;
0492         }
0493         else if(QMessageBox::Save == value) // saving
0494         {
0495             save();
0496             return true;
0497         }
0498         else if(QMessageBox::Discard == value)
0499         {
0500             return true;
0501         }
0502     }
0503     return true;
0504 }
0505 
0506 bool MainWindow::wheelEventForView(QWheelEvent* event)
0507 {
0508     if(nullptr == event)
0509         return false;
0510 
0511     if(event->modifiers() & Qt::ShiftModifier)
0512     {
0513         ui->m_view->setResizeAnchor(QGraphicsView::AnchorUnderMouse);
0514         ui->m_view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
0515         // Scale the view / do the zoom
0516         double scaleFactor= 1.1;
0517 
0518         if((event->angleDelta().x() > 0) && (m_counterZoom < 20))
0519         {
0520             ui->m_view->scale(scaleFactor, scaleFactor);
0521             ++m_counterZoom;
0522         }
0523         else if(m_counterZoom > -20)
0524         {
0525             --m_counterZoom;
0526             ui->m_view->scale(1.0 / scaleFactor, 1.0 / scaleFactor);
0527         }
0528         ui->m_view->setResizeAnchor(QGraphicsView::NoAnchor);
0529         ui->m_view->setTransformationAnchor(QGraphicsView::NoAnchor);
0530         return true;
0531     }
0532     return false;
0533 }
0534 
0535 void MainWindow::openPDF()
0536 {
0537     auto pdf= new PdfManager(this);
0538     if(pdf->exec())
0539     {
0540         BusyIndicatorDialog dialog(tr("Image Generation"), tr("Image generation in progress"),
0541                                    ":/rcstyle/busy_movie.gif", this);
0542 
0543         auto fvoid= QtConcurrent::run([pdf, this, &dialog]() {
0544             auto imgs= pdf->images();
0545             m_mainCtrl->editCtrl()->loadImages(imgs);
0546             pdf->deleteLater();
0547             dialog.accept();
0548             dialog.deleteLater();
0549         });
0550 
0551         dialog.exec();
0552     }
0553 }
0554 
0555 void MainWindow::openBackgroundImage()
0556 {
0557     QString supportedFormat("Supported files (*.jpg *.png *.xpm)");
0558 
0559     QString img= QFileDialog::getOpenFileName(this, tr("Open Background Image"), QDir::homePath(), supportedFormat);
0560 
0561     if(img.isEmpty())
0562         return;
0563 
0564     m_mainCtrl->editCtrl()->loadImageFromUrl(QUrl::fromLocalFile(img));
0565 }
0566 
0567 void MainWindow::openImage()
0568 {
0569     QString supportedFormat("Supported files (*.jpg *.png);;All Files (*.*)");
0570     QString img= QFileDialog::getOpenFileName(this, tr("Open Image"), QDir::homePath(), supportedFormat);
0571 
0572     if(img.isEmpty())
0573         return;
0574 
0575     QPixmap map(img);
0576     m_mainCtrl->imageCtrl()->addImage(map, img);
0577 }
0578 
0579 void MainWindow::showPreferences()
0580 {
0581     PreferencesDialog dialog;
0582     if(m_preferences->value("hasCustomPath", false).toBool())
0583     {
0584         dialog.setGenerationPath(m_preferences->value("GenerationCustomPath", QDir::homePath()).toString());
0585     }
0586     if(QDialog::Accepted == dialog.exec())
0587     {
0588         m_preferences->registerValue("hasCustomPath", dialog.hasCustomPath());
0589         m_preferences->registerValue("GenerationCustomPath", dialog.generationPath());
0590     }
0591 }
0592 
0593 bool MainWindow::saveAs()
0594 {
0595     auto filename= QFileDialog::getSaveFileName(this, tr("Save CharacterSheet"), QDir::homePath(),
0596                                                 tr("Rolisteam CharacterSheet (*.rcs)"));
0597     if(filename.isEmpty())
0598         return false;
0599 
0600     if(!filename.endsWith(".rcs"))
0601     {
0602         filename.append(QStringLiteral(".rcs"));
0603     }
0604     return saveFile(filename);
0605 }
0606 
0607 bool MainWindow::save()
0608 {
0609     if(m_mainCtrl->currentFile().isEmpty())
0610         return saveAs();
0611     else
0612         return saveFile(m_mainCtrl->currentFile());
0613 }
0614 
0615 bool MainWindow::saveFile(const QString& filename)
0616 {
0617     if(filename.isEmpty())
0618         return false;
0619 
0620     // init Json
0621     IOWorker::saveFile(SerializerHelper::buildData(m_mainCtrl.get()), filename);
0622     m_mainCtrl->setCurrentFile(filename);
0623     m_mainCtrl->setModified(false);
0624     return true;
0625 }
0626 bool MainWindow::loadFile(const QString& filename)
0627 {
0628     if(filename.isEmpty())
0629         return false;
0630 
0631     SerializerHelper::fetchMainController(m_mainCtrl.get(), IOWorker::readFileToObject(filename));
0632     m_mainCtrl->setCurrentFile(filename);
0633     m_mainCtrl->setModified(false);
0634     return true;
0635 }
0636 void MainWindow::open()
0637 {
0638     if(mayBeSaved())
0639     {
0640         auto filename= QFileDialog::getOpenFileName(this, tr("Save CharacterSheet"), QDir::homePath(),
0641                                                     tr("Rolisteam CharacterSheet (*.rcs)"));
0642         if(!filename.isEmpty())
0643         {
0644             loadFile(filename);
0645         }
0646     }
0647 }
0648 
0649 void MainWindow::openRecentFile()
0650 {
0651     if(mayBeSaved())
0652     {
0653         QAction* act= qobject_cast<QAction*>(sender());
0654         if(act)
0655         {
0656             loadFile(act->data().toString());
0657         }
0658     }
0659 }
0660 
0661 void MainWindow::updatePageSelector()
0662 {
0663     auto current= m_mainCtrl->editCtrl()->currentPage();
0664     ui->m_selectPageView->clear();
0665     QStringList list;
0666     auto s= m_mainCtrl->editCtrl()->pageCount();
0667     for(unsigned int i= 0; i < s; ++i)
0668     {
0669         list << QStringLiteral("Page %1").arg(i + 1);
0670     }
0671     ui->m_selectPageView->addItems(list);
0672     ui->m_selectPageView->setCurrentRow(current);
0673 }
0674 
0675 void MainWindow::updateRecentFileAction()
0676 {
0677     m_recentFiles.erase(
0678         std::remove_if(m_recentFiles.begin(), m_recentFiles.end(), [](QString const& f) { return !QFile::exists(f); }),
0679         m_recentFiles.end());
0680 
0681     int i= 0;
0682     for(auto act : m_recentActions)
0683     {
0684         if(i >= m_recentFiles.size())
0685         {
0686             act->setVisible(false);
0687             continue;
0688         }
0689         else
0690         {
0691             act->setVisible(true);
0692             act->setText(QStringLiteral("&%1  %2").arg(i + 1).arg(QFileInfo(m_recentFiles[i]).fileName()));
0693             act->setData(m_recentFiles[i]);
0694         }
0695         ++i;
0696     }
0697     m_separatorAction->setVisible(!m_recentFiles.empty());
0698 }
0699 
0700 void MainWindow::generateAndShowQML()
0701 {
0702     if(m_mainCtrl->generatorCtrl()->textEdited())
0703     {
0704         QMessageBox::StandardButton btn= QMessageBox::question(
0705             this, tr("Do you want to erase current QML code ?"),
0706             tr("Generate QML code will override any change you made in the QML.<br/>Do you really want to generate QML "
0707                "code ?"),
0708             QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
0709 
0710         if(btn == QMessageBox::Cancel)
0711         {
0712             return;
0713         }
0714     }
0715     m_mainCtrl->generatorCtrl()->generateQML(m_mainCtrl->imageCtrl());
0716     showQMLFromCode();
0717 }
0718 
0719 void MainWindow::showQMLFromCode()
0720 {
0721     auto data= ui->m_codeEdit->toPlainText();
0722 
0723     // setTextEdited(false);
0724     auto provider= m_mainCtrl->imageCtrl()->provider();
0725 
0726     QTemporaryFile file;
0727     if(file.open()) // QIODevice::WriteOnly
0728     {
0729         file.write(data.toUtf8());
0730         file.close();
0731     }
0732 
0733     ui->m_quickview->engine()->clearComponentCache();
0734     ui->m_quickview->engine()->addImportPath("qrc:/charactersheet/qml");
0735     ui->m_quickview->engine()->addImageProvider(QLatin1String("rcs"), provider);
0736 
0737     auto charactersheet
0738         = m_mainCtrl->characterCtrl()->characterSheetFromUuid(m_mainCtrl->generatorCtrl()->uuidCharacter());
0739     if(nullptr != charactersheet)
0740     {
0741         for(int i= 0; i < charactersheet->getFieldCount(); ++i)
0742         {
0743             TreeSheetItem* field= charactersheet->getFieldAt(i);
0744             if(nullptr != field)
0745             {
0746                 ui->m_quickview->engine()->rootContext()->setContextProperty(field->id(), field);
0747             }
0748         }
0749     }
0750     else
0751     {
0752         QList<TreeSheetItem*> list= m_mainCtrl->generatorCtrl()->fieldModel()->children();
0753         for(TreeSheetItem* item : list)
0754         {
0755             ui->m_quickview->engine()->rootContext()->setContextProperty(item->id(), item);
0756         }
0757     }
0758 
0759     ui->m_quickview->engine()->rootContext()->setContextProperty("_character",
0760                                                                  m_mainCtrl->generatorCtrl()->mockCharacter());
0761 
0762     ui->m_quickview->setSource(QUrl::fromLocalFile(file.fileName()));
0763     m_mainCtrl->displayQmlError(ui->m_quickview->errors());
0764     ui->m_quickview->setResizeMode(QQuickWidget::SizeRootObjectToView);
0765     // QObject* root= ui->m_quickview->rootObject();
0766     /*connect(root, SIGNAL(showText(QString)), this, SIGNAL(reportLog(QString)));
0767     connect(root, SIGNAL(rollDiceCmd(QString, bool)), this, SLOT(rollDice(QString, bool)));
0768     connect(root, SIGNAL(rollDiceCmd(QString)), this, SLOT(rollDice(QString)));*/
0769 }
0770 
0771 /*void MainWindow::saveQML()
0772 {
0773     QString qmlFile= QFileDialog::getOpenFileName(this, tr("Save CharacterSheet View"), QDir::homePath(),
0774                                                   tr("CharacterSheet View (*.qml)"));
0775     if(!qmlFile.isEmpty())
0776     {
0777         QString data= ui->m_codeEdit->toPlainText();
0778         generateQML(data);
0779         ui->m_codeEdit->setPlainText(data);
0780 
0781         QFile file(qmlFile);
0782         if(file.open(QIODevice::WriteOnly))
0783         {
0784             file.write(data.toLatin1());
0785             file.close();
0786         }
0787     }
0788 }*/
0789 
0790 void MainWindow::openQML()
0791 {
0792     QString qmlFile= QFileDialog::getOpenFileName(this, tr("Save CharacterSheet View"), QDir::homePath(),
0793                                                   tr("Rolisteam CharacterSheet View (*.qml)"));
0794     if(!qmlFile.isEmpty())
0795     {
0796         QFile file(qmlFile);
0797         if(file.open(QIODevice::ReadOnly))
0798         {
0799             QString qmlContent= file.readAll();
0800             ui->m_codeEdit->setPlainText(qmlContent);
0801             showQMLFromCode();
0802         }
0803     }
0804 }
0805 
0806 void MainWindow::addBackgroundImage()
0807 {
0808     QString supportedFormat("Supported files (*.jpg *.png);;All Files (*.*)");
0809     QString img= QFileDialog::getOpenFileName(this, tr("Open Background Image"), QDir::homePath(), supportedFormat);
0810     if(!img.isEmpty())
0811         return;
0812 
0813     QPixmap map(img);
0814     m_mainCtrl->imageCtrl()->addBackgroundImage(m_mainCtrl->editCtrl()->currentPage(), map, img, QString());
0815 }
0816 
0817 void MainWindow::exportPDF()
0818 {
0819     QObject* root= ui->m_quickview->rootObject();
0820     if(nullptr == root)
0821         return;
0822 
0823     auto maxPage= QQmlProperty::read(root, "maxPage").toInt();
0824     auto currentPage= QQmlProperty::read(root, "page").toInt();
0825     auto sheetW= QQmlProperty::read(root, "width").toReal();
0826     auto sheetH= QQmlProperty::read(root, "height").toReal();
0827 
0828     ui->m_tabWidget->setCurrentWidget(ui->m_qml);
0829 
0830     QObject* imagebg= root->findChild<QObject*>("imagebg");
0831     if(nullptr != imagebg)
0832     {
0833         sheetW= QQmlProperty::read(imagebg, "width").toReal();
0834         sheetH= QQmlProperty::read(imagebg, "height").toReal();
0835     }
0836 
0837     QPrinter printer;
0838     QPrintDialog dialog(&printer, this);
0839     if(dialog.exec() == QDialog::Accepted)
0840     {
0841         QPainter painter;
0842         if(painter.begin(&printer))
0843         {
0844             for(int i= 0; i <= maxPage; ++i)
0845             {
0846                 root->setProperty("page", i);
0847                 ui->m_quickview->repaint();
0848                 QTimer timer;
0849                 timer.setSingleShot(true);
0850                 QEventLoop loop;
0851                 connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
0852                 timer.start(m_preferences->value("waitingTimeBetweenPage", 300).toInt());
0853                 loop.exec();
0854 
0855                 auto image= ui->m_quickview->grabFramebuffer();
0856                 QRectF rect(0, 0, printer.width(), printer.height());
0857                 QRectF source(0.0, 0.0, sheetW, sheetH);
0858                 painter.drawImage(rect, image, source);
0859                 if(i != maxPage)
0860                     printer.newPage();
0861             }
0862             painter.end();
0863         }
0864     }
0865     root->setProperty("page", currentPage);
0866 }
0867 
0868 void MainWindow::addPage() {}
0869 
0870 void MainWindow::removePage()
0871 {
0872     m_mainCtrl->processCommand(
0873         new DeletePageCommand(m_mainCtrl->editCtrl(), m_mainCtrl->generatorCtrl()->fieldModel()));
0874 }
0875 
0876 void MainWindow::showContextMenuForImageTab()
0877 {
0878     QMenu menu;
0879 
0880     menu.addAction(m_copyPath);
0881     menu.addAction(m_copyUrl);
0882     menu.addSeparator();
0883     menu.addAction(m_replaceImage);
0884     menu.addAction(m_removeImage);
0885     menu.addAction(m_reloadImageFromFile);
0886 
0887     menu.exec(QCursor::pos());
0888 }
0889 
0890 void MainWindow::showContextMenuForCharacterTab()
0891 {
0892     QMenu menu;
0893     menu.addAction(m_addCharacter);
0894     // menu.addAction(m_copyCharacter);
0895     menu.addSeparator();
0896     menu.addAction(m_applyValueOnAllCharacters);
0897     menu.addAction(m_applyValueOnSelectedCharacterLines);
0898     //  menu.addAction(m_applyValueOnAllCharacterLines);
0899     menu.addAction(m_defineAsTabName);
0900     menu.addSeparator();
0901     menu.addAction(m_deleteCharacter);
0902     menu.exec(QCursor::pos());
0903 }
0904 
0905 void MainWindow::setUpActionForImageTab()
0906 {
0907     ui->m_imageList->setModel(m_mainCtrl->imageCtrl()->model());
0908 
0909     auto headerView= ui->m_imageList->horizontalHeader();
0910     // headerView->setStretchLastSection(true);
0911     headerView->setMinimumSectionSize(300);
0912     headerView->setSectionResizeMode(QHeaderView::Stretch);
0913     headerView->setSectionResizeMode(2, QHeaderView::ResizeToContents);
0914 
0915 #ifndef Q_OS_OSX
0916     ui->m_imageList->setAlternatingRowColors(true);
0917 #endif
0918     m_copyPath= new QAction(tr("Copy Path"), this);
0919     m_copyPath->setShortcut(QKeySequence("CTRL+c"));
0920 
0921     m_copyUrl= new QAction(tr("Copy Url"), this);
0922     m_copyUrl->setShortcut(QKeySequence("CTRL+u"));
0923 
0924     m_replaceImage= new QAction(tr("Replace Image"), this);
0925     m_removeImage= new QAction(tr("Remove Image"), this);
0926     m_reloadImageFromFile= new QAction(tr("Reload image from file"), this);
0927 
0928     connect(m_copyPath, &QAction::triggered, this,
0929             [this]() { m_mainCtrl->imageCtrl()->copyPath(ui->m_imageList->currentIndex()); });
0930     connect(m_copyUrl, &QAction::triggered, this,
0931             [this]() { m_mainCtrl->imageCtrl()->copyUrl(ui->m_imageList->currentIndex()); });
0932     connect(m_replaceImage, &QAction::triggered, this, [this]() {
0933         auto filepath= QFileDialog::getOpenFileName(this, tr("Load Image"), QDir::homePath(),
0934                                                     tr("Supported Image Format (*.jpg *.png *.svg *.gif)"));
0935         if(filepath.isEmpty())
0936             return;
0937         m_mainCtrl->imageCtrl()->replaceImage(ui->m_imageList->currentIndex(), filepath);
0938     });
0939     connect(m_removeImage, &QAction::triggered, this,
0940             [this]() { m_mainCtrl->imageCtrl()->removeImage(ui->m_imageList->currentIndex().row()); });
0941     connect(m_reloadImageFromFile, &QAction::triggered, this,
0942             [this]() { m_mainCtrl->imageCtrl()->reloadImage(ui->m_imageList->currentIndex()); });
0943 }
0944 
0945 void MainWindow::setUpActionForCharacterTab()
0946 {
0947     m_addCharacter= new QAction(tr("Add character"), this);
0948     m_deleteCharacter= new QAction(tr("Delete character"), this);
0949     // m_copyCharacter= new QAction(tr("Copy character"), this);
0950     m_defineAsTabName= new QAction(tr("Character's Name"), this);
0951 
0952     m_applyValueOnSelectedCharacterLines= new QAction(tr("Apply on Selection"), this);
0953     m_applyValueOnAllCharacters= new QAction(tr("Apply on all characters"), this);
0954 
0955     connect(m_addCharacter, &QAction::triggered, m_mainCtrl->characterCtrl(),
0956             &CharacterController::sendAddCharacterCommand);
0957     connect(m_deleteCharacter, &QAction::triggered, m_mainCtrl->characterCtrl(),
0958             [this]() { m_mainCtrl->characterCtrl()->sendRemoveCharacterCommand(ui->m_characterView->currentIndex()); });
0959 
0960     connect(m_applyValueOnAllCharacters, &QAction::triggered, this,
0961             [this]() { m_mainCtrl->characterCtrl()->applyOnAllCharacter(ui->m_characterView->currentIndex()); });
0962 
0963     connect(m_applyValueOnSelectedCharacterLines, &QAction::triggered, this, [this]() {
0964         m_mainCtrl->characterCtrl()->applyOnSelection(ui->m_characterView->currentIndex(),
0965                                                       ui->m_characterView->selectionModel()->selectedIndexes());
0966     });
0967 
0968     connect(m_defineAsTabName, &QAction::triggered, this, [this]() {
0969         auto index= ui->m_characterView->currentIndex();
0970         auto name= index.data().toString();
0971         if(name.isEmpty())
0972             return;
0973         CharacterSheet* sheet= m_mainCtrl->characterCtrl()->model()->getCharacterSheet(index.column() - 1);
0974         sheet->setName(name);
0975         // emit dataChanged();
0976     });
0977     connect(m_mainCtrl->characterCtrl()->model(), &CharacterSheetModel::columnsInserted, this, [this]() {
0978         auto count= m_mainCtrl->characterCtrl()->model()->columnCount();
0979         if(count < 2)
0980             return;
0981         auto w= ui->m_characterView->geometry().width() / count;
0982         for(int i= 0; i < count; ++i)
0983         {
0984             ui->m_characterView->setColumnWidth(i, w);
0985         }
0986     });
0987 }
0988 }