mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
Integrate EditorCallback into mapeditor
EditorCallback being set up with std::unique_ptr stored in MapController.
This commit is contained in:
@@ -486,8 +486,12 @@ set(lib_MAIN_HEADERS
|
||||
callback/GameRandomizer.h
|
||||
callback/MapInfoCallback.h
|
||||
callback/EditorCallback.h
|
||||
<<<<<<< HEAD
|
||||
|
||||
campaign/CampaignBonus.h
|
||||
=======
|
||||
|
||||
>>>>>>> eeff61da6 (Integrate EditorCallback into mapeditor)
|
||||
campaign/CampaignConstants.h
|
||||
campaign/CampaignHandler.h
|
||||
campaign/CampaignRegions.h
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
/*
|
||||
* EditorCallback.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "EditorCallback.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
@@ -5,6 +14,8 @@
|
||||
#define THROW_EDITOR_UNSUPPORTED \
|
||||
throw std::runtime_error(std::string("EditorCallback: ") + __func__ + " is not available in map editor")
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
const CMap * EditorCallback::getMapConstPtr() const
|
||||
{
|
||||
if(!map)
|
||||
@@ -136,11 +147,6 @@ bool EditorCallback::isVisibleFor(const CGObjectInstance *obj, PlayerColor playe
|
||||
THROW_EDITOR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
void EditorCallback::pickAllowedArtsSet(std::vector<ArtifactID> &, vstd::RNG &)
|
||||
{
|
||||
THROW_EDITOR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
scripting::Pool * EditorCallback::getGlobalContextPool() const
|
||||
{
|
||||
@@ -187,3 +193,5 @@ int EditorCallback::getResource(PlayerColor, GameResID) const
|
||||
{
|
||||
THROW_EDITOR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
#include "../lib/callback/MapInfoCallback.h"
|
||||
|
||||
class EditorCallback : public MapInfoCallback
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class DLL_LINKAGE EditorCallback : public MapInfoCallback
|
||||
{
|
||||
protected:
|
||||
const CMap * getMapConstPtr() const override;
|
||||
@@ -49,10 +51,8 @@ public:
|
||||
bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player) const override;
|
||||
bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const override;
|
||||
|
||||
bool isVisibleFor(int3 pos, PlayerColor player) const;
|
||||
bool isVisibleFor(const CGObjectInstance * obj, PlayerColor player) const;
|
||||
|
||||
void pickAllowedArtsSet(std::vector<ArtifactID> & out, vstd::RNG & rand) override;
|
||||
bool isVisibleFor(int3 pos, PlayerColor player) const override;
|
||||
bool isVisibleFor(const CGObjectInstance * obj, PlayerColor player) const override;
|
||||
|
||||
// Optional scripting
|
||||
#if SCRIPTING_ENABLED
|
||||
@@ -69,6 +69,10 @@ public:
|
||||
EPlayerStatus getPlayerStatus(PlayerColor player, bool verbose) const override;
|
||||
int getResource(PlayerColor player, GameResID which) const override;
|
||||
|
||||
virtual ~EditorCallback() = default;
|
||||
|
||||
private:
|
||||
const CMap * map;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
@@ -1,3 +1,12 @@
|
||||
/*
|
||||
* MapInfoCallback.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "MapInfoCallback.h"
|
||||
#include "../constants/EntityIdentifiers.h"
|
||||
|
||||
@@ -21,6 +21,7 @@ struct DLL_LINKAGE ArtSlotInfo : public GameCallbackHolder
|
||||
ArtifactInstanceID artifactID;
|
||||
bool locked = false; //if locked, then artifact points to the combined artifact
|
||||
|
||||
ArtSlotInfo() = delete;
|
||||
explicit ArtSlotInfo(IGameInfoCallback * cb);
|
||||
ArtSlotInfo(const CArtifactInstance * artifact, bool locked);
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ class CDefaultObjectTypeHandler : public AObjectTypeHandler
|
||||
|
||||
std::shared_ptr<CGObjectInstance> create(IGameInfoCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl) const final
|
||||
{
|
||||
assert(cb);
|
||||
|
||||
auto result = createObject(cb);
|
||||
|
||||
preInitObject(result.get());
|
||||
@@ -46,6 +48,8 @@ protected:
|
||||
virtual void randomizeObject(ObjectType * object, IGameRandomizer & gameRandomizer) const {}
|
||||
virtual std::shared_ptr<ObjectType> createObject(IGameInfoCallback * cb) const
|
||||
{
|
||||
assert(cb);
|
||||
|
||||
return std::make_shared<ObjectType>(cb);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,7 +48,6 @@ set(editor_SRCS
|
||||
campaigneditor/scenarioproperties.cpp
|
||||
campaigneditor/startingbonus.cpp
|
||||
campaigneditor/campaignview.cpp
|
||||
EditorCallback.cpp
|
||||
)
|
||||
|
||||
set(editor_HEADERS
|
||||
@@ -102,7 +101,6 @@ set(editor_HEADERS
|
||||
campaigneditor/scenarioproperties.h
|
||||
campaigneditor/startingbonus.h
|
||||
campaigneditor/campaignview.h
|
||||
EditorCallback.h
|
||||
)
|
||||
|
||||
set(editor_FORMS
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "helper.h"
|
||||
#include "mapcontroller.h"
|
||||
#include "EditorCallback.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CMemoryBuffer.h"
|
||||
@@ -24,7 +23,7 @@
|
||||
#include "../lib/mapping/MapFormatJson.h"
|
||||
#include "../lib/modding/ModIncompatibility.h"
|
||||
|
||||
std::unique_ptr<CMap> Helper::openMapInternal(const QString & filenameSelect)
|
||||
std::unique_ptr<CMap> Helper::openMapInternal(const QString & filenameSelect, IGameInfoCallback * ecb)
|
||||
{
|
||||
QFileInfo fi(filenameSelect);
|
||||
std::string fname = fi.fileName().toStdString();
|
||||
@@ -50,12 +49,8 @@ std::unique_ptr<CMap> Helper::openMapInternal(const QString & filenameSelect)
|
||||
|
||||
if(!modList.empty())
|
||||
throw ModIncompatibility(modList);
|
||||
|
||||
auto cb = std::make_shared<EditorCallback>(nullptr);
|
||||
|
||||
std::unique_ptr<CMap> map = mapService.loadMap(resId, cb.get());
|
||||
cb->setMap(map.get());
|
||||
return map;
|
||||
|
||||
return mapService.loadMap(resId, ecb);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Corrupted map");
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
|
||||
class CMap;
|
||||
class CampaignState;
|
||||
class IGameInfoCallback;
|
||||
|
||||
namespace Helper
|
||||
{
|
||||
std::unique_ptr<CMap> openMapInternal(const QString &);
|
||||
std::unique_ptr<CMap> openMapInternal(const QString &, IGameInfoCallback *);
|
||||
std::shared_ptr<CampaignState> openCampaignInternal(const QString &);
|
||||
void saveCampaign(std::shared_ptr<CampaignState> campaignState, const QString & filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,19 +43,18 @@ ArtifactWidget::ArtifactWidget(CArtifactFittingSet & fittingSet, QWidget * paren
|
||||
}
|
||||
ui->possiblePositions->addItem(QString::fromStdString(NArtifactPosition::backpack), ArtifactPosition::BACKPACK_START);
|
||||
fillArtifacts();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ArtifactWidget::fillArtifacts()
|
||||
{
|
||||
ui->artifact->clear();
|
||||
auto currentSlot = ui->possiblePositions->currentData().toInt();
|
||||
for (const auto& art : LIBRARY->arth->getDefaultAllowed())
|
||||
for (const auto & art : LIBRARY->arth->getDefaultAllowed())
|
||||
{
|
||||
auto artifact = art.toArtifact();
|
||||
// forbid spell scroll for now as require special handling
|
||||
if (artifact->canBePutAt(&fittingSet, currentSlot, true) && artifact->getId() != ArtifactID::SPELL_SCROLL) {
|
||||
if (artifact->canBePutAt(&fittingSet, currentSlot, true) && artifact->getId() != ArtifactID::SPELL_SCROLL)
|
||||
{
|
||||
ui->artifact->addItem(QString::fromStdString(artifact->getNameTranslated()), QVariant::fromValue(artifact->getIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ HeroArtifactsWidget::HeroArtifactsWidget(MapController & controller, CGHeroInsta
|
||||
fittingSet(CArtifactFittingSet(h))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->saveButton, &QPushButton::clicked, this, &HeroArtifactsWidget::onSaveButtonClicked);
|
||||
connect(ui->cancelButton, &QPushButton::clicked, this, &HeroArtifactsWidget::onCancelButtonClicked);
|
||||
|
||||
}
|
||||
|
||||
HeroArtifactsWidget::~HeroArtifactsWidget()
|
||||
@@ -37,9 +41,10 @@ HeroArtifactsWidget::~HeroArtifactsWidget()
|
||||
|
||||
void HeroArtifactsWidget::on_addButton_clicked()
|
||||
{
|
||||
ArtifactWidget artifactWidget{ fittingSet, this };
|
||||
connect(&artifactWidget, &ArtifactWidget::saveArtifact, this, &HeroArtifactsWidget::onSaveArtifact);
|
||||
artifactWidget.exec();
|
||||
auto * artifactWidget = new ArtifactWidget(fittingSet, this);
|
||||
connect(artifactWidget, &ArtifactWidget::saveArtifact, this, &HeroArtifactsWidget::onSaveArtifact);
|
||||
artifactWidget->exec();
|
||||
artifactWidget->deleteLater();
|
||||
}
|
||||
|
||||
void HeroArtifactsWidget::on_removeButton_clicked()
|
||||
@@ -55,6 +60,16 @@ void HeroArtifactsWidget::on_removeButton_clicked()
|
||||
ui->artifacts->removeRow(row);
|
||||
}
|
||||
|
||||
void HeroArtifactsWidget::onSaveButtonClicked()
|
||||
{
|
||||
accept();
|
||||
}
|
||||
|
||||
void HeroArtifactsWidget::onCancelButtonClicked()
|
||||
{
|
||||
reject();
|
||||
}
|
||||
|
||||
void HeroArtifactsWidget::onSaveArtifact(int32_t artifactIndex, ArtifactPosition slot)
|
||||
{
|
||||
auto artifact = controller.map()->createArtifact(LIBRARY->arth->getByIndex(artifactIndex)->getId());
|
||||
@@ -143,8 +158,11 @@ void HeroArtifactsDelegate::setModelData(QWidget * editor, QAbstractItemModel *
|
||||
{
|
||||
if (auto * ed = qobject_cast<HeroArtifactsWidget *>(editor))
|
||||
{
|
||||
ed->commitChanges();
|
||||
updateModelData(model, index);
|
||||
if(ed->result() == QDialog::Accepted)
|
||||
{
|
||||
ed->commitChanges();
|
||||
updateModelData(model, index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -158,6 +176,7 @@ void HeroArtifactsDelegate::updateModelData(QAbstractItemModel * model, const QM
|
||||
for(const auto & [artPosition, artSlotInfo] : hero.artifactsWorn)
|
||||
{
|
||||
auto slotText = NArtifactPosition::namesHero[artPosition.num];
|
||||
|
||||
textList += QString("%1: %2").arg(QString::fromStdString(slotText)).arg(QString::fromStdString(artSlotInfo.getArt()->getType()->getNameTranslated()));
|
||||
}
|
||||
textList += QString("%1:").arg(QString::fromStdString(NArtifactPosition::backpack));
|
||||
|
||||
@@ -38,6 +38,10 @@ private slots:
|
||||
|
||||
void on_removeButton_clicked();
|
||||
|
||||
void onSaveButtonClicked();
|
||||
|
||||
void onCancelButtonClicked();
|
||||
|
||||
private:
|
||||
enum Column
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<class>HeroArtifactsWidget</class>
|
||||
<widget class="QDialog" name="HeroArtifactsWidget">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
<enum>Qt::WindowModality::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@@ -46,7 +46,7 @@
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@@ -96,19 +96,19 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
<enum>QAbstractItemView::SelectionMode::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>true</bool>
|
||||
@@ -137,6 +137,37 @@
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutBottom">
|
||||
<item>
|
||||
<spacer name="horizontalSpacerBottom">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveButton">
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -41,6 +41,7 @@ Initializer::Initializer(MapController & controller, CGObjectInstance * o, const
|
||||
, defaultPlayer(pl)
|
||||
{
|
||||
logGlobal->info("New object instance initialized");
|
||||
o->cb = controller.getCallback();
|
||||
///IMPORTANT! initialize order should be from base objects to derived objects
|
||||
INIT_OBJ_TYPE(CGResource);
|
||||
INIT_OBJ_TYPE(CGArtifact);
|
||||
@@ -206,6 +207,13 @@ void Initializer::initialize(CGArtifact * o)
|
||||
auto a = controller.map()->createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault()));
|
||||
o->setArtifactInstance(a);
|
||||
}
|
||||
else if(o->ID == Obj::ARTIFACT)
|
||||
{
|
||||
auto instance = controller.map()->createArtifact(o->getArtifactType());
|
||||
o->setArtifactInstance(instance);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unimplemented initializer for CGArtifact object ID = "+ std::to_string(o->ID.getNum()));
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGMine * o)
|
||||
@@ -365,11 +373,11 @@ void Inspector::updateProperties(CGTownInstance * o)
|
||||
void Inspector::updateProperties(CGArtifact * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
|
||||
addProperty(QObject::tr("Message"), o->message, false);
|
||||
|
||||
|
||||
const CArtifactInstance * instance = o->getArtifactInstance();
|
||||
if(instance)
|
||||
if(instance && o->ID == Obj::SPELL_SCROLL)
|
||||
{
|
||||
SpellID spellId = instance->getScrollSpellID();
|
||||
if(spellId != SpellID::NONE)
|
||||
|
||||
@@ -395,7 +395,7 @@ bool MainWindow::openMap(const QString & filenameSelect)
|
||||
{
|
||||
try
|
||||
{
|
||||
controller.setMap(Helper::openMapInternal(filenameSelect));
|
||||
controller.setMap(Helper::openMapInternal(filenameSelect, controller.getCallback()));
|
||||
}
|
||||
catch(const ModIncompatibility & e)
|
||||
{
|
||||
@@ -703,7 +703,7 @@ void MainWindow::addGroupIntoCatalog(const QString & groupName, bool useCustomNa
|
||||
}
|
||||
|
||||
//create object to extract name
|
||||
auto temporaryObj(factory->create(nullptr, templ));
|
||||
auto temporaryObj(factory->create(controller.getCallback(), templ));
|
||||
QString translated = useCustomName ? QString::fromStdString(temporaryObj->getObjectName().c_str()) : subGroupName;
|
||||
itemType->setText(translated);
|
||||
|
||||
@@ -1370,7 +1370,8 @@ void MainWindow::on_actionh3m_converter_triggered()
|
||||
for(auto & m : mapFiles)
|
||||
{
|
||||
CMapService mapService;
|
||||
auto map = Helper::openMapInternal(m);
|
||||
auto map = Helper::openMapInternal(m, controller.getCallback());
|
||||
controller.setCallback(std::make_unique<EditorCallback>(map.get()));
|
||||
controller.repairMap(map.get());
|
||||
mapService.saveMap(map, (saveDirectory + '/' + QFileInfo(m).completeBaseName() + ".vmap").toStdString());
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ MapController::MapController(MainWindow * m): main(m)
|
||||
_miniscenes[i].reset(new MinimapScene(i));
|
||||
}
|
||||
connectScenes();
|
||||
_cb = std::make_unique<EditorCallback>(nullptr);
|
||||
}
|
||||
|
||||
void MapController::connectScenes()
|
||||
@@ -70,6 +71,16 @@ MapController::~MapController()
|
||||
main = nullptr;
|
||||
}
|
||||
|
||||
void MapController::setCallback(std::unique_ptr<EditorCallback> cb)
|
||||
{
|
||||
_cb = std::move(cb);
|
||||
}
|
||||
|
||||
EditorCallback * MapController::getCallback()
|
||||
{
|
||||
return _cb.get();
|
||||
}
|
||||
|
||||
const std::unique_ptr<CMap> & MapController::getMapUniquePtr() const
|
||||
{
|
||||
return _map;
|
||||
@@ -104,6 +115,8 @@ void MapController::repairMap(CMap * map)
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
assert(map->cb);
|
||||
|
||||
//make sure events/rumors has name to have proper identifiers
|
||||
int emptyNameId = 1;
|
||||
@@ -201,7 +214,9 @@ void MapController::repairMap(CMap * map)
|
||||
|
||||
void MapController::setMap(std::unique_ptr<CMap> cmap)
|
||||
{
|
||||
cmap->cb = _cb.get();
|
||||
_map = std::move(cmap);
|
||||
_cb->setMap(_map.get());
|
||||
|
||||
repairMap();
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "maphandler.h"
|
||||
#include "mapview.h"
|
||||
#include "lib/modding/ModVerificationInfo.h"
|
||||
#include "../lib/callback/EditorCallback.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
using ModCompatibilityInfo = std::map<std::string, ModVerificationInfo>;
|
||||
@@ -31,6 +32,8 @@ public:
|
||||
MapController(const MapController &&) = delete;
|
||||
~MapController();
|
||||
|
||||
void setCallback(std::unique_ptr<EditorCallback>);
|
||||
EditorCallback * getCallback();
|
||||
void setMap(std::unique_ptr<CMap>);
|
||||
void initObstaclePainters(CMap * map);
|
||||
|
||||
@@ -93,6 +96,7 @@ signals:
|
||||
void requestModsUpdate(const ModCompatibilityInfo & mods, bool leaveCheckedUnchanged) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<EditorCallback> _cb;
|
||||
std::unique_ptr<CMap> _map;
|
||||
std::unique_ptr<MapHandler> _mapHandler;
|
||||
MainWindow * main;
|
||||
|
||||
@@ -594,7 +594,7 @@ void MapView::dragEnterEvent(QDragEnterEvent * event)
|
||||
auto factory = LIBRARY->objtypeh->getHandlerFor(objId, objSubId);
|
||||
auto templ = factory->getTemplates()[templateId];
|
||||
controller->discardObject(sc->level);
|
||||
controller->createObject(sc->level, factory->create(nullptr, templ));
|
||||
controller->createObject(sc->level, factory->create(controller->getCallback(), templ));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -512,4 +512,4 @@ void WindowNewMap::on_sizeCustomRadio_toggled(bool checked)
|
||||
ui->sizeGroup2->setEnabled(true);
|
||||
}
|
||||
updateTemplateList();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user