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 }