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

0001 /*************************************************************************
0002  *        Copyright (C) 2007 by Romain Campioni                          *
0003  *        Copyright (C) 2009 by Renaud Guezennec                         *
0004  *        Copyright (C) 2010 by Berenger Morel                           *
0005  *        Copyright (C) 2010 by Joseph Boudou                            *
0006  *                                                                       *
0007  *        https://rolisteam.org/                                      *
0008  *                                                                       *
0009  *   rolisteam is free software; you can redistribute it and/or modify   *
0010  *   it under the terms of the GNU General Public License as published   *
0011  *   by the Free Software Foundation; either version 2 of the License,   *
0012  *   or (at your option) any later version.                              *
0013  *                                                                       *
0014  *   This program is distributed in the hope that it will be useful,     *
0015  *   but WITHOUT ANY WARRANTY; without even the implied warranty of      *
0016  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       *
0017  *   GNU General Public License for more details.                        *
0018  *                                                                       *
0019  *   You should have received a copy of the GNU General Public License   *
0020  *   along with this program; if not, write to the                       *
0021  *   Free Software Foundation, Inc.,                                     *
0022  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           *
0023  *************************************************************************/
0024 #include "dialogs/vmapwizzarddialog.h"
0025 #include <QApplication>
0026 #include <QBitmap>
0027 #include <QBuffer>
0028 #include <QCommandLineParser>
0029 #include <QDesktopServices>
0030 #include <QFileDialog>
0031 #include <QFileInfo>
0032 #include <QLoggingCategory>
0033 #include <QMenuBar>
0034 #include <QMessageBox>
0035 #include <QProcess>
0036 #include <QSettings>
0037 #include <QStatusBar>
0038 #include <QStringBuilder>
0039 #include <QSystemTrayIcon>
0040 #include <QTime>
0041 #include <QUrl>
0042 #include <QUuid>
0043 #include <QWebEngineProfile>
0044 #include <QWebEngineSettings>
0045 #include <algorithm>
0046 
0047 #include "mainwindow.h"
0048 #include "ui_mainwindow.h"
0049 
0050 #include "common/logcategory.h"
0051 #include "controller/contentcontroller.h"
0052 #include "controller/playercontroller.h"
0053 #include "data/person.h"
0054 #include "data/player.h"
0055 #include "model/historymodel.h"
0056 #include "preferences/preferencesmanager.h"
0057 #include "rwidgets/customs/shortcutvisitor.h"
0058 #include "rwidgets/customs/workspace.h"
0059 #include "rwidgets/dialogs/historyviewerdialog.h"
0060 #include "rwidgets/dialogs/imageselectordialog.h"
0061 #include "rwidgets/dialogs/keygeneratordialog.h"
0062 #include "rwidgets/dialogs/preferencesdialog.h"
0063 #include "rwidgets/dialogs/shortcuteditordialog.h"
0064 #include "rwidgets/dialogs/tipofdayviewer.h"
0065 #include "rwidgets/docks/notificationzone.h"
0066 #include "rwidgets/docks/playerspanel.h"
0067 #include "rwidgets/gmtoolbox/gamemastertool.h"
0068 #include "rwidgets/mediacontainers/mediacontainer.h"
0069 
0070 // worker
0071 #include "utils/networkdownloader.h"
0072 #include "worker/iohelper.h"
0073 #include "worker/modelhelper.h"
0074 #include "worker/utilshelper.h"
0075 
0076 #include "data/campaignmanager.h"
0077 #include "model/actiononlistmodel.h"
0078 #include "rwidgets/dialogs/campaignproperties.h"
0079 
0080 // LOG
0081 #include "common/logcontroller.h"
0082 
0083 // Controller
0084 #include "controller/instantmessagingcontroller.h"
0085 #include "controller/networkcontroller.h"
0086 #include "controller/preferencescontroller.h"
0087 #include "controller/view_controller/imageselectorcontroller.h"
0088 
0089 // dialogs
0090 #include "rwidgets/dialogs/aboutrolisteam.h"
0091 #include "rwidgets/dialogs/campaignintegritydialog.h"
0092 #include "rwidgets/dialogs/importdatafromcampaigndialog.h"
0093 #include "rwidgets/dialogs/newfiledialog.h"
0094 
0095 // GMToolBox
0096 #include "rwidgets/gmtoolbox/NameGenerator/namegeneratorwidget.h"
0097 #include "rwidgets/gmtoolbox/UnitConvertor/convertor.h"
0098 
0099 // session
0100 #include "rwidgets/docks/antagonistboard.h"
0101 #include "rwidgets/docks/campaigndock.h"
0102 #include "utils/iohelper.h"
0103 
0104 Q_LOGGING_CATEGORY(WidgetClient, "WidgetClient")
0105 
0106 MainWindow::MainWindow(GameController* game, const QStringList& args)
0107     : QMainWindow()
0108     , m_gameController(game)
0109 #ifndef NULL_PLAYER
0110     , m_audioPlayer(new AudioPlayer(m_gameController->audioController(), this))
0111 #endif
0112     , m_dockLogUtil(new NotificationZone(game->logController(), this))
0113     , m_systemTray(new QSystemTrayIcon)
0114     , m_ui(new Ui::MainWindow)
0115     , m_roomPanelDockWidget(new QDockWidget(this))
0116 {
0117     parseCommandLineArguments(args);
0118     setAcceptDrops(true);
0119     m_systemTray->setIcon(QIcon::fromTheme("500-symbole"));
0120     m_systemTray->show();
0121 
0122     // ALLOCATIONS
0123     m_campaignDock.reset(new campaign::CampaignDock(m_gameController->campaignManager()->editor()));
0124     connect(m_campaignDock.get(), &campaign::CampaignDock::openResource, this,
0125             [this](const QString& id, const QString& path, Core::ContentType type)
0126             {
0127                 std::map<QString, QVariant> vec;
0128                 vec.insert({Core::keys::KEY_PATH, path});
0129                 vec.insert({Core::keys::KEY_UUID, id});
0130                 vec.insert({Core::keys::KEY_TYPE, QVariant::fromValue(type)});
0131                 vec.insert({Core::keys::KEY_SERIALIZED, utils::IOHelper::loadFile(path)});
0132                 vec.insert({Core::keys::KEY_INTERNAL, true});
0133                 auto localId= m_gameController->localPlayerId();
0134                 vec.insert({Core::keys::KEY_OWNERID, localId});
0135                 vec.insert({Core::keys::KEY_LOCALID, localId});
0136                 m_gameController->openMedia(vec);
0137             });
0138     connect(m_campaignDock.get(), &campaign::CampaignDock::removeFile, this,
0139             [this](const QString& path)
0140             {
0141                 auto campaignManager= m_gameController->campaignManager();
0142                 campaignManager->removeFile(path);
0143             });
0144 
0145     connect(m_gameController->campaignManager(), &campaign::CampaignManager::createBlankFile, this,
0146             [this](const QString& path, Core::MediaType mediaType)
0147             {
0148                 std::map<QString, QVariant> params;
0149                 /// TODO change mediaType to ContentType.
0150                 params.insert({Core::keys::KEY_TYPE, QVariant::fromValue(mediaType)});
0151                 params.insert({Core::keys::KEY_PATH, path});
0152                 m_gameController->newMedia(params);
0153             });
0154 
0155     connect(m_gameController->campaignManager(), &campaign::CampaignManager::createBlankFile, this,
0156             [this](const QString& path, Core::MediaType mediaType)
0157             {
0158                 std::map<QString, QVariant> params;
0159                 /// TODO change mediaType to ContentType.
0160                 params.insert({Core::keys::KEY_TYPE, QVariant::fromValue(mediaType)});
0161                 params.insert({Core::keys::KEY_PATH, path});
0162                 m_gameController->newMedia(params);
0163             });
0164 
0165     connect(m_gameController, &GameController::dataLoaded, this,
0166             [this](const QStringList missignFiles, const QStringList unmanagedFile)
0167             {
0168                 if(missignFiles.isEmpty() && unmanagedFile.isEmpty())
0169                     return;
0170                 campaign::CampaignIntegrityDialog dialog(missignFiles, unmanagedFile, m_gameController->campaignRoot(),
0171                                                          this);
0172                 auto val= dialog.exec();
0173                 if(val == QDialog::Accepted)
0174                 {
0175                     auto const& missingActions= dialog.missingFileActions();
0176                     auto const& unmanagedActions= dialog.unmanagedFileActions();
0177                     auto ctrl= m_gameController->campaignManager();
0178 
0179                     QList<QPair<QString, Core::CampaignAction>> list;
0180                     std::transform(std::begin(missingActions), std::end(missingActions), std::back_inserter(list),
0181                                    [](const DataInfo& info)
0182                                    {
0183                                        return QPair<QString, Core::CampaignAction>(
0184                                            {info.data, info.indexAction == 0 ? Core::CampaignAction::ForgetAction :
0185                                                                                Core::CampaignAction::CreateAction});
0186                                    });
0187                     ctrl->performAction(list);
0188 
0189                     QList<QPair<QString, Core::CampaignAction>> list2;
0190                     std::transform(std::begin(unmanagedActions), std::end(unmanagedActions), std::back_inserter(list2),
0191                                    [](const DataInfo& info)
0192                                    {
0193                                        return QPair<QString, Core::CampaignAction>(
0194                                            {info.data, info.indexAction == 0 ? Core::CampaignAction::ManageAction :
0195                                                                                Core::CampaignAction::DeleteAction});
0196                                    });
0197                     ctrl->performAction(list2);
0198                 }
0199             });
0200 
0201     m_antagonistWidget.reset(new campaign::AntagonistBoard(m_gameController->campaignManager()->editor(), this));
0202 
0203     m_gmToolBoxList.append({new NameGeneratorWidget(this), new GMTOOL::Convertor(this)});
0204     m_roomPanel= new ChannelListPanel(m_gameController->networkController(), this);
0205 
0206     connect(m_gameController, &GameController::updateAvailableChanged, this, &MainWindow::showUpdateNotification);
0207     connect(m_gameController, &GameController::tipOfDayChanged, this, &MainWindow::showTipChecker);
0208     connect(m_gameController, &GameController::localPlayerIdChanged, this,
0209             [this]() { m_roomPanel->setLocalPlayerId(m_gameController->localPlayerId()); });
0210 
0211     m_ui->setupUi(this);
0212 
0213     m_separatorAction= m_ui->m_fileMenu->insertSeparator(m_ui->m_closeAction);
0214     m_separatorAction->setVisible(false);
0215     // registerQmlTypes();
0216 
0217 #ifdef HAVE_WEBVIEW
0218     auto defaultProfile= QWebEngineProfile::defaultProfile();
0219     QWebEngineSettings* defaultSettings= defaultProfile->settings();
0220     defaultSettings->setAttribute(QWebEngineSettings::PluginsEnabled, true);
0221     defaultSettings->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, false);
0222 #endif
0223 
0224     m_preferences= m_gameController->preferencesManager();
0225 
0226     auto func= [](QVariant var)
0227     {
0228         auto v= var.toInt();
0229         if(var.isNull())
0230             v= 6;
0231         VisualItem::setHighlightWidth(v);
0232     };
0233     m_preferences->registerLambda(QStringLiteral("VMAP::highlightPenWidth"), func);
0234     auto func2= [](QVariant var)
0235     {
0236         auto v= var.value<QColor>();
0237         if(!v.isValid())
0238             v= QColor(Qt::red);
0239         VisualItem::setHighlightColor(v);
0240     };
0241     m_preferences->registerLambda(QStringLiteral("VMAP::highlightColor"), func2);
0242 
0243     connect(m_ui->m_mediaTitleAct, &QAction::toggled, this,
0244             [this](bool b)
0245             { m_ui->m_toolBar->setToolButtonStyle(b ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly); });
0246 
0247     connect(m_gameController->contentController(), &ContentController::sessionChanged, this,
0248             &MainWindow::setWindowModified);
0249     connect(m_gameController->contentController(), &ContentController::canPasteChanged, m_ui->m_pasteAct,
0250             &QAction::setEnabled);
0251 
0252     connect(m_gameController->networkController(), &NetworkController::connectedChanged, this,
0253             [this](bool connected)
0254             {
0255                 if(connected)
0256                     postConnection();
0257 
0258                 updateWindowTitle();
0259                 m_ui->m_changeProfileAct->setEnabled(connected);
0260                 m_ui->m_disconnectAction->setEnabled(connected);
0261             });
0262 
0263     connect(m_gameController->campaign(), &campaign::Campaign::nameChanged, this, &MainWindow::updateWindowTitle);
0264 
0265     // connect(m_sessionManager, &SessionManager::openResource, this,
0266     // &MainWindow::openResource);
0267 
0268     /// Create all GM toolbox widget
0269     for(auto& gmTool : m_gmToolBoxList)
0270     {
0271         QWidget* wid= dynamic_cast<QWidget*>(gmTool);
0272 
0273         if(wid == nullptr)
0274             continue;
0275 
0276         QDockWidget* widDock= new QDockWidget(this);
0277         widDock->setAllowedAreas(Qt::AllDockWidgetAreas);
0278         widDock->setWidget(wid);
0279         widDock->setWindowTitle(wid->windowTitle());
0280         widDock->setObjectName(wid->objectName());
0281         addDockWidget(Qt::RightDockWidgetArea, widDock);
0282 
0283         m_ui->m_gmToolBoxMenu->addAction(widDock->toggleViewAction());
0284         widDock->setVisible(false);
0285     }
0286 
0287     connect(m_gameController->instantMessagingController(), &InstantMessagingController::unreadChanged, this,
0288             [this]()
0289             {
0290                 auto ctrl= m_gameController->instantMessagingController();
0291                 m_ui->m_showChatAct->setIcon(ctrl->unread() ? QIcon::fromTheme("chaticon_with_message") :
0292                                                               QIcon::fromTheme("chaticon"));
0293             });
0294 
0295     m_ui->m_pasteAct->setEnabled(m_gameController->contentController()->canPaste());
0296 
0297     // Room List
0298     m_roomPanelDockWidget->setAllowedAreas(Qt::AllDockWidgetAreas);
0299     m_roomPanelDockWidget->setWidget(m_roomPanel);
0300     m_roomPanelDockWidget->setWindowTitle(m_roomPanel->windowTitle());
0301     m_roomPanelDockWidget->setObjectName(m_roomPanel->objectName());
0302     m_roomPanelDockWidget->setVisible(false);
0303     addDockWidget(Qt::RightDockWidgetArea, m_roomPanelDockWidget);
0304 
0305     connect(m_ui->m_keyGeneratorAct, &QAction::triggered, this,
0306             []()
0307             {
0308                 KeyGeneratorDialog dialog;
0309                 dialog.exec();
0310             });
0311 
0312     setupUi();
0313     readSettings();
0314     connect(m_ui->m_cleanHistoryAct, &QAction::triggered, m_gameController->contentController(),
0315             &ContentController::clearHistory);
0316 
0317     connect(m_gameController->contentController(), &ContentController::historyChanged, this,
0318             &MainWindow::updateFileHistoryMenu);
0319     updateFileHistoryMenu();
0320 }
0321 
0322 MainWindow::~MainWindow()= default;
0323 
0324 void MainWindow::setupUi()
0325 {
0326     connect(m_ui->m_showChatAct, &QAction::triggered, m_gameController->instantMessagingController(),
0327             &InstantMessagingController::setVisible);
0328     connect(m_gameController->instantMessagingController(), &InstantMessagingController::visibleChanged,
0329             m_ui->m_showChatAct, &QAction::setChecked);
0330 
0331     auto contentCtrl= m_gameController->contentController();
0332 
0333     connect(m_ui->m_pasteAct, &QAction::triggered, contentCtrl, &ContentController::pasteData);
0334 
0335     m_mdiArea.reset(new Workspace(m_ui->m_toolBar, contentCtrl, m_gameController->instantMessagingController()));
0336     setCentralWidget(m_mdiArea.get());
0337 
0338     addDockWidget(Qt::RightDockWidgetArea, m_campaignDock.get());
0339     m_ui->m_menuSubWindows->insertAction(m_ui->m_audioPlayerAct, m_campaignDock->toggleViewAction());
0340 
0341     createNotificationZone();
0342     ///////////////////
0343     // PlayerList
0344     ///////////////////
0345     auto playersListWidget= new PlayersPanel(m_gameController->playerController(), this);
0346 
0347     addDockWidget(Qt::RightDockWidgetArea, playersListWidget);
0348     auto dock= new QDockWidget();
0349     dock->setObjectName("AntagonistTable");
0350     dock->setWindowTitle(tr("Antagonist Table"));
0351     dock->setWidget(m_antagonistWidget.get());
0352     dock->setWindowIcon(QIcon::fromTheme("contact"));
0353     addDockWidget(Qt::RightDockWidgetArea, dock);
0354     setWindowIcon(QIcon::fromTheme("logo"));
0355     m_ui->m_menuSubWindows->insertAction(m_ui->m_characterListAct, playersListWidget->toggleViewAction());
0356     m_ui->m_menuSubWindows->insertAction(m_ui->m_characterListAct, dock->toggleViewAction());
0357     m_ui->m_menuSubWindows->removeAction(m_ui->m_characterListAct);
0358 
0359     ///////////////////
0360     // Audio Player
0361     ///////////////////
0362 #ifndef NULL_PLAYER
0363     // ReceiveEvent::registerNetworkReceiver(NetMsg::MusicCategory, m_audioPlayer.get());
0364     addDockWidget(Qt::RightDockWidgetArea, m_audioPlayer.get());
0365     m_ui->m_menuSubWindows->insertAction(m_ui->m_audioPlayerAct, m_audioPlayer->toggleViewAction());
0366     m_ui->m_menuSubWindows->removeAction(m_ui->m_audioPlayerAct);
0367     connect(m_audioPlayer.get(), &AudioPlayer::changePlayerDirectory, this,
0368             [this]() { m_preferencesDialog->show(PreferencesDialog::PreferenceTab::Player); });
0369 #endif
0370 
0371     m_preferencesDialog= new PreferencesDialog(m_gameController->preferencesController(), this);
0372     linkActionToMenu();
0373 }
0374 
0375 void MainWindow::closeAllMediaContainer()
0376 {
0377     auto content= m_gameController->contentController();
0378     content->clear();
0379 }
0380 
0381 void MainWindow::showTipChecker()
0382 {
0383     auto tip= m_gameController->tipOfDay();
0384     TipOfDayViewer view(tip.title, tip.content, tip.url, this);
0385     view.exec();
0386 
0387     m_preferences->registerValue(QStringLiteral("MainWindow::neverDisplayTips"), view.dontshowAgain());
0388 }
0389 
0390 void MainWindow::closeEvent(QCloseEvent* event)
0391 {
0392     if(mayBeSaved())
0393     {
0394         m_gameController->aboutToClose();
0395         writeSettings();
0396         event->accept();
0397     }
0398     else
0399     {
0400         event->ignore();
0401     }
0402 }
0403 void MainWindow::userNatureChange()
0404 {
0405     updateUi();
0406     updateWindowTitle();
0407 }
0408 
0409 void MainWindow::createNotificationZone()
0410 {
0411     m_dockLogUtil->setObjectName("dockLogUtil");
0412     m_dockLogUtil->setAllowedAreas(Qt::AllDockWidgetAreas);
0413     m_dockLogUtil->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable
0414                                | QDockWidget::DockWidgetFloatable);
0415 
0416     m_ui->m_menuSubWindows->insertAction(m_ui->m_notificationAct, m_dockLogUtil->toggleViewAction());
0417     m_ui->m_menuSubWindows->removeAction(m_ui->m_notificationAct);
0418     addDockWidget(Qt::RightDockWidgetArea, m_dockLogUtil.get());
0419 }
0420 
0421 void MainWindow::linkActionToMenu()
0422 {
0423     connect(m_ui->m_addVectorialMap, &QAction::triggered, this, &MainWindow::newVMap);
0424     m_ui->m_newNoteAction->setData(QVariant::fromValue(Core::ContentType::NOTES));
0425     m_ui->m_newSharedNote->setData(QVariant::fromValue(Core::ContentType::SHAREDNOTE));
0426     m_ui->m_openWebViewAct->setData(QVariant::fromValue(Core::ContentType::WEBVIEW));
0427     m_ui->m_newMindmap->setData(QVariant::fromValue(Core::ContentType::MINDMAP));
0428 
0429     auto callNewMedia= [this]()
0430     {
0431         auto act= qobject_cast<QAction*>(sender());
0432         std::map<QString, QVariant> params;
0433         if(!m_gameController->localIsGM())
0434         {
0435             QUrl dir;
0436             QString filter;
0437             NewFileDialog dial(act->data().value<Core::ContentType>());
0438             if(QDialog::Rejected == dial.exec())
0439                 return;
0440             params.insert({Core::keys::KEY_TYPE, static_cast<int>(dial.type())});
0441             params.insert({Core::keys::KEY_URL, dial.url()});
0442             params.insert({Core::keys::KEY_NAME, dial.name()});
0443         }
0444         else
0445         {
0446             params.insert({Core::keys::KEY_TYPE, act->data()});
0447         }
0448 
0449         m_gameController->newMedia(params);
0450     };
0451 
0452     connect(m_ui->m_newNoteAction, &QAction::triggered, this, callNewMedia);
0453     connect(m_ui->m_newSharedNote, &QAction::triggered, this, callNewMedia);
0454     connect(m_ui->m_newMindmap, &QAction::triggered, this, callNewMedia);
0455 
0456     // open
0457     connect(m_ui->m_openPictureAction, &QAction::triggered, this, &MainWindow::openImage);
0458     connect(m_ui->m_openCharacterSheet, &QAction::triggered, this, &MainWindow::openGenericContent);
0459     connect(m_ui->m_openWebViewAct, &QAction::triggered, this,
0460             [this]()
0461             {
0462                 auto str= QInputDialog::getText(this, tr("Open Webpage"), tr("Webpage url:"));
0463                 if(str.isEmpty())
0464                     return;
0465                 std::map<QString, QVariant> params;
0466                 params.insert({Core::keys::KEY_TYPE, QVariant::fromValue(Core::ContentType::WEBVIEW)});
0467                 QUrl url= QUrl::fromUserInput(str);
0468                 params.insert({Core::keys::KEY_NAME, url.fileName()});
0469                 params.insert({Core::keys::KEY_PATH, str});
0470                 m_gameController->newMedia(params);
0471             });
0472     connect(m_ui->m_openVectorialMap, &QAction::triggered, this,
0473             []()
0474             {
0475                 // TODO open vmap
0476             });
0477     connect(m_ui->m_openCampaignAction, &QAction::triggered, this, &MainWindow::openCampaign);
0478     connect(m_ui->m_openNoteAction, &QAction::triggered, this, &MainWindow::openGenericContent);
0479     connect(m_ui->m_openShareNote, &QAction::triggered, this, &MainWindow::openGenericContent);
0480     connect(m_ui->m_openPdfAct, &QAction::triggered, this, &MainWindow::openGenericContent);
0481     connect(m_ui->m_openMindmap, &QAction::triggered, this, &MainWindow::openGenericContent);
0482 
0483     connect(m_ui->m_importFromCampaign, &QAction::triggered, this,
0484             [this]()
0485             {
0486                 ImportDataFromCampaignDialog dialog(m_gameController->campaignRoot(), this);
0487 
0488                 if(dialog.exec() == QDialog::Accepted)
0489                 {
0490                     /// TODO call controller to import data.
0491                     auto src= dialog.source();
0492                     auto cats= dialog.categoryList();
0493                     auto manager= m_gameController->campaignManager();
0494                     manager->importDataFrom(src, cats);
0495                 }
0496             });
0497 
0498     connect(m_ui->m_shortCutEditorAct, &QAction::triggered, this, &MainWindow::showShortCutEditor);
0499 
0500     m_ui->m_openPictureAction->setData(static_cast<int>(Core::ContentType::PICTURE));
0501     // m_ui->m_openOnlinePictureAction->setData(static_cast<int>(Core::ContentType::ONLINEPICTURE));
0502     m_ui->m_openCharacterSheet->setData(static_cast<int>(Core::ContentType::CHARACTERSHEET));
0503     m_ui->m_openVectorialMap->setData(static_cast<int>(Core::ContentType::VECTORIALMAP));
0504     m_ui->m_openNoteAction->setData(static_cast<int>(Core::ContentType::NOTES));
0505     m_ui->m_openShareNote->setData(static_cast<int>(Core::ContentType::SHAREDNOTE));
0506     m_ui->m_openMindmap->setData(static_cast<int>(Core::ContentType::MINDMAP));
0507     m_ui->m_openPdfAct->setData(static_cast<int>(Core::ContentType::PDF));
0508 
0509     // m_ui->m_recentFileMenu->setVisible(false);
0510     connect(m_ui->m_closeAction, &QAction::triggered, m_mdiArea.get(), &Workspace::closeActiveSub);
0511     connect(m_ui->m_saveCampaignAction, &QAction::triggered, m_gameController, &GameController::save);
0512     connect(m_ui->m_saveCampaignAsAct, &QAction::triggered, this,
0513             [this]()
0514             {
0515                 QString fileName= QFileDialog::getExistingDirectory(this, tr("Open Campaign"),
0516                                                                     m_gameController->campaign()->rootDirectory());
0517                 if(fileName.isEmpty())
0518                     return;
0519                 m_gameController->saveAs(fileName);
0520             });
0521     connect(m_ui->m_preferencesAction, &QAction::triggered, m_preferencesDialog,
0522             [this]() { m_preferencesDialog->show(); });
0523 
0524     // Campaign
0525     auto func= [this]()
0526     {
0527         auto s= qobject_cast<QAction*>(sender());
0528         if(nullptr == s)
0529             return;
0530         auto tab= s->data().value<CampaignProperties::Tab>();
0531         CampaignProperties dialog(m_gameController->campaign(), m_gameController->preferencesController()->themeModel(),
0532                                   this);
0533         dialog.setCurrentTab(tab);
0534         dialog.exec();
0535     };
0536     m_ui->m_diceAliasAct->setData(QVariant::fromValue(CampaignProperties::Tab::Dice));
0537     m_ui->m_statesAct->setData(QVariant::fromValue(CampaignProperties::Tab::States));
0538     m_ui->m_campaignPropertiesAct->setData(QVariant::fromValue(CampaignProperties::Tab::Properties));
0539 
0540     connect(m_ui->m_diceAliasAct, &QAction::triggered, this, func);
0541     connect(m_ui->m_statesAct, &QAction::triggered, this, func);
0542     connect(m_ui->m_campaignPropertiesAct, &QAction::triggered, this, func);
0543 
0544     // Edition
0545     // Windows managing
0546     connect(m_ui->m_cascadeViewAction, &QAction::triggered, m_mdiArea.get(), &Workspace::cascadeSubWindows);
0547     connect(m_ui->m_tabViewAction, &QAction::triggered, m_mdiArea.get(), &Workspace::setTabbedMode);
0548     connect(m_ui->m_tileViewAction, &QAction::triggered, m_mdiArea.get(), &Workspace::tileSubWindows);
0549 
0550     connect(m_ui->m_fullScreenAct, &QAction::triggered, this,
0551             [=](bool enable)
0552             {
0553                 if(enable)
0554                 {
0555                     showFullScreen();
0556                     m_mdiArea->addAction(m_ui->m_fullScreenAct);
0557                     menuBar()->setVisible(false);
0558                     m_mdiArea->setMouseTracking(true);
0559                     setMouseTracking(true);
0560                 }
0561                 else
0562                 {
0563                     showNormal();
0564                     menuBar()->setVisible(true);
0565                     m_mdiArea->setMouseTracking(false);
0566                     setMouseTracking(false);
0567                     m_mdiArea->removeAction(m_ui->m_fullScreenAct);
0568                 }
0569             });
0570 
0571     auto undoStack= m_gameController->undoStack();
0572 
0573     auto redo= undoStack->createRedoAction(this, tr("&Redo"));
0574     auto undo= undoStack->createUndoAction(this, tr("&Undo"));
0575 
0576     redo->setIcon(QIcon::fromTheme("redo"));
0577     undo->setIcon(QIcon::fromTheme("undo"));
0578 
0579     undo->setShortcut(QKeySequence::Undo);
0580     redo->setShortcut(QKeySequence::Redo);
0581     auto acts= m_ui->m_toolBar->actions();
0582     auto firstAct= acts.first();
0583 
0584     m_ui->m_toolBar->insertAction(firstAct, undo);
0585     m_ui->m_toolBar->insertAction(firstAct, redo);
0586 
0587     connect(undoStack, &QUndoStack::cleanChanged, this, [this](bool clean) { setWindowModified(!clean); });
0588 
0589     m_ui->m_editMenu->insertAction(m_ui->m_shortCutEditorAct, redo);
0590     m_ui->m_editMenu->insertAction(redo, undo);
0591     m_ui->m_editMenu->insertSeparator(m_ui->m_shortCutEditorAct);
0592 
0593     // close
0594     connect(m_ui->m_quitAction, &QAction::triggered, this, &MainWindow::close);
0595 
0596     // network
0597     connect(m_ui->m_disconnectAction, &QAction::triggered, m_gameController->networkController(),
0598             &NetworkController::stopConnection);
0599     connect(m_ui->m_connectionLinkAct, &QAction::triggered, this,
0600             [this]()
0601             {
0602                 QString str("rolisteam://%1/%2/%3");
0603                 auto networkCtrl= m_gameController->networkController();
0604 
0605                 auto* clipboard= QGuiApplication::clipboard();
0606                 clipboard->setText(str.arg(networkCtrl->host())
0607                                        .arg(networkCtrl->port())
0608                                        .arg(QString::fromUtf8(networkCtrl->serverPassword().toBase64())));
0609             });
0610     connect(m_ui->m_roomListAct, &QAction::triggered, m_roomPanelDockWidget, &QDockWidget::setVisible);
0611     // Help
0612     connect(m_ui->m_aboutAction, &QAction::triggered, this,
0613             [this]()
0614             {
0615                 AboutRolisteam diag(m_gameController->version(), this);
0616                 diag.exec();
0617             });
0618     connect(m_ui->m_onlineHelpAction, &QAction::triggered, this, &MainWindow::helpOnLine);
0619 
0620     m_ui->m_supportRolisteam->setData(QStringLiteral("https://liberapay.com/Rolisteam/donate"));
0621     m_ui->m_patreon->setData(QStringLiteral("https://www.patreon.com/rolisteam"));
0622     connect(m_ui->m_supportRolisteam, &QAction::triggered, this, &MainWindow::showSupportPage);
0623     connect(m_ui->m_patreon, &QAction::triggered, this, &MainWindow::showSupportPage);
0624 
0625     connect(m_ui->m_manageHistoryAct, &QAction::triggered, this,
0626             [this]()
0627             {
0628                 auto ctrl= m_gameController->contentController();
0629                 HistoryViewerDialog dialog(ctrl->historyModel(), this);
0630                 dialog.exec();
0631                 updateFileHistoryMenu();
0632             });
0633 }
0634 
0635 void MainWindow::showSupportPage()
0636 {
0637     auto act= qobject_cast<QAction*>(sender());
0638     if(nullptr == act)
0639         return;
0640 
0641     QString url= act->data().toString();
0642     if(!QDesktopServices::openUrl(QUrl(url))) //"https://liberapay.com/Rolisteam/donate"
0643     {
0644         QMessageBox msgBox(QMessageBox::Information, tr("Support"),
0645                            tr("The %1 donation page can be found online at :<br> <a "
0646                               "href=\"%2\">%2</a>")
0647                                .arg(m_preferences->value("Application_Name", "rolisteam").toString(), url),
0648                            QMessageBox::Ok);
0649         msgBox.exec();
0650     }
0651 }
0652 
0653 void MainWindow::mouseMoveEvent(QMouseEvent* event)
0654 {
0655     if(isFullScreen())
0656     {
0657         if(qFuzzyCompare(event->windowPos().y(), 0.0))
0658         {
0659             menuBar()->setVisible(true);
0660         }
0661         else if(event->windowPos().y() > 100)
0662         {
0663             menuBar()->setVisible(false);
0664         }
0665     }
0666     QMainWindow::mouseMoveEvent(event);
0667 }
0668 
0669 void MainWindow::makeVisible(bool value)
0670 {
0671     if(value)
0672         m_preferences->value("FullScreenAtStarting", true).toBool() ? showMaximized() : show();
0673     else
0674         QMainWindow::setVisible(value);
0675 }
0676 
0677 void MainWindow::newVMap()
0678 {
0679     MapWizzardDialog mapWizzard(m_mdiArea.get());
0680     if(mapWizzard.exec())
0681     {
0682         std::map<QString, QVariant> params;
0683         params.insert({QStringLiteral("name"), mapWizzard.name()});
0684         params.insert({QStringLiteral("permission"), mapWizzard.permission()});
0685         params.insert({QStringLiteral("bgcolor"), mapWizzard.backgroundColor()});
0686         params.insert({QStringLiteral("gridSize"), mapWizzard.gridSize()});
0687         params.insert({QStringLiteral("gridPattern"), QVariant::fromValue(mapWizzard.pattern())});
0688         params.insert({QStringLiteral("gridColor"), mapWizzard.gridColor()});
0689         params.insert({QStringLiteral("visibility"), mapWizzard.visibility()});
0690         params.insert({QStringLiteral("scale"), mapWizzard.scale()});
0691         params.insert({QStringLiteral("unit"), mapWizzard.unit()});
0692         params.insert({Core::keys::KEY_TYPE, QVariant::fromValue(Core::ContentType::VECTORIALMAP)});
0693         m_gameController->newMedia(params);
0694     }
0695 }
0696 
0697 bool MainWindow::mayBeSaved(bool connectionLoss)
0698 {
0699     QMessageBox msgBox(this);
0700     QAbstractButton* saveBtn= msgBox.addButton(QMessageBox::Save);
0701     QAbstractButton* quitBtn= msgBox.addButton(tr("Quit"), QMessageBox::RejectRole);
0702     Qt::WindowFlags flags= msgBox.windowFlags();
0703     msgBox.setWindowFlags(flags ^ Qt::WindowSystemMenuHint);
0704 
0705     QString message;
0706     QString msg= m_preferences->value("Application_Name", "rolisteam").toString();
0707     if(connectionLoss)
0708     {
0709         message= tr("Connection has been lost. %1 will be close").arg(msg);
0710         msgBox.setIcon(QMessageBox::Critical);
0711         msgBox.setWindowTitle(tr("Connection lost"));
0712     }
0713     else
0714     {
0715         msgBox.setIcon(QMessageBox::Information);
0716         msgBox.addButton(QMessageBox::Cancel);
0717         msgBox.setWindowTitle(tr("Quit %1 ").arg(msg));
0718     }
0719     return true;
0720 }
0721 
0722 void MainWindow::openCampaign()
0723 {
0724     QString fileName
0725         = QFileDialog::getExistingDirectory(this, tr("Open Campaign"), m_gameController->campaign()->rootDirectory());
0726 
0727     m_gameController->setCampaignRoot(fileName);
0728 }
0729 
0730 bool MainWindow::saveStory(bool saveAs)
0731 {
0732     /*auto contentCtrl= m_gameController->contentController();
0733     if(contentCtrl->sessionPath().isEmpty() || saveAs)
0734     {
0735         QString fileName= QFileDialog::getSaveFileName(
0736             this, tr("Save Scenario as"), m_preferences->value("SessionDirectory", QDir::homePath()).toString(),
0737             tr("Scenarios (*.sce)"));
0738         if(fileName.isNull())
0739         {
0740             return false;
0741         }
0742         if(!fileName.endsWith(".sce"))
0743         {
0744             fileName.append(QStringLiteral(".sce"));
0745         }
0746         contentCtrl->setSessionPath(fileName);
0747     }
0748     QFileInfo info(contentCtrl->sessionPath());
0749     m_preferences->registerValue("SessionDirectory", info.absolutePath());
0750     contentCtrl->saveSession();
0751     updateWindowTitle();*/
0752     return true;
0753 }
0754 ////////////////////////////////////////////////////
0755 // Save data
0756 ////////////////////////////////////////////////////
0757 /*void MainWindow::saveCurrentMedia()
0758 {
0759 
0760     auto content= m_gameController->contentController();
0761     auto mediaId= content->currentMediaId();
0762     auto media= content->media(mediaId);
0763 
0764     if(!media)
0765         return;
0766 
0767     QString dest= media->path();
0768     QUrl url(dest);
0769     if(qobject_cast<QAction*>(sender()) == m_ui->m_saveAsAction || dest.isEmpty() || !url.isLocalFile())
0770     {
0771         auto type= media->contentType();
0772         auto filter= CleverURI::getFilterForType(type);
0773         auto key= m_preferences->value(, QDir::homePath()).toString();
0774         QFileDialog::getSaveFileName(this, tr("Save %1").arg(media->name()), key, filter);
0775     }
0776 }*/
0777 
0778 void MainWindow::stopReconnection()
0779 {
0780     m_ui->m_changeProfileAct->setEnabled(true);
0781     m_ui->m_disconnectAction->setEnabled(false);
0782 }
0783 
0784 void MainWindow::setUpNetworkConnection()
0785 {
0786     connect(m_gameController, &GameController::localIsGMChanged, this, &MainWindow::userNatureChange);
0787     auto networkCtrl= m_gameController->networkController();
0788     connect(networkCtrl, &NetworkController::downloadingData, m_dockLogUtil.get(), &NotificationZone::receiveData);
0789 }
0790 
0791 void MainWindow::helpOnLine()
0792 {
0793     if(!QDesktopServices::openUrl(QUrl("http://doc.rolisteam.org/")))
0794     {
0795         QMessageBox* msgBox= new QMessageBox(QMessageBox::Information, tr("Help"),
0796                                              tr("Documentation of %1 can be found online at :<br> <a "
0797                                                 "href=\"http://doc.rolisteam.org\">http://doc.rolisteam.org/</a>")
0798                                                  .arg(m_preferences->value("Application_Name", "rolisteam").toString()),
0799                                              QMessageBox::Ok);
0800         msgBox->exec();
0801     }
0802 }
0803 void MainWindow::updateUi()
0804 {
0805     auto isGM= m_gameController->localIsGM();
0806     m_ui->m_addVectorialMap->setEnabled(isGM);
0807     m_ui->m_openVectorialMap->setEnabled(isGM);
0808     m_ui->m_openCharacterSheet->setEnabled(isGM);
0809 
0810     m_ui->m_openCampaignAction->setEnabled(isGM);
0811     m_ui->m_closeAction->setEnabled(isGM);
0812     m_ui->m_saveCampaignAction->setEnabled(isGM);
0813     m_ui->m_saveCampaignAsAct->setEnabled(isGM);
0814     m_ui->m_connectionLinkAct->setVisible(m_gameController->networkController()->hosting());
0815     m_ui->m_changeProfileAct->setEnabled(false);
0816     m_ui->m_disconnectAction->setEnabled(true);
0817 
0818     if(isGM)
0819     {
0820         auto act= m_ui->m_historyMenu->menuAction();
0821         m_ui->menubar->removeAction(act);
0822     }
0823     else
0824     {
0825         auto act= m_ui->m_campaignMenu->menuAction();
0826         m_ui->menubar->removeAction(act);
0827     }
0828 }
0829 void MainWindow::showUpdateNotification()
0830 {
0831     QMessageBox::information(this, tr("Update Notification"),
0832                              tr("The %1 version has been released. "
0833                                 "Please take a look at <a "
0834                                 "href=\"http://www.rolisteam.org/download\">Download page</a> for "
0835                                 "more "
0836                                 "information")
0837                                  .arg(m_gameController->remoteVersion()));
0838 }
0839 
0840 void MainWindow::notifyAboutAddedPlayer(Player* player) const
0841 {
0842     m_gameController->addFeatureLog(tr("%1 just joins the game.").arg(player->name()), logns::cat::network);
0843     if(player->userVersion().compare(m_gameController->version()) != 0)
0844     {
0845         m_gameController->addErrorLog(
0846             tr("%1 has not the right version: %2.").arg(player->name(), player->userVersion()), logns::cat::network);
0847     }
0848 }
0849 
0850 void MainWindow::notifyAboutDeletedPlayer(Player* player) const
0851 {
0852     m_gameController->addFeatureLog(tr("%1 just leaves the game.").arg(player->name()), logns::cat::network);
0853 }
0854 
0855 void MainWindow::readSettings()
0856 {
0857     QSettings settings(Core::settings::KEY_ROLISTEAM,
0858                        QString(Core::settings::KEY_PREF_DIR).arg(m_gameController->version()));
0859 
0860     restoreState(settings.value(Core::settings::KEY_WINDOW_STATE).toByteArray());
0861     bool maxi= settings.value(Core::settings::KEY_MAXIMIZED, false).toBool();
0862     m_ui->m_mediaTitleAct->setChecked(settings.value(Core::settings::KEY_MEDIA_TITLE, false).toBool());
0863     if(!maxi)
0864     {
0865         restoreGeometry(settings.value(Core::settings::KEY_GEOMETRY).toByteArray());
0866     }
0867 
0868     m_antagonistWidget->setFullMode(settings.value(Core::settings::KEY_ANTA_FULLMODE, false).toBool());
0869     m_antagonistWidget->setMinimalMode(settings.value(Core::settings::KEY_ANTA_MINIMODE, true).toBool());
0870 
0871     auto size= settings.beginReadArray(Core::settings::KEY_ANTA_ARRAY);
0872     auto& acts= m_antagonistWidget->columnsActions();
0873     for(int i= 0; i < size; ++i)
0874     {
0875         settings.setArrayIndex(i);
0876         auto& act= acts[i];
0877         act->setChecked(settings.value(Core::settings::KEY_ANTA_COL, true).toBool());
0878     }
0879 
0880     // read recent scenario
0881     auto ctrl= m_gameController->contentController();
0882     SettingsHelper::readHistoryModel(ctrl->historyModel());
0883     m_dockLogUtil->initSetting();
0884 }
0885 void MainWindow::writeSettings()
0886 {
0887     QSettings settings(Core::settings::KEY_ROLISTEAM,
0888                        QString(Core::settings::KEY_PREF_DIR).arg(m_gameController->version()));
0889     settings.setValue(Core::settings::KEY_GEOMETRY, saveGeometry());
0890     settings.setValue(Core::settings::KEY_WINDOW_STATE, saveState());
0891     settings.setValue(Core::settings::KEY_MAXIMIZED, isMaximized());
0892     settings.setValue(Core::settings::KEY_MEDIA_TITLE, m_ui->m_mediaTitleAct->isChecked());
0893 
0894     settings.setValue(Core::settings::KEY_ANTA_MINIMODE, m_antagonistWidget->minimalMode());
0895     settings.setValue(Core::settings::KEY_ANTA_FULLMODE, m_antagonistWidget->fullMode());
0896 
0897     settings.beginWriteArray(Core::settings::KEY_ANTA_ARRAY);
0898     auto& acts= m_antagonistWidget->columnsActions();
0899     int i= 0;
0900     for(auto const& act : acts)
0901     {
0902         settings.setArrayIndex(i);
0903         i++;
0904         settings.setValue(Core::settings::KEY_ANTA_COL, act->isChecked());
0905     }
0906     settings.endArray();
0907 
0908     auto ctrl= m_gameController->contentController();
0909     if(!m_gameController->localIsGM())
0910         SettingsHelper::writeHistoryModel(ctrl->historyModel());
0911     for(auto& gmtool : m_gmToolBoxList)
0912     {
0913         gmtool->writeSettings();
0914     }
0915 }
0916 void MainWindow::parseCommandLineArguments(const QStringList& list)
0917 {
0918     if(list.isEmpty())
0919         return;
0920     QCommandLineParser parser;
0921     parser.addHelpOption();
0922     parser.addVersionOption();
0923 
0924     QCommandLineOption port(QStringList() << "p"
0925                                           << "port",
0926                             tr("Set rolisteam to use <port> for the connection"), "port");
0927     QCommandLineOption hostname(QStringList() << "s"
0928                                               << "server",
0929                                 tr("Set rolisteam to connect to <server>."), "server");
0930     QCommandLineOption role(QStringList() << "r"
0931                                           << "role",
0932                             tr("Define the <role>: gm or pc"), "role");
0933     QCommandLineOption reset(QStringList() << "reset-settings",
0934                              tr("Erase the settings and use the default parameters"));
0935     QCommandLineOption user(QStringList() << "u"
0936                                           << "user",
0937                             tr("Define the <username>"), "username");
0938     QCommandLineOption websecurity(QStringList() << "w"
0939                                                  << "disable-web-security",
0940                                    tr("Remove limit to PDF file size"));
0941     QCommandLineOption translation(QStringList() << "t"
0942                                                  << "translation",
0943                                    QObject::tr("path to the translation file: <translationfile>"), "translationfile");
0944     QCommandLineOption url(QStringList() << "l"
0945                                          << "link",
0946                            QObject::tr("Define URL to connect to server: <url>"), "url");
0947 
0948     parser.addOption(port);
0949     parser.addOption(hostname);
0950     parser.addOption(role);
0951     parser.addOption(reset);
0952     parser.addOption(user);
0953     parser.addOption(translation);
0954     parser.addOption(websecurity);
0955     parser.addOption(url);
0956 
0957     parser.process(list);
0958 
0959     bool hasPort= parser.isSet(port);
0960     bool hasHostname= parser.isSet(hostname);
0961     bool hasRole= parser.isSet(role);
0962     bool hasUser= parser.isSet(user);
0963     bool hasUrl= parser.isSet(url);
0964     bool hasResetSetting= parser.isSet(reset);
0965 
0966     QString portValue;
0967     QString hostnameValue;
0968     QString roleValue;
0969     QString username;
0970     QString urlString;
0971     QString passwordValue;
0972     if(hasPort)
0973     {
0974         portValue= parser.value(port);
0975     }
0976     if(hasHostname)
0977     {
0978         hostnameValue= parser.value(hostname);
0979     }
0980     if(hasRole)
0981     {
0982         roleValue= parser.value(role);
0983     }
0984     if(hasUser)
0985     {
0986         username= parser.value(user);
0987     }
0988     if(hasResetSetting)
0989     {
0990         QSettings settings("rolisteam", QString("rolisteam_%1/preferences").arg(m_gameController->version()));
0991         settings.clear();
0992     }
0993     if(hasUrl)
0994     {
0995         urlString= parser.value(url);
0996         // auto list = urlString.split("/",QString::SkipEmptyParts);
0997         QRegularExpression ex("^rolisteam://(.*)/([0-9]+)/(.*)$");
0998         QRegularExpressionMatch match= ex.match(urlString);
0999         // rolisteam://IP/port/password
1000         // if(list.size() ==  4)
1001         if(match.hasMatch())
1002         {
1003             hostnameValue= match.captured(1);
1004             portValue= match.captured(2);
1005             passwordValue= match.captured(3);
1006             QByteArray pass= QByteArray::fromBase64(passwordValue.toUtf8());
1007             m_commandLineProfile.reset(new CommandLineProfile({hostnameValue, portValue.toInt(), pass}));
1008         }
1009     }
1010 }
1011 
1012 void MainWindow::cleanUpData()
1013 {
1014     m_gameController->clear();
1015 
1016     ChannelListPanel* roomPanel= qobject_cast<ChannelListPanel*>(m_roomPanelDockWidget->widget());
1017     if(nullptr != roomPanel)
1018     {
1019         roomPanel->cleanUp();
1020     }
1021 }
1022 
1023 void MainWindow::postConnection()
1024 {
1025     m_ui->m_changeProfileAct->setEnabled(false);
1026     m_ui->m_disconnectAction->setEnabled(true);
1027 
1028     setUpNetworkConnection();
1029     updateWindowTitle();
1030     updateUi();
1031 }
1032 
1033 void MainWindow::openGenericContent()
1034 {
1035     QAction* action= static_cast<QAction*>(sender());
1036     Core::ContentType type= static_cast<Core::ContentType>(action->data().toInt());
1037 
1038     QString folder= m_preferences->value(Core::preferences::KEY_OPEN_DIRECTORY, QDir::homePath()).toString();
1039     QString title= tr("Open %1").arg(helper::utils::typeToString(type));
1040     QStringList filepath= QFileDialog::getOpenFileNames(this, title, folder, helper::utils::filterForType(type, false));
1041     QStringList list= filepath;
1042 
1043     for(auto const& path : list)
1044     {
1045         m_gameController->openMedia(
1046             {{Core::keys::KEY_URL, QUrl::fromUserInput(path)},
1047              {Core::keys::KEY_TYPE, QVariant::fromValue(type)},
1048              {Core::keys::KEY_NAME, utils::IOHelper::shortNameFromPath(path)},
1049              {Core::keys::KEY_OWNERID, m_gameController->playerController()->localPlayer()->uuid()}});
1050     }
1051 }
1052 
1053 void MainWindow::openImage()
1054 {
1055     ImageSelectorController ctrl(false, ImageSelectorController::All);
1056     ImageSelectorDialog dialog(&ctrl, this);
1057     if(QDialog::Accepted != dialog.exec())
1058         return;
1059 
1060     std::map<QString, QVariant> args(
1061         {{Core::keys::KEY_NAME, ctrl.title()},
1062          {Core::keys::KEY_URL, ctrl.address()},
1063          {Core::keys::KEY_OWNERID, m_gameController->playerController()->localPlayer()->uuid()},
1064          {Core::keys::KEY_TYPE, QVariant::fromValue(Core::ContentType::PICTURE)},
1065          {Core::keys::KEY_DATA, ctrl.finalImageData()}});
1066 
1067     m_gameController->openMedia(args);
1068 }
1069 
1070 void MainWindow::updateFileHistoryMenu()
1071 {
1072     auto const& ctrl= m_gameController->contentController();
1073     auto const& model= ctrl->historyModel();
1074     auto const& data= model->data();
1075 
1076     m_ui->m_historyMenu->clear();
1077     m_ui->m_historyMenu->addAction(m_ui->m_manageHistoryAct);
1078     m_ui->m_historyMenu->addAction(m_ui->m_cleanHistoryAct);
1079     m_ui->m_historyMenu->addSeparator();
1080     std::for_each(std::begin(data), std::end(data),
1081                   [this](const history::LinkInfo& info)
1082                   {
1083                       auto action= new QAction(info.displayName, this);
1084                       action->setData(info.id);
1085                       action->setIcon(QIcon(helper::utils::typeToIconPath(info.type)));
1086                       m_ui->m_historyMenu->addAction(action);
1087                       connect(action, &QAction::triggered, this, &MainWindow::openFileFromHistory);
1088                   });
1089 
1090     SettingsHelper::writeHistoryModel(ctrl->historyModel());
1091 }
1092 
1093 void MainWindow::openFileFromHistory()
1094 {
1095     auto act= qobject_cast<QAction*>(sender());
1096     auto id= act->data().toString();
1097     auto const& ctrl= m_gameController->contentController();
1098     auto const& model= ctrl->historyModel();
1099     auto info= model->idToPath(id);
1100     if(!info.url.isEmpty())
1101     {
1102         std::map<QString, QVariant> map{{Core::keys::KEY_URL, info.url},
1103                                         {Core::keys::KEY_NAME, info.displayName},
1104                                         {Core::keys::KEY_TYPE, QVariant::fromValue(info.type)}};
1105         if(info.type == Core::ContentType::PICTURE)
1106         {
1107             ImageSelectorController* ctrl= new ImageSelectorController(false, ImageSelectorController::All);
1108             connect(ctrl, &ImageSelectorController::imageDataChanged, this,
1109                     [this, ctrl, map]()
1110                     {
1111                         std::map<QString, QVariant> map2(map);
1112                         auto data= ctrl->finalImageData();
1113                         map2.insert({Core::keys::KEY_DATA, data});
1114                         m_gameController->openMedia(map2);
1115                         ctrl->deleteLater();
1116                     });
1117             ctrl->downloadImageFrom(info.url);
1118         }
1119         else
1120         {
1121             m_gameController->openMedia(map);
1122         }
1123     }
1124 }
1125 
1126 void MainWindow::updateWindowTitle()
1127 {
1128     auto networkCtrl= m_gameController->networkController();
1129 
1130     auto const connectionStatus= m_gameController->connected() ? tr("Connected") : tr("Not Connected");
1131     auto const networkStatus= networkCtrl->hosting() ? tr("Server") : tr("Client");
1132 
1133     if(networkCtrl->isGM())
1134     {
1135         auto camp= m_gameController->campaign();
1136         setWindowTitle(QStringLiteral("%6[*] - v%2 - %3 - %4 - %5 - %1")
1137                            .arg(m_preferences->value("applicationName", "Rolisteam").toString(),
1138                                 m_gameController->version(), connectionStatus, networkStatus, tr("GM"), camp->name()));
1139     }
1140     else
1141     {
1142         setWindowTitle(QStringLiteral("%1 - v%2[*] - %3 - %4 - %5")
1143                            .arg(m_preferences->value("applicationName", "Rolisteam").toString(),
1144                                 m_gameController->version(), connectionStatus, networkStatus, tr("Player")));
1145     }
1146 
1147     setWindowIcon(QIcon::fromTheme("500-symbole"));
1148 }
1149 
1150 void MainWindow::dragEnterEvent(QDragEnterEvent* event)
1151 {
1152     if(event->mimeData()->hasUrls() || event->mimeData()->hasImage())
1153     {
1154         event->acceptProposedAction();
1155     }
1156     QMainWindow::dragEnterEvent(event);
1157 }
1158 
1159 void MainWindow::dropEvent(QDropEvent* event)
1160 {
1161     const QMimeData* data= event->mimeData();
1162     if(!data->hasUrls() && !data->hasImage())
1163         return;
1164 
1165     qDebug() << "data has url and image" << data->hasImage() << data->hasUrls();
1166 
1167     if(data->hasImage())
1168     {
1169         auto contentCtrl= m_gameController->contentController();
1170         auto img= qvariant_cast<QImage>(data->imageData());
1171         qDebug() << img.isNull() << img;
1172 
1173         auto name= tr("Unknown");
1174         if(data->hasText())
1175         {
1176             name= data->text();
1177         }
1178 
1179         contentCtrl->openMedia(
1180             {{Core::keys::KEY_TYPE, QVariant::fromValue(Core::ContentType::PICTURE)},
1181              {Core::keys::KEY_DATA, IOHelper::imageToData(img)},
1182              {Core::keys::KEY_NAME, name},
1183              {Core::keys::KEY_OWNERID, m_gameController->playerController()->localPlayer()->uuid()}});
1184     }
1185     else if(data->hasUrls())
1186     {
1187         QList<QUrl> list= data->urls();
1188         auto contentCtrl= m_gameController->contentController();
1189         for(const auto& url : list)
1190         {
1191             auto path= url.toLocalFile();
1192             if(!path.isEmpty()) // local file
1193             {
1194 
1195                 Core::ContentType type= helper::utils::extensionToContentType(path);
1196                 if(type == Core::ContentType::UNKNOWN)
1197                     continue;
1198                 qCInfo(WidgetClient)
1199                     << QStringLiteral("MainWindow: dropEvent for %1").arg(helper::utils::typeToString(type));
1200                 contentCtrl->openMedia(
1201                     {{Core::keys::KEY_URL, url},
1202                      {Core::keys::KEY_TYPE, QVariant::fromValue(type)},
1203                      {Core::keys::KEY_NAME, utils::IOHelper::shortNameFromPath(path)},
1204                      {Core::keys::KEY_OWNERID, m_gameController->playerController()->localPlayer()->uuid()}});
1205             }
1206             else // remote
1207             {
1208                 auto urltext= url.toString(QUrl::None);
1209                 Core::ContentType type= helper::utils::extensionToContentType(urltext);
1210                 if(type != Core::ContentType::PICTURE)
1211                     continue;
1212 
1213 #ifdef HAVE_QT_NETWORK
1214                 auto downloader= new NetworkDownloader(url);
1215                 connect(
1216                     downloader, &NetworkDownloader::finished, this,
1217                     [this, contentCtrl, type, url, urltext, downloader](const QByteArray& data)
1218                     {
1219                         contentCtrl->openMedia(
1220                             {{Core::keys::KEY_URL, urltext},
1221                              {Core::keys::KEY_PATH, urltext},
1222                              {Core::keys::KEY_TYPE, QVariant::fromValue(type)},
1223                              {Core::keys::KEY_DATA, data},
1224                              {Core::keys::KEY_NAME, utils::IOHelper::shortNameFromPath(url.fileName())},
1225                              {Core::keys::KEY_OWNERID, m_gameController->playerController()->localPlayer()->uuid()}});
1226                         downloader->deleteLater();
1227                     });
1228                 downloader->download();
1229 #endif
1230             }
1231         }
1232     }
1233     event->acceptProposedAction();
1234 }
1235 
1236 void MainWindow::showShortCutEditor()
1237 {
1238     ShortcutVisitor visitor;
1239     visitor.registerWidget(this, "mainwindow", true);
1240 
1241     ShortCutEditorDialog dialog;
1242     dialog.setModel(visitor.getModel());
1243     dialog.exec();
1244 }
1245 
1246 void MainWindow::openImageAs(const QPixmap& pix, Core::ContentType type)
1247 {
1248     auto viewer= qobject_cast<MediaContainer*>(sender());
1249     QString title(tr("Export from %1"));
1250     QString sourceName= tr("unknown");
1251     if(nullptr != viewer)
1252     {
1253         // sourceName= viewer->getUriName();
1254     }
1255 
1256     // MediaContainer* destination= nullptr;
1257     if(type == Core::ContentType::VECTORIALMAP)
1258     {
1259         /* auto media= newDocument(type, false);
1260          auto vmapFrame= dynamic_cast<VMapFrame*>(media);*/
1261         // if(vmapFrame)
1262         {
1263             /*auto vmap= vmapFrame->getMap();
1264             vmap->addImageItem(pix.toImage());
1265             destination= media;*/
1266         }
1267     }
1268     else if(type == Core::ContentType::PICTURE)
1269     {
1270         /* auto img= new Image(m_mdiArea);
1271          auto imgPix= pix.toImage();
1272          img->setImage(imgPix);
1273          destination= img;*/
1274     }
1275     // if(destination)
1276     // destination->setUriName(title.arg(sourceName));
1277 
1278     // destination->setRemote(false);
1279     // destination->setCleverUri(new CleverURI(sourceName, "", type));
1280     // addMediaToMdiArea(destination, true);
1281 }
1282 void MainWindow::focusInEvent(QFocusEvent* event)
1283 {
1284     QMainWindow::focusInEvent(event);
1285     if(m_isOut)
1286     {
1287         m_gameController->addSearchLog(QStringLiteral("Rolisteam gets focus."), logns::cat::usability);
1288         m_isOut= false;
1289     }
1290 }
1291 void MainWindow::focusOutEvent(QFocusEvent* event)
1292 {
1293     QMainWindow::focusOutEvent(event);
1294     if(!isActiveWindow())
1295     {
1296         m_gameController->addSearchLog(QStringLiteral("User gives focus to another windows."), logns::cat::usability);
1297         m_isOut= true;
1298     }
1299 }