Crossfire Server, Trunk
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);
80  Q_ASSERT(resources);
81  myResources = resources;
82  Q_ASSERT(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  connect(myModel, &QAbstractItemModel::rowsInserted, [this](const QModelIndex &parent, int /*first*/, int /*last*/) {
130  myTree->expand(parent);
131  });
132 
133  QWidget* w = new QWidget(splitter);
134  myStackedPanels = new QStackedLayout(w);
135  splitter->addWidget(w);
136 
137  /* dummy panel to display for empty items */
138  AssetWrapperPanel* dummy = new AssetWrapperPanel(this);
139  dummy->addLabel(tr("No details available."), nullptr);
140  addPanel("empty", dummy);
141  myStackedPanels->setCurrentWidget(dummy);
142  myCurrentPanel = dummy;
143 
144  connect(&myFiltersMapper, SIGNAL(mapped(QObject*)), this, SLOT(onFilterChange(QObject*)));
145  updateFilters();
146 
147  addPanel("Archetype", new ArchetypePanel(model, this));
148  addPanel("Face", new CREFacePanel(this, model, myResources, myStore));
149  addPanel("Animation", new AnimationPanel(this, model));
150  addPanel("Artifact", new ArtifactPanel(this, myResources));
151  addPanel("ArtifactList", new ArtifactListPanel(this));
152  addPanel("Recipe", new RecipePanel(this));
153  addPanel("Treasure", new TreasurePanel(this));
154  addPanel("TreasureList", new CRETreasurePanel(model, this));
155  addPanel("Faceset", new FacesetsPanel(this, myResources->licenseManager()));
156  addPanel("Quest", new CREQuestPanel(model, this));
157  addPanel("QuestStep", new QuestStepPanel(myMessages, this));
158  addPanel("GeneralMessage", new CREGeneralMessagePanel(this));
159  addPanel("Region", new RegionPanel(this));
160  addPanel("Map", new CREMapPanel(myScripts, this));
161  addPanel("Script", new CREScriptPanel(model, this));
162  addPanel("Message", new CREMessagePanel(myMessages, this));
163  addPanel("RandomMap", new CRERandomMapPanel(this));
164  addPanel("AttackMessage", new AttackMessagePanel(this));
165  addPanel("SoundFiles", new SoundFilesPanel(this));
166  addPanel("SoundFile", new SoundFilePanel(this, model));
167  addPanel("GameSounds", new GameSoundsPanel(this));
168  addPanel("GameSound", new GameSoundPanel(this));
169 
170  splitter->setSizes({5000, 5000});
171 
172  QApplication::restoreOverrideCursor();
173 }
174 
176 {
177  qDeleteAll(myPanels);
178 }
179 
180 void CREResourcesWindow::currentRowChanged(const QModelIndex &current, const QModelIndex &)
181 {
182  if (!current.isValid()) {
183  myCurrentPanel = nullptr;
184  return;
185  }
186 
187  auto rc = myModel->mapToSource(current);
188  AssetWrapper *item = reinterpret_cast<AssetWrapper *>(rc.internalPointer());
189  if (!item) {
190  return;
191  }
192 
193  auto newPanel = myPanels[item->displayPanelName()];
194  if (!newPanel) {
195 // printf("no panel for %s\n", qPrintable(item->getPanelName()));
196  return;
197  }
198 
199  newPanel->setAsset(item);
200 
201  if (myCurrentPanel != newPanel) {
202  myStackedPanels->setCurrentWidget(newPanel);
203  myCurrentPanel = newPanel;
204  }
205 }
206 
208 {
209  panel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
210  myPanels[name] = panel;
211  myStackedPanels->addWidget(panel);
212 }
213 
215 {
216  CREFilterDialog dlg;
217  if (dlg.exec() != QDialog::Accepted)
218  return;
219 
220  /* sending this signal will ultimately call our own updateFilters() */
221  emit filtersModified();
222 }
223 
225 {
227  settings.loadFilters(myFilters);
228 
229  myFiltersMenu->clear();
230 
231  QAction* clear = new QAction(tr("(none)"), this);
232  connect(clear, SIGNAL(triggered()), this, SLOT(clearFilter()));
233  myFiltersMenu->addAction(clear);
234 
235  if (myFilters.filters().size() > 0)
236  {
237 
239  {
240  QAction* a = new QAction(filter->name(), this);
241  myFiltersMenu->addAction(a);
242  myFiltersMapper.setMapping(a, filter);
243  connect(a, SIGNAL(triggered()), &myFiltersMapper, SLOT(map()));
244  }
245  }
246  myFiltersMenu->addSeparator();
247 
248  QAction* quick = new QAction(tr("Quick filter..."), this);
249  connect(quick, SIGNAL(triggered()), this, SLOT(onQuickFilter()));
250  myFiltersMenu->addAction(quick);
251  QAction* dialog = new QAction(tr("Filters definition..."), this);
252  connect(dialog, SIGNAL(triggered()), this, SLOT(onFilter()));
253  myFiltersMenu->addAction(dialog);
254 
255  clearFilter();
256 }
257 
258 void CREResourcesWindow::onFilterChange(QObject* object) {
259  CREFilterDefinition* filter = qobject_cast<CREFilterDefinition*>(object);
260  if (filter == NULL)
261  return;
262  setFilter(filter->filter(), filter->name());
263 }
264 
266  QuickFilterDialog dlg(this, myModel->filter());
267  if (dlg.exec() != QDialog::Accepted) {
268  return;
269  }
270  setFilter(dlg.filter(), dlg.filter());
271 }
272 
274  setFilter(QString(), QString());
275 }
276 
277 void CREResourcesWindow::setFilter(const QString &filter, const QString &name) {
279  myFilterButton->setText(filter.isEmpty() ? tr("Filter...") : tr("Filter: %1").arg(name));
280  auto root = myModel->mapFromSource(myTreeRoot);
281  if (!myTreeRoot.isValid() || root.isValid()) {
282  if (myTree->model() == nullptr) {
283  myTree->setModel(myModel);
284  connect(myTree->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(currentRowChanged(const QModelIndex&, const QModelIndex&)));
285  }
286  myTree->setRootIndex(root);
287  } else {
288  myTree->setModel(nullptr);
289  }
290 }
291 
293 {
294  assert(myTreeRoot.isValid());
295  auto root = static_cast<const AssetWrapper *>(myTreeRoot.internalPointer());
296  assert(root->canExportAsCsv());
297 
298  auto destination = QFileDialog::getSaveFileName(this, tr("Export as CSV..."), QString(), tr("CSV file (*.csv);;All files (*.*)"));
299  if (destination.isEmpty())
300  return;
301 
302  QString contents;
303  root->fillCsvHeader(contents);
304 
305  int count = myModel->rowCount(myTree->rootIndex());
306 
307  for (int i = 0; i < count; i++) {
308  auto idx = myModel->index(i, 0, myTree->rootIndex());
309  if (!idx.isValid()) {
310  continue;
311  }
312  idx = myModel->mapToSource(idx);
313  auto w = static_cast<const AssetWrapper *>(idx.internalPointer());
314  root->exportAsCSV(w, contents);
315  }
316 
317  QFile file(destination);
318  if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
319  QMessageBox::critical(this, tr("Error writing CSV file %1").arg(destination), tr("Unable to write CSV file %1!").arg(destination));
320  return;
321  }
322 
323  file.write(contents.toLocal8Bit());
324  QMessageBox::information(this, tr("Export complete"), tr("Ressources correctly exported to CSV file %1.").arg(destination));
325 }
326 
327 void CREResourcesWindow::treeCustomMenu(const QPoint & pos)
328 {
329  QMenu menu;
330 
331  QModelIndex index = myModel->mapToSource(myTree->indexAt(pos));
332  if (index.isValid() && index.internalPointer()) {
333  AssetWrapper *item = reinterpret_cast<AssetWrapper *>(index.internalPointer());
334  if (item) {
335  item->fillMenu(&menu);
336  }
337  }
338 
339  if (menu.actions().size() == 0)
340  return;
341  menu.exec(myTree->mapToGlobal(pos) + QPoint(5, 5));
342 }
343 
345 {
346  auto name = QInputDialog::getText(this, "Create new quest", "New quest code").toStdString();
347  if (name.empty()) {
348  return;
349  }
350  if (getManager()->quests()->find(name)) {
351  QMessageBox::critical(this, "Quest already exists", tr("Quest %1 already exists!").arg(name.data()));
352  return;
353  }
354 
355  auto quest = quest_create(name.data());
356  quest->face = getManager()->faces()->get("quest_generic.111");
358 }
359 
361 {
362 #if 0
363  MessageFile* file = new MessageFile("<new file>");
364  file->setModified();
365  myMessages->messages().append(file);
366 #endif
367 }
AssetWrapperPanel
Definition: AssetWrapperPanel.h:29
ScriptFileManager
Definition: ScriptFileManager.h:26
QuestStepPanel
Definition: CREQuestPanel.h:29
global.h
CREMessagePanel.h
CREResourcesWindow::addMessage
void addMessage(bool)
Definition: CREResourcesWindow.cpp:360
settings
struct Settings settings
Definition: init.cpp:139
ArchetypePanel
Definition: ArchetypePanel.h:27
ResourcesManager::licenseManager
LicenseManager * licenseManager()
Definition: ResourcesManager.h:153
layout
Definition: main.cpp:84
TreasureListPanel.h
CREResourcesWindow::myTree
QTreeView * myTree
Definition: CREResourcesWindow.h:48
CREResourcesWindow::onQuickFilter
void onQuickFilter()
Definition: CREResourcesWindow.cpp:265
dialogc.dialog
def dialog
Definition: dialogc.py:79
CREResourcesWindow::myModel
ScriptFilterAssetModel * myModel
Definition: CREResourcesWindow.h:47
python_init.scripts
scripts
Definition: python_init.py:11
FacesetsPanel.h
ScriptFileManager.h
MessageManager.h
ResourcesManager
Definition: ResourcesManager.h:80
AssetsManager.h
SoundFilesPanel.h
filter
Definition: filter.py:1
disinfect.a
a
Definition: disinfect.py:13
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:256
CREFilterDialog.h
recipe.h
root
static char root[500]
Definition: mapper.cpp:304
mad_mage_user.file
file
Definition: mad_mage_user.py:15
CREResourcesWindow::myFilters
CREFilterDefinitionManager myFilters
Definition: CREResourcesWindow.h:60
MessageManager::messages
QList< MessageFile * > & messages()
Definition: MessageManager.cpp:51
CREResourcesWindow::myMessages
MessageManager * myMessages
Definition: CREResourcesWindow.h:54
CRERandomMapPanel
Definition: RandomMapPanel.h:25
CREQuestPanel
Definition: CREQuestPanel.h:39
RandomMap.h
CREResourcesWindow::myStore
CREMapInformationManager * myStore
Definition: CREResourcesWindow.h:53
MessageFile
Definition: MessageFile.h:68
AnimationPanel
Definition: AnimationPanel.h:29
getManager
AssetsManager * getManager()
Definition: assets.cpp:305
AssetWrapperPanel::addLabel
QLabel * addLabel(const QString &label, const char *property, bool wrapText=false)
Definition: AssetWrapperPanel.cpp:68
CRETreasurePanel
Definition: TreasureListPanel.h:28
RecipePanel.h
CREResourcesWindow::onFilter
void onFilter()
Definition: CREResourcesWindow.cpp:214
CREResourcesWindow::onFilterChange
void onFilterChange(QObject *object)
Definition: CREResourcesWindow.cpp:258
quest
Definition: quest.py:1
disinfect.map
map
Definition: disinfect.py:4
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
Definition: TreasurePanel.h:26
GameSoundPanel.h
AssetsCollection::define
T * define(const Key &name, T *asset)
Definition: AssetsCollection.h:120
AssetWrapper
Definition: AssetWrapper.h:25
QuickFilterDialog::filter
QString filter() const
Definition: QuickFilterDialog.cpp:33
RegionPanel
Definition: RegionPanel.h:22
CREResourcesWindow::myFiltersMapper
QSignalMapper myFiltersMapper
Definition: CREResourcesWindow.h:59
CRESettings.h
ScriptFilterAssetModel
Definition: AssetModel.h:78
CREResourcesWindow::clearFilter
void clearFilter()
Definition: CREResourcesWindow.cpp:273
AssetsManager::quests
Quests * quests()
Definition: AssetsManager.h:71
AssetsManager::faces
Faces * faces()
Definition: AssetsManager.h:39
ArtifactPanel.h
ArchetypePanel.h
GeneralMessagePanel.h
AssetModel
Definition: AssetModel.h:29
CREReportDisplay.h
CREResourcesWindow::myEngine
CREScriptEngine myEngine
Definition: CREResourcesWindow.h:61
FacePanel.h
AssetModel.h
disinfect.count
int count
Definition: disinfect.py:7
CREResourcesWindow::addPanel
void addPanel(QString name, AssetWrapperPanel *panel)
Definition: CREResourcesWindow.cpp:207
CREQuestPanel.h
MessageManager
Definition: MessageManager.h:25
CREResourcesWindow::setFilter
void setFilter(const QString &filter, const QString &name)
Definition: CREResourcesWindow.cpp:277
AssetsCollection::get
T * get(const Key &name)
Definition: AssetsCollection.h:89
ArtifactPanel
Definition: ArtifactPanel.h:29
CREResourcesWindow::filtersModified
void filtersModified()
RecipePanel
Definition: RecipePanel.h:27
CREResourcesWindow::treeCustomMenu
void treeCustomMenu(const QPoint &pos)
Definition: CREResourcesWindow.cpp:327
CREResourcesWindow::myFilterButton
QPushButton * myFilterButton
Definition: CREResourcesWindow.h:57
CREResourcesWindow::addQuest
void addQuest(bool)
Definition: CREResourcesWindow.cpp:344
CREGeneralMessagePanel
Definition: GeneralMessagePanel.h:21
CREResourcesWindow::onExportAsCsv
void onExportAsCsv()
Definition: CREResourcesWindow.cpp:292
RegionPanel.h
CREMapInformationManager.h
ResourcesManager.h
CREResourcesWindow::myStackedPanels
QStackedLayout * myStackedPanels
Definition: CREResourcesWindow.h:52
navar-midane_apply.messages
list messages
Definition: navar-midane_apply.py:8
FacesetsPanel
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
item
Definition: item.py:1
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
Definition: AttackMessagePanel.h:26
assets.h
CREResourcesWindow::myTreeRoot
QModelIndex myTreeRoot
Definition: CREResourcesWindow.h:49
npc_dialog.index
int index
Definition: npc_dialog.py:109
AnimationPanel.h
ArtifactWrapper.h
CREResourcesWindow::myPanels
QHash< QString, QPointer< AssetWrapperPanel > > myPanels
Definition: CREResourcesWindow.h:51
QuickFilterDialog
Definition: QuickFilterDialog.h:23
AssetWrapper::canExportAsCsv
virtual bool canExportAsCsv() const
Definition: AssetWrapper.h:66
givecontents.contents
bool contents
DIALOGCHECK MINARGS 1 MAXARGS 1
Definition: givecontents.py:19
CREFilterDefinition
Definition: CREFilterDefinition.h:19
CREMessagePanel
Definition: CREMessagePanel.h:31
ScriptFilePanel.h
CREMapPanel
Definition: CREMapPanel.h:26
connect
Definition: connect.py:1
CREMapInformationManager
Definition: CREMapInformationManager.h:27
CREResourcesWindow.h
replace.current
current
Definition: replace.py:64
GameSoundPanel
Definition: GameSoundPanel.h:19
quests
static struct_quest ** quests
Definition: mapper.cpp:892
CREMapPanel.h
QuickFilterDialog.h
CREPixmap.h
quest_create
quest_definition * quest_create(const char *name)
Definition: Quests.cpp:63
CREFacePanel
Definition: FacePanel.h:30
CREScriptPanel
Definition: ScriptFilePanel.h:23
CREResourcesWindow::updateFilters
void updateFilters()
Definition: CREResourcesWindow.cpp:224
SoundFilesPanel
Definition: SoundFilesPanel.h:18
give.name
name
Definition: give.py:27
CREFilterDefinitionManager::filters
QList< CREFilterDefinition * > & filters()
Definition: CREFilterDefinitionManager.cpp:41
CREResourcesWindow::currentRowChanged
void currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
Definition: CREResourcesWindow.cpp:180
CREResourcesWindow::~CREResourcesWindow
virtual ~CREResourcesWindow()
Definition: CREResourcesWindow.cpp:175