Crossfire Resources Editor
CREResourcesWindow.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 2022 the Crossfire Development Team
5  *
6  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
7  * welcome to redistribute it under certain conditions. For details, please
8  * see COPYING and LICENSE.
9  *
10  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
11  */
12 
13 #include <Qt>
14 #include <QtWidgets>
15 #include <QScriptValue>
16 #include <stdexcept>
17 
18 #include "CREResourcesWindow.h"
19 #include "CREPixmap.h"
20 
21 #include "CREFilterDialog.h"
22 #include "CREFilterDefinition.h"
23 
24 #include "CRESettings.h"
25 
26 #include "CREReportDisplay.h"
27 
35 #include "recipes/RecipePanel.h"
36 #include "CREMapPanel.h"
37 #include "regions/RegionPanel.h"
38 #include "CREQuestPanel.h"
39 #include "CREMessagePanel.h"
42 #include "faces/FacesetsPanel.h"
44 
46 
48 #include "MessageFile.h"
50 
51 #include "CREScriptEngine.h"
52 
53 #include "random_maps/RandomMap.h"
55 
56 #include "global.h"
57 #include "recipe.h"
58 #include "assets.h"
59 #include "AssetsManager.h"
60 
61 #include "MessageManager.h"
62 #include "ResourcesManager.h"
63 #include "assets/AssetModel.h"
64 #include "faces/FacePanel.h"
65 #include "sounds/SoundFilesPanel.h"
66 #include "sounds/SoundFilePanel.h"
67 #include "sounds/GameSoundsPanel.h"
68 #include "sounds/GameSoundPanel.h"
69 #include "QuickFilterDialog.h"
70 
71 CREResourcesWindow::CREResourcesWindow(CREMapInformationManager* store, MessageManager* messages, ResourcesManager* resources, ScriptFileManager* scripts, AssetModel *model, const QModelIndex &root, QWidget* parent) : QWidget(parent)
72 {
73  QApplication::setOverrideCursor(Qt::WaitCursor);
74  setWindowTitle(model->data(root, Qt::DisplayRole).toString());
75 
76  Q_ASSERT(store);
77  myStore = store;
78  Q_ASSERT(messages);
79  myMessages = messages;
80  Q_ASSERT(resources);
81  myResources = resources;
82  Q_ASSERT(scripts);
83  myScripts = scripts;
84  myModel = new ScriptFilterAssetModel(model, &myEngine, this);
85  myTreeRoot = root;
86 
87  setAttribute(Qt::WA_DeleteOnClose);
88 
89  QVBoxLayout* layout = new QVBoxLayout(this);
90 
91  myFiltersMenu = new QMenu(this);
92  QHBoxLayout* buttons = new QHBoxLayout();
93  myFilterButton = new QPushButton(tr("Filter..."), this);
94  myFilterButton->setMenu(myFiltersMenu);
95  buttons->addWidget(myFilterButton);
96 
97  auto exportCsv = new QPushButton(tr("Export as CSV"), this);
98  buttons->addWidget(exportCsv);
99  connect(exportCsv, SIGNAL(clicked()), this, SLOT(onExportAsCsv()));
100  if (!root.isValid() || !static_cast<const AssetWrapper *>(root.internalPointer())->canExportAsCsv())
101  {
102  exportCsv->setEnabled(false);
103  exportCsv->setToolTip(tr("CSV export is not available for this type of ressources"));
104  }
105  else
106  {
107  exportCsv->setToolTip(tr("Export visible ressources to CSV file"));
108  }
109 
110  layout->addLayout(buttons);
111 
112  auto splitter = new QSplitter(this);
113  layout->addWidget(splitter);
114 
115  myTree = new QTreeView(this);
116  myTree->setModel(myModel);
117  myTree->setRootIndex(myModel->mapFromSource(root));
118  splitter->addWidget(myTree);
119  myTree->setIconSize(QSize(32, 32));
120  myTree->collapseAll();
121  myTree->expand(myModel->mapFromSource(root));
122  myTree->setSelectionMode(QAbstractItemView::SingleSelection);
123  myTree->setDragEnabled(true);
124  myTree->setDropIndicatorShown(true);
125  myTree->setDragDropMode(QAbstractItemView::DragDrop);
126  myTree->setContextMenuPolicy(Qt::CustomContextMenu);
127  connect(myTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(treeCustomMenu(const QPoint&)));
128  connect(myTree->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(currentRowChanged(const QModelIndex&, const QModelIndex&)));
129 
130  QWidget* w = new QWidget(splitter);
131  myStackedPanels = new QStackedLayout(w);
132  splitter->addWidget(w);
133 
134  /* dummy panel to display for empty items */
135  AssetWrapperPanel* dummy = new AssetWrapperPanel(this);
136  dummy->addLabel(tr("No details available."), nullptr);
137  addPanel("empty", dummy);
138  myStackedPanels->setCurrentWidget(dummy);
139  myCurrentPanel = dummy;
140 
141  connect(&myFiltersMapper, SIGNAL(mapped(QObject*)), this, SLOT(onFilterChange(QObject*)));
142  updateFilters();
143 
144  addPanel("Archetype", new ArchetypePanel(model, this));
145  addPanel("Face", new CREFacePanel(this, model, myResources, myStore));
146  addPanel("Animation", new AnimationPanel(this, model));
147  addPanel("Artifact", new ArtifactPanel(this, myResources));
148  addPanel("ArtifactList", new ArtifactListPanel(this));
149  addPanel("Recipe", new RecipePanel(this));
150  addPanel("Treasure", new TreasurePanel(this));
151  addPanel("TreasureList", new CRETreasurePanel(model, this));
152  addPanel("Faceset", new FacesetsPanel(this, myResources->licenseManager()));
153  addPanel("Quest", new CREQuestPanel(model, this));
154  addPanel("QuestStep", new QuestStepPanel(myMessages, this));
155  addPanel("GeneralMessage", new CREGeneralMessagePanel(this));
156  addPanel("Region", new RegionPanel(this));
157  addPanel("Map", new CREMapPanel(myScripts, this));
158  addPanel("Script", new CREScriptPanel(model, this));
159  addPanel("Message", new CREMessagePanel(myMessages, this));
160  addPanel("RandomMap", new CRERandomMapPanel(this));
161  addPanel("AttackMessage", new AttackMessagePanel(this));
162  addPanel("SoundFiles", new SoundFilesPanel(this));
163  addPanel("SoundFile", new SoundFilePanel(this, model));
164  addPanel("GameSounds", new GameSoundsPanel(this));
165  addPanel("GameSound", new GameSoundPanel(this));
166 
167  splitter->setSizes({5000, 5000});
168 
169  connect(myStore, &CREMapInformationManager::finished, [&] () {
170  connect(myModel, &QAbstractItemModel::rowsInserted, [this](const QModelIndex &parent, int first, int /*last*/) {
171  myTree->expand(parent);
172  auto child = myModel->index(first, 0, parent);
173  myTree->selectionModel()->select(child, QItemSelectionModel::ClearAndSelect);
174  myTree->setCurrentIndex(child);
175  });
176  });
177 
178  QApplication::restoreOverrideCursor();
179 }
180 
182 {
183  qDeleteAll(myPanels);
184 }
185 
186 void CREResourcesWindow::currentRowChanged(const QModelIndex &current, const QModelIndex &)
187 {
188  if (!current.isValid()) {
189  myCurrentPanel = nullptr;
190  return;
191  }
192 
193  auto rc = myModel->mapToSource(current);
194  AssetWrapper *item = reinterpret_cast<AssetWrapper *>(rc.internalPointer());
195  if (!item) {
196  return;
197  }
198 
199  auto newPanel = myPanels[item->displayPanelName()];
200  if (!newPanel) {
201 // printf("no panel for %s\n", qPrintable(item->getPanelName()));
202  return;
203  }
204 
205  newPanel->setAsset(item);
206 
207  if (myCurrentPanel != newPanel) {
208  myStackedPanels->setCurrentWidget(newPanel);
209  myCurrentPanel = newPanel;
210  }
211 }
212 
214 {
215  panel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
216  myPanels[name] = panel;
217  myStackedPanels->addWidget(panel);
218 }
219 
221 {
222  CREFilterDialog dlg;
223  if (dlg.exec() != QDialog::Accepted)
224  return;
225 
226  /* sending this signal will ultimately call our own updateFilters() */
227  emit filtersModified();
228 }
229 
231 {
232  CRESettings settings;
233  settings.loadFilters(myFilters);
234 
235  myFiltersMenu->clear();
236 
237  QAction* clear = new QAction(tr("(none)"), this);
238  connect(clear, SIGNAL(triggered()), this, SLOT(clearFilter()));
239  myFiltersMenu->addAction(clear);
240 
241  if (myFilters.filters().size() > 0)
242  {
243 
244  foreach(CREFilterDefinition* filter, myFilters.filters())
245  {
246  QAction* a = new QAction(filter->name(), this);
247  myFiltersMenu->addAction(a);
248  myFiltersMapper.setMapping(a, filter);
249  connect(a, SIGNAL(triggered()), &myFiltersMapper, SLOT(map()));
250  }
251  }
252  myFiltersMenu->addSeparator();
253 
254  QAction* quick = new QAction(tr("Quick filter..."), this);
255  connect(quick, SIGNAL(triggered()), this, SLOT(onQuickFilter()));
256  myFiltersMenu->addAction(quick);
257  QAction* dialog = new QAction(tr("Filters definition..."), this);
258  connect(dialog, SIGNAL(triggered()), this, SLOT(onFilter()));
259  myFiltersMenu->addAction(dialog);
260 
261  clearFilter();
262 }
263 
264 void CREResourcesWindow::onFilterChange(QObject* object) {
265  CREFilterDefinition* filter = qobject_cast<CREFilterDefinition*>(object);
266  if (filter == NULL)
267  return;
268  setFilter(filter->filter(), filter->name());
269 }
270 
272  QuickFilterDialog dlg(this, myModel->filter());
273  if (dlg.exec() != QDialog::Accepted) {
274  return;
275  }
276  setFilter(dlg.filter(), dlg.filter());
277 }
278 
280  setFilter(QString(), QString());
281 }
282 
283 void CREResourcesWindow::setFilter(const QString &filter, const QString &name) {
284  myModel->setFilter(filter);
285  myFilterButton->setText(filter.isEmpty() ? tr("Filter...") : tr("Filter: %1").arg(name));
286  auto root = myModel->mapFromSource(myTreeRoot);
287  if (!myTreeRoot.isValid() || root.isValid()) {
288  if (myTree->model() == nullptr) {
289  myTree->setModel(myModel);
290  connect(myTree->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(currentRowChanged(const QModelIndex&, const QModelIndex&)));
291  }
292  myTree->setRootIndex(root);
293  } else {
294  myTree->setModel(nullptr);
295  }
296 }
297 
299 {
300  assert(myTreeRoot.isValid());
301  auto root = static_cast<const AssetWrapper *>(myTreeRoot.internalPointer());
302  assert(root->canExportAsCsv());
303 
304  auto destination = QFileDialog::getSaveFileName(this, tr("Export as CSV..."), QString(), tr("CSV file (*.csv);;All files (*.*)"));
305  if (destination.isEmpty())
306  return;
307 
308  QString contents;
309  root->fillCsvHeader(contents);
310 
311  int count = myModel->rowCount(myTree->rootIndex());
312 
313  for (int i = 0; i < count; i++) {
314  auto idx = myModel->index(i, 0, myTree->rootIndex());
315  if (!idx.isValid()) {
316  continue;
317  }
318  idx = myModel->mapToSource(idx);
319  auto w = static_cast<const AssetWrapper *>(idx.internalPointer());
320  root->exportAsCSV(w, contents);
321  }
322 
323  QFile file(destination);
324  if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
325  QMessageBox::critical(this, tr("Error writing CSV file %1").arg(destination), tr("Unable to write CSV file %1!").arg(destination));
326  return;
327  }
328 
329  file.write(contents.toLocal8Bit());
330  QMessageBox::information(this, tr("Export complete"), tr("Ressources correctly exported to CSV file %1.").arg(destination));
331 }
332 
333 void CREResourcesWindow::treeCustomMenu(const QPoint & pos)
334 {
335  QMenu menu;
336 
337  QModelIndex index = myModel->mapToSource(myTree->indexAt(pos));
338  if (index.isValid() && index.internalPointer()) {
339  AssetWrapper *item = reinterpret_cast<AssetWrapper *>(index.internalPointer());
340  if (item) {
341  item->fillMenu(&menu);
342  }
343  }
344 
345  if (menu.actions().size() == 0)
346  return;
347  menu.exec(myTree->mapToGlobal(pos) + QPoint(5, 5));
348 }
AssetWrapperPanel
Base class for a panel displaying information about an asset.
Definition: AssetWrapperPanel.h:29
ScriptFileManager
Manage scripts for items.
Definition: ScriptFileManager.h:26
QuestStepPanel
Display details of a quest step.
Definition: CREQuestPanel.h:29
CREFilterDefinition::name
const QString & name() const
Definition: CREFilterDefinition.cpp:29
CREMapInformationManager::finished
void finished()
CREMessagePanel.h
ArchetypePanel
Display and allow edition of archetypes.
Definition: ArchetypePanel.h:27
ResourcesManager::licenseManager
LicenseManager * licenseManager()
Definition: ResourcesManager.h:155
TreasureListPanel.h
CREResourcesWindow::myTree
QTreeView * myTree
Definition: CREResourcesWindow.h:48
CREResourcesWindow::onQuickFilter
void onQuickFilter()
Definition: CREResourcesWindow.cpp:271
CREResourcesWindow::myModel
ScriptFilterAssetModel * myModel
Definition: CREResourcesWindow.h:47
CRESettings::loadFilters
void loadFilters(CREFilterDefinitionManager &filters)
Definition: CRESettings.cpp:60
FacesetsPanel.h
ScriptFileManager.h
MessageManager.h
ResourcesManager
Class managing all assets, tracking in which file they are, which are modified, and such.
Definition: ResourcesManager.h:80
SoundFilesPanel.h
CREResourcesWindow::CREResourcesWindow
CREResourcesWindow(CREMapInformationManager *store, MessageManager *messages, ResourcesManager *resources, ScriptFileManager *scripts, AssetModel *model, const QModelIndex &root, QWidget *parent)
Definition: CREResourcesWindow.cpp:71
CREFilterDialog
Definition: CREFilterDialog.h:24
ScriptFilterAssetModel::setFilter
void setFilter(const QString &filter)
Definition: AssetModel.cpp:263
CREFilterDialog.h
CREResourcesWindow::myFilters
CREFilterDefinitionManager myFilters
Definition: CREResourcesWindow.h:60
CREResourcesWindow::myMessages
MessageManager * myMessages
Definition: CREResourcesWindow.h:54
CRERandomMapPanel
Display details about a random map.
Definition: RandomMapPanel.h:25
CREQuestPanel
Details of a quest.
Definition: CREQuestPanel.h:39
RandomMap.h
CREResourcesWindow::myStore
CREMapInformationManager * myStore
Definition: CREResourcesWindow.h:53
CREFilterDefinition::filter
const QString & filter() const
Definition: CREFilterDefinition.cpp:39
AnimationPanel
Display details about an animation.
Definition: AnimationPanel.h:29
AssetWrapperPanel::addLabel
QLabel * addLabel(const QString &label, const char *property, bool wrapText=false)
Definition: AssetWrapperPanel.cpp:68
CRETreasurePanel
Display and allow edition of a treasure list.
Definition: TreasureListPanel.h:28
RecipePanel.h
CREResourcesWindow::onFilter
void onFilter()
Definition: CREResourcesWindow.cpp:220
CREResourcesWindow::onFilterChange
void onFilterChange(QObject *object)
Definition: CREResourcesWindow.cpp:264
CREFilterDefinition.h
GameSoundsPanel
Definition: GameSoundsPanel.h:19
CREScriptEngine.h
RandomMapPanel.h
AssetModel::data
virtual QVariant data(const QModelIndex &index, int role) const override
Definition: AssetModel.cpp:70
CREResourcesWindow::myFiltersMenu
QMenu * myFiltersMenu
Definition: CREResourcesWindow.h:58
TreasurePanel
Display and allow edition of a treasure in a treasure list.
Definition: TreasurePanel.h:26
GameSoundPanel.h
AssetWrapper
Base class for all assets that can be displayed or edited by CRE.
Definition: AssetWrapper.h:25
QuickFilterDialog::filter
QString filter() const
Get the input filter.
Definition: QuickFilterDialog.cpp:33
RegionPanel
Display details of a region.
Definition: RegionPanel.h:22
CREResourcesWindow::myFiltersMapper
QSignalMapper myFiltersMapper
Definition: CREResourcesWindow.h:59
CRESettings.h
ScriptFilterAssetModel
Proxy model filtering based on user-written filter, used in the main resource view.
Definition: AssetModel.h:78
CREResourcesWindow::clearFilter
void clearFilter()
Definition: CREResourcesWindow.cpp:279
ArtifactPanel.h
ArchetypePanel.h
GeneralMessagePanel.h
AssetModel
Qt model representing all items in CRE, with the exception of experience.
Definition: AssetModel.h:29
CREReportDisplay.h
CREResourcesWindow::myEngine
CREScriptEngine myEngine
Definition: CREResourcesWindow.h:61
FacePanel.h
AssetWrapper::fillMenu
virtual void fillMenu(QMenu *)
Definition: AssetWrapper.h:66
AssetModel.h
CREResourcesWindow::addPanel
void addPanel(QString name, AssetWrapperPanel *panel)
Definition: CREResourcesWindow.cpp:213
CREQuestPanel.h
MessageManager
Manage NPC dialogs.
Definition: MessageManager.h:25
CREResourcesWindow::setFilter
void setFilter(const QString &filter, const QString &name)
Definition: CREResourcesWindow.cpp:283
ArtifactPanel
Display details about an artifact definition.
Definition: ArtifactPanel.h:29
CREResourcesWindow::filtersModified
void filtersModified()
RecipePanel
Display details of a crafting recipe.
Definition: RecipePanel.h:27
CREResourcesWindow::treeCustomMenu
void treeCustomMenu(const QPoint &pos)
Definition: CREResourcesWindow.cpp:333
CREResourcesWindow::myFilterButton
QPushButton * myFilterButton
Definition: CREResourcesWindow.h:57
CREGeneralMessagePanel
Display and allow edition of general messages.
Definition: GeneralMessagePanel.h:21
CREResourcesWindow::onExportAsCsv
void onExportAsCsv()
Definition: CREResourcesWindow.cpp:298
RegionPanel.h
CREMapInformationManager.h
ResourcesManager.h
CREResourcesWindow::myStackedPanels
QStackedLayout * myStackedPanels
Definition: CREResourcesWindow.h:52
FacesetsPanel
Display details about a faceset.
Definition: FacesetsPanel.h:28
ArtifactListPanel.h
ScriptFilterAssetModel::filter
const QString & filter() const
Definition: AssetModel.h:83
AssetWrapperPanel.h
GameSoundsPanel.h
CREResourcesWindow::myScripts
ScriptFileManager * myScripts
Definition: CREResourcesWindow.h:56
SoundFilePanel.h
CREResourcesWindow::myResources
ResourcesManager * myResources
Definition: CREResourcesWindow.h:55
MessageFile.h
TreasurePanel.h
ArtifactListPanel
Definition: ArtifactListPanel.h:23
SoundFilePanel
Definition: SoundFilePanel.h:22
CREResourcesWindow::myCurrentPanel
AssetWrapperPanel * myCurrentPanel
Definition: CREResourcesWindow.h:50
CRESettings
Definition: CRESettings.h:20
AttackMessagePanel.h
AttackMessagePanel
Display attack messages for a single attack type.
Definition: AttackMessagePanel.h:26
CREResourcesWindow::myTreeRoot
QModelIndex myTreeRoot
Definition: CREResourcesWindow.h:49
AnimationPanel.h
AssetWrapper::exportAsCSV
virtual void exportAsCSV(const AssetWrapper *, QString &) const
Definition: AssetWrapper.h:70
ArtifactWrapper.h
CREResourcesWindow::myPanels
QHash< QString, QPointer< AssetWrapperPanel > > myPanels
Definition: CREResourcesWindow.h:51
QuickFilterDialog
Dialog for the "quick filter", using a field completer.
Definition: QuickFilterDialog.h:23
AssetWrapper::canExportAsCsv
virtual bool canExportAsCsv() const
Definition: AssetWrapper.h:68
CREFilterDefinition
Definition: CREFilterDefinition.h:19
CREMessagePanel
Display information about a NPC message file, and allow edition.
Definition: CREMessagePanel.h:31
ScriptFilePanel.h
CREMapPanel
Display details about a map.
Definition: CREMapPanel.h:26
CREMapInformationManager
Definition: CREMapInformationManager.h:27
CREResourcesWindow.h
GameSoundPanel
Definition: GameSoundPanel.h:19
CREMapPanel.h
AssetWrapper::displayPanelName
virtual QString displayPanelName() const
Definition: AssetWrapper.h:45
QuickFilterDialog.h
CREPixmap.h
CREFacePanel
Display details about a face.
Definition: FacePanel.h:30
CREScriptPanel
Display details of a Python script.
Definition: ScriptFilePanel.h:23
CREResourcesWindow::updateFilters
void updateFilters()
Definition: CREResourcesWindow.cpp:230
SoundFilesPanel
Definition: SoundFilesPanel.h:18
CREFilterDefinitionManager::filters
QList< CREFilterDefinition * > & filters()
Definition: CREFilterDefinitionManager.cpp:41
CREResourcesWindow::currentRowChanged
void currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
Definition: CREResourcesWindow.cpp:186
CREResourcesWindow::~CREResourcesWindow
virtual ~CREResourcesWindow()
Definition: CREResourcesWindow.cpp:181