Separate MapImageExporter from Editor

This commit is contained in:
GriffinR
2025-03-20 14:10:58 -04:00
parent 1adbfcb3ec
commit 52a06d5b32
14 changed files with 176 additions and 124 deletions

View File

@@ -40,6 +40,9 @@
<property name="sizeAdjustPolicy">
<enum>QComboBox::SizeAdjustPolicy::AdjustToContents</enum>
</property>
<property name="insertPolicy">
<enum>QComboBox::InsertPolicy::NoInsert</enum>
</property>
</widget>
</item>
</layout>

View File

@@ -89,7 +89,7 @@ public:
bool hasEvent(Event *) const;
void deleteConnections();
QList<MapConnection*> getConnections() const;
QList<MapConnection*> getConnections() const { return m_connections; }
void removeConnection(MapConnection *);
void addConnection(MapConnection *);
void loadConnection(MapConnection *);

View File

@@ -26,13 +26,19 @@ public:
QString direction() const { return m_direction; }
void setDirection(const QString &direction, bool mirror = true);
bool isCardinal() const { return isCardinal(m_direction); }
bool isHorizontal() const { return isHorizontal(m_direction); }
bool isVertical() const { return isVertical(m_direction); }
bool isDiving() const { return isDiving(m_direction); }
int offset() const { return m_offset; }
void setOffset(int offset, bool mirror = true);
MapConnection* findMirror();
MapConnection* createMirror();
QPixmap getPixmap();
QPixmap render() const;
QPoint relativePos(bool clipped = false) const;
static QPointer<Project> project;
static const QMap<QString, QString> oppositeDirections;

View File

@@ -95,6 +95,8 @@ public:
int getHeight() const { return height; }
int getBorderWidth() const { return border_width; }
int getBorderHeight() const { return border_height; }
int getBorderDrawWidth() const;
int getBorderDrawHeight() const;
bool isWithinBounds(int x, int y);
bool isWithinBorderBounds(int x, int y);
@@ -140,6 +142,8 @@ private:
void setNewDimensionsBlockdata(int newWidth, int newHeight);
void setNewBorderDimensionsBlockdata(int newWidth, int newHeight);
static int getBorderDrawDistance(int dimension, qreal minimum);
signals:
void layoutChanged(Layout *layout);
//void modified();

View File

@@ -175,8 +175,6 @@ public:
void eventsView_onMousePress(QMouseEvent *event);
int getBorderDrawDistance(int dimension);
bool selectingEvent = false;
void deleteSelectedEvents();

View File

@@ -1,10 +1,7 @@
#ifndef MAPIMAGEEXPORTER_H
#define MAPIMAGEEXPORTER_H
#include "map.h"
#include "editor.h"
#include <QDialog>
#include "project.h"
namespace Ui {
class MapImageExporter;
@@ -39,15 +36,21 @@ class MapImageExporter : public QDialog
Q_OBJECT
public:
explicit MapImageExporter(QWidget *parent, Editor *editor, ImageExporterMode mode);
explicit MapImageExporter(QWidget *parent, Project *project, Layout *layout, ImageExporterMode mode = ImageExporterMode::Normal)
: MapImageExporter(parent, project, nullptr, layout, mode) {};
explicit MapImageExporter(QWidget *parent, Project *project, Map *map, ImageExporterMode mode = ImageExporterMode::Normal)
: MapImageExporter(parent, project, map, map->layout(), mode) {};
~MapImageExporter();
private:
Ui::MapImageExporter *ui;
ImageExporterMode mode() const { return m_mode; }
Layout *m_layout = nullptr;
private:
explicit MapImageExporter(QWidget *parent, Project *project, Map *map, Layout *layout, ImageExporterMode mode);
Ui::MapImageExporter *ui;
Project *m_project = nullptr;
Map *m_map = nullptr;
Editor *m_editor = nullptr;
Layout *m_layout = nullptr;
QGraphicsScene *m_scene = nullptr;
QPixmap m_preview;
@@ -55,6 +58,8 @@ private:
ImageExporterSettings m_settings;
ImageExporterMode m_mode = ImageExporterMode::Normal;
QString getTitle(ImageExporterMode mode);
QString getDescription(ImageExporterMode mode);
void updatePreview();
void scalePreview();
void updateShowBorderState();
@@ -65,6 +70,7 @@ private:
QPixmap getFormattedLayoutPixmap(Layout *layout, bool ignoreBorder = false, bool ignoreGrid = false);
void paintGrid(QPixmap *pixmap, bool ignoreBorder = false);
bool historyItemAppliesToFrame(const QUndoCommand *command);
void updateMapSelection(const QString &text);
protected:
virtual void showEvent(QShowEvent *) override;

View File

@@ -233,10 +233,6 @@ void Map::deleteConnections() {
m_connections.clear();
}
QList<MapConnection*> Map::getConnections() const {
return m_connections;
}
void Map::addConnection(MapConnection *connection) {
if (!connection || m_connections.contains(connection))
return;
@@ -244,7 +240,7 @@ void Map::addConnection(MapConnection *connection) {
// Maps should only have one Dive/Emerge connection at a time.
// (Users can technically have more by editing their data manually, but we will only display one at a time)
// Any additional connections being added (this can happen via mirroring) are tracked for deleting but otherwise ignored.
if (MapConnection::isDiving(connection->direction())) {
if (connection->isDiving()) {
for (const auto &i : m_connections) {
if (i->direction() == connection->direction()) {
trackConnection(connection);

View File

@@ -60,7 +60,7 @@ Map* MapConnection::targetMap() const {
return getMap(m_targetMapName);
}
QPixmap MapConnection::getPixmap() {
QPixmap MapConnection::render() const {
auto map = targetMap();
if (!map)
return QPixmap();
@@ -68,6 +68,28 @@ QPixmap MapConnection::getPixmap() {
return map->renderConnection(m_direction, m_parentMap ? m_parentMap->layout() : nullptr);
}
// Get the position of the target map relative to its parent map.
// For right/down connections this is offset by the dimensions of the parent map.
// For left/up connections this is offset by the dimensions of the target map.
// If 'clipped' is true, only the rendered dimensions of the target map will be used, rather than its full dimensions.
QPoint MapConnection::relativePos(bool clipped) const {
int x = 0, y = 0;
if (m_direction == "right") {
if (m_parentMap) x = m_parentMap->getWidth();
y = m_offset;
} else if (m_direction == "down") {
x = m_offset;
if (m_parentMap) y = m_parentMap->getHeight();
} else if (m_direction == "left") {
if (targetMap()) x = !clipped ? -targetMap()->getWidth() : -targetMap()->getConnectionRect(m_direction).width();
y = m_offset;
} else if (m_direction == "up") {
x = m_offset;
if (targetMap()) y = !clipped ? -targetMap()->getHeight() : -targetMap()->getConnectionRect(m_direction).height();
}
return QPoint(x, y);
}
void MapConnection::setParentMap(Map* map, bool mirror) {
if (map == m_parentMap)
return;

View File

@@ -58,6 +58,25 @@ bool Layout::isWithinBorderBounds(int x, int y) {
return (x >= 0 && x < this->getBorderWidth() && y >= 0 && y < this->getBorderHeight());
}
int Layout::getBorderDrawWidth() const {
return getBorderDrawDistance(border_width, BORDER_DISTANCE);
}
int Layout::getBorderDrawHeight() const {
return getBorderDrawDistance(border_height, BORDER_DISTANCE);
}
// We need to draw sufficient border blocks to fill the area that gets loaded around the player in-game (BORDER_DISTANCE).
// Note that this is not the same as the player's view distance.
// The result will be some multiple of the input dimension, because we only draw the border in increments of its full width/height.
int Layout::getBorderDrawDistance(int dimension, qreal minimum) {
if (dimension >= minimum)
return dimension;
// Get first multiple of dimension >= the minimum
return dimension * qCeil(minimum / qMax(dimension, 1));
}
bool Layout::getBlock(int x, int y, Block *out) {
if (isWithinBounds(x, y)) {
int i = y * getWidth() + x;

View File

@@ -761,7 +761,7 @@ void Editor::displayConnection(MapConnection *connection) {
if (!connection)
return;
if (MapConnection::isDiving(connection->direction())) {
if (connection->isDiving()) {
displayDivingConnection(connection);
return;
}
@@ -826,7 +826,7 @@ void Editor::removeConnectionPixmap(MapConnection *connection) {
if (!connection)
return;
if (MapConnection::isDiving(connection->direction())) {
if (connection->isDiving()) {
removeDivingMapPixmap(connection);
return;
}
@@ -1790,8 +1790,8 @@ void Editor::displayMapBorder() {
int borderWidth = this->layout->getBorderWidth();
int borderHeight = this->layout->getBorderHeight();
int borderHorzDist = getBorderDrawDistance(borderWidth);
int borderVertDist = getBorderDrawDistance(borderHeight);
int borderHorzDist = this->layout->getBorderDrawWidth();
int borderVertDist = this->layout->getBorderDrawHeight();
QPixmap pixmap = this->layout->renderBorder();
for (int y = -borderVertDist; y < this->layout->getHeight() + borderVertDist; y += borderHeight)
for (int x = -borderHorzDist; x < this->layout->getWidth() + borderHorzDist; x += borderWidth) {
@@ -1816,17 +1816,6 @@ void Editor::updateMapConnections() {
item->render(true);
}
int Editor::getBorderDrawDistance(int dimension) {
// Draw sufficient border blocks to fill the player's view (BORDER_DISTANCE)
if (dimension >= BORDER_DISTANCE) {
return dimension;
} else if (dimension) {
return dimension * (BORDER_DISTANCE / dimension + (BORDER_DISTANCE % dimension ? 1 : 0));
} else {
return BORDER_DISTANCE;
}
}
void Editor::toggleGrid(bool checked) {
if (porymapConfig.showGrid == checked)
return;

View File

@@ -2492,12 +2492,19 @@ void MainWindow::on_actionImport_Map_from_Advance_Map_1_92_triggered() {
void MainWindow::showExportMapImageWindow(ImageExporterMode mode) {
if (!editor->project) return;
// If the user is requesting this window again we assume it's for a new
// window (the map/mode may have changed), so delete the old window.
if (this->mapImageExporter)
// If the user is requesting this window again with a different mode
// then we'll recreate the window with the new mode.
if (this->mapImageExporter && this->mapImageExporter->mode() != mode)
delete this->mapImageExporter;
this->mapImageExporter = new MapImageExporter(this, this->editor, mode);
if (!this->mapImageExporter) {
// Open new image export window
if (this->editor->map){
this->mapImageExporter = new MapImageExporter(this, this->editor->project, this->editor->map, mode);
} else if (this->editor->layout) {
this->mapImageExporter = new MapImageExporter(this, this->editor->project, this->editor->layout, mode);
}
}
openSubWindow(this->mapImageExporter);
}

View File

@@ -5,7 +5,7 @@
#include <math.h>
ConnectionPixmapItem::ConnectionPixmapItem(MapConnection* connection)
: QGraphicsPixmapItem(connection->getPixmap()),
: QGraphicsPixmapItem(connection->render()),
connection(connection)
{
this->setEditable(true);
@@ -28,7 +28,7 @@ void ConnectionPixmapItem::refresh() {
// Render additional visual effects on top of the base map image.
void ConnectionPixmapItem::render(bool ignoreCache) {
if (ignoreCache)
this->basePixmap = this->connection->getPixmap();
this->basePixmap = this->connection->render();
QPixmap pixmap = this->basePixmap.copy(0, 0, this->basePixmap.width(), this->basePixmap.height());
this->setZValue(-1);
@@ -63,10 +63,10 @@ QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVari
int newOffset = this->connection->offset();
// Restrict movement to the metatile grid and perpendicular to the connection direction.
if (MapConnection::isVertical(this->connection->direction())) {
if (this->connection->isVertical()) {
x = (round(newPos.x() / this->mWidth) * this->mWidth) - this->originX;
newOffset = x / this->mWidth;
} else if (MapConnection::isHorizontal(this->connection->direction())) {
} else if (this->connection->isHorizontal()) {
y = (round(newPos.y() / this->mHeight) * this->mHeight) - this->originY;
newOffset = y / this->mHeight;
}
@@ -87,9 +87,9 @@ void ConnectionPixmapItem::updatePos() {
qreal x = this->originX;
qreal y = this->originY;
if (MapConnection::isVertical(this->connection->direction())) {
if (this->connection->isVertical()) {
x += this->connection->offset() * this->mWidth;
} else if (MapConnection::isHorizontal(this->connection->direction())) {
} else if (this->connection->isHorizontal()) {
y += this->connection->offset() * this->mHeight;
}
@@ -98,22 +98,13 @@ void ConnectionPixmapItem::updatePos() {
}
void ConnectionPixmapItem::updateOrigin() {
const Map *parentMap = connection->parentMap();
const Map *targetMap = connection->targetMap();
const QString direction = connection->direction();
int x = 0, y = 0;
if (direction == "right") {
if (parentMap) x = parentMap->getWidth();
} else if (direction == "down") {
if (parentMap) y = parentMap->getHeight();
} else if (direction == "left") {
if (targetMap) x = -targetMap->getConnectionRect(direction).width();
} else if (direction == "up") {
if (targetMap) y = -targetMap->getConnectionRect(direction).height();
if (this->connection->isVertical()) {
this->originX = 0;
this->originY = this->connection->relativePos(true).y() * this->mHeight;
} else if (this->connection->isHorizontal()) {
this->originX = this->connection->relativePos(true).x() * this->mWidth;
this->originY = 0;
}
this->originX = x * this->mWidth;
this->originY = y * this->mHeight;
updatePos();
}

View File

@@ -25,7 +25,7 @@ QPixmap DivingMapPixmapItem::getBasePixmap(MapConnection* connection) {
return QPixmap(); // Save some rendering time if it won't be displayed
if (connection->targetMapName() == connection->parentMapName())
return QPixmap(); // If the map is connected to itself then rendering is pointless.
return connection->getPixmap();
return connection->render();
}
void DivingMapPixmapItem::updatePixmap() {

View File

@@ -10,61 +10,71 @@
#define STITCH_MODE_BORDER_DISTANCE 2
QString getTitle(ImageExporterMode mode) {
QString MapImageExporter::getTitle(ImageExporterMode mode) {
switch (mode)
{
case ImageExporterMode::Normal:
return "Export Map Image";
return QString("Export %1 Image").arg(m_map ? "Map" : "Layout");
case ImageExporterMode::Stitch:
return "Export Map Stitch Image";
return QStringLiteral("Export Map Stitch Image");
case ImageExporterMode::Timelapse:
return "Export Map Timelapse Image";
return QString("Export %1 Timelapse Image").arg(m_map ? "Map" : "Layout");
}
return "";
}
QString getDescription(ImageExporterMode mode) {
QString MapImageExporter::getDescription(ImageExporterMode mode) {
switch (mode)
{
case ImageExporterMode::Normal:
return "Exports an image of the selected map.";
return QString("Exports an image of the selected %1.").arg(m_map ? "Map" : "Layout");
case ImageExporterMode::Stitch:
return "Exports a combined image of all the maps connected to the selected map.";
case ImageExporterMode::Timelapse:
return "Exports a GIF of the edit history for the selected map.";
return QString("Exports a GIF of the edit history for the selected %1.").arg(m_map ? "Map" : "Layout");
}
return "";
}
MapImageExporter::MapImageExporter(QWidget *parent, Editor *editor, ImageExporterMode mode) :
MapImageExporter::MapImageExporter(QWidget *parent, Project *project, Map *map, Layout *layout, ImageExporterMode mode) :
QDialog(parent),
ui(new Ui::MapImageExporter)
ui(new Ui::MapImageExporter),
m_project(project),
m_map(map),
m_layout(layout),
m_mode(mode)
{
setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this);
m_map = editor->map;
m_layout = editor->layout;
m_editor = editor;
m_mode = mode;
setWindowTitle(getTitle(m_mode));
ui->label_Description->setText(getDescription(m_mode));
ui->groupBox_Connections->setVisible(m_mode != ImageExporterMode::Stitch);
ui->groupBox_Connections->setVisible(m_map && m_mode != ImageExporterMode::Stitch);
ui->groupBox_Timelapse->setVisible(m_mode == ImageExporterMode::Timelapse);
ui->groupBox_Events->setVisible(m_map != nullptr);
if (m_map) {
ui->comboBox_MapSelection->addItems(editor->project->mapNames);
ui->comboBox_MapSelection->setCurrentText(m_map->name());
ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down
// Initialize map selector.
if (m_mode != ImageExporterMode::Timelapse) {
if (m_map) {
ui->comboBox_MapSelection->addItems(m_project->mapNames);
ui->comboBox_MapSelection->setCurrentText(m_map->name());
ui->label_MapSelection->setText(m_mode == ImageExporterMode::Stitch ? QStringLiteral("Starting Map") : QStringLiteral("Map"));
} else if (m_layout) {
ui->comboBox_MapSelection->addItems(m_project->layoutIds);
ui->comboBox_MapSelection->setCurrentText(m_layout->id);
ui->label_MapSelection->setText(QStringLiteral("Layout"));
}
} else {
// Some settings only apply to maps. When exporting an image in layout-only mode we hide them.
// At the moment edit history for events (and the DraggablePixmapItem class)
// depend on the editor and assume their map is the current map.
// Until this is resolved the selected map cannot be changed in Timelapse mode.
ui->comboBox_MapSelection->setVisible(false);
ui->label_MapSelection->setVisible(false);
ui->groupBox_Events->setVisible(false);
ui->groupBox_Connections->setVisible(false);
}
ui->graphicsView_Preview->setFocus();
connect(ui->pushButton_Save, &QPushButton::pressed, this, &MapImageExporter::saveImage);
connect(ui->pushButton_Cancel, &QPushButton::pressed, this, &MapImageExporter::close);
connect(ui->comboBox_MapSelection, &QComboBox::currentTextChanged, this, &MapImageExporter::updateMapSelection);
}
MapImageExporter::~MapImageExporter() {
@@ -84,6 +94,25 @@ void MapImageExporter::resizeEvent(QResizeEvent *event) {
scalePreview();
}
void MapImageExporter::updateMapSelection(const QString &text) {
if (m_map) {
if (!m_project->mapNames.contains(text))
return;
Map *newMap = m_project->loadMap(text);
if (newMap == m_map)
return;
m_map = newMap;
} else {
if (!m_project->layoutIds.contains(text))
return;
Layout *newLayout = m_project->loadLayout(text);
if (newLayout == m_layout)
return;
m_layout = newLayout;
}
updatePreview();
}
void MapImageExporter::saveImage() {
// Make sure preview is up-to-date before we save.
if (m_preview.isNull())
@@ -91,7 +120,6 @@ void MapImageExporter::saveImage() {
if (m_preview.isNull())
return;
const QString title = getTitle(m_mode);
const QString itemName = m_map ? m_map->name() : m_layout->name;
QString defaultFilename;
switch (m_mode)
@@ -112,7 +140,7 @@ void MapImageExporter::saveImage() {
.arg(defaultFilename)
.arg(m_mode == ImageExporterMode::Timelapse ? "gif" : "png");
QString filter = m_mode == ImageExporterMode::Timelapse ? "Image Files (*.gif)" : "Image Files (*.png *.jpg *.bmp)";
QString filepath = FileDialog::getSaveFileName(this, title, defaultFilepath, filter);
QString filepath = FileDialog::getSaveFileName(this, windowTitle(), defaultFilepath, filter);
if (!filepath.isEmpty()) {
switch (m_mode) {
case ImageExporterMode::Normal:
@@ -275,7 +303,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress, bool inclu
QSet<QString> visited;
QList<StitchedMap> stitchedMaps;
QList<StitchedMap> unvisited;
unvisited.append(StitchedMap{0, 0, m_editor->map});
unvisited.append(StitchedMap{0, 0, m_map});
progress->setLabelText("Gathering stitched maps...");
while (!unvisited.isEmpty()) {
@@ -291,31 +319,10 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress, bool inclu
visited.insert(cur.map->name());
stitchedMaps.append(cur);
for (MapConnection *connection : cur.map->getConnections()) {
const QString direction = connection->direction();
int x = cur.x;
int y = cur.y;
int offset = connection->offset();
Map *connectionMap = connection->targetMap();
if (!connectionMap)
continue;
if (direction == "up") {
x += offset;
y -= connectionMap->getHeight();
} else if (direction == "down") {
x += offset;
y += cur.map->getHeight();
} else if (direction == "left") {
x -= connectionMap->getWidth();
y += offset;
} else if (direction == "right") {
x += cur.map->getWidth();
y += offset;
} else {
// Ignore Dive/Emerge connections and unrecognized directions
continue;
}
unvisited.append(StitchedMap{x, y, connectionMap});
for (const auto &connection : cur.map->getConnections()) {
if (!connection->isCardinal()) continue;
QPoint pos = connection->relativePos();
unvisited.append(StitchedMap{cur.x + pos.x(), cur.y + pos.y(), connection->targetMap()});
}
}
@@ -441,7 +448,7 @@ QPixmap MapImageExporter::getFormattedLayoutPixmap(Layout *layout, bool ignoreBo
if (m_settings.showCollision) {
QPainter collisionPainter(&pixmap);
layout->renderCollision(true);
collisionPainter.setOpacity(m_editor->collisionOpacity);
collisionPainter.setOpacity(static_cast<qreal>(porymapConfig.collisionOpacity) / 100);
collisionPainter.drawPixmap(0, 0, layout->collision_pixmap);
collisionPainter.end();
}
@@ -452,8 +459,8 @@ QPixmap MapImageExporter::getFormattedLayoutPixmap(Layout *layout, bool ignoreBo
if (!ignoreBorder && m_settings.showBorder) {
int borderDistance = m_mode ? STITCH_MODE_BORDER_DISTANCE : BORDER_DISTANCE;
layout->renderBorder();
int borderHorzDist = m_editor->getBorderDrawDistance(layout->getBorderWidth());
int borderVertDist = m_editor->getBorderDrawDistance(layout->getBorderHeight());
int borderHorzDist = layout->getBorderDrawWidth();
int borderVertDist = layout->getBorderDrawHeight();
borderWidth = borderDistance * 16;
borderHeight = borderDistance * 16;
QPixmap newPixmap = QPixmap(layout->pixmap.width() + borderWidth * 2, layout->pixmap.height() + borderHeight * 2);
@@ -487,17 +494,20 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
QPainter connectionPainter(&pixmap);
int borderDistance = m_mode ? STITCH_MODE_BORDER_DISTANCE : BORDER_DISTANCE;
int borderWidth = borderDistance * 16;
int borderHeight = borderDistance * 16;
// TODO: Reading the connections from the editor and not 'map' is incorrect.
for (auto connectionItem : m_editor->connection_items) {
const QString direction = connectionItem->connection->direction();
if ((m_settings.showUpConnections && direction == "up")
|| (m_settings.showDownConnections && direction == "down")
|| (m_settings.showLeftConnections && direction == "left")
|| (m_settings.showRightConnections && direction == "right"))
connectionPainter.drawImage(connectionItem->x() + borderWidth, connectionItem->y() + borderHeight,
connectionItem->connection->getPixmap().toImage());
for (const auto &connection : m_map->getConnections()) {
const QString direction = connection->direction();
if (direction == "up") {
if (!m_settings.showUpConnections) continue;
} else if (direction == "down") {
if (!m_settings.showDownConnections) continue;
} else if (direction == "left") {
if (!m_settings.showLeftConnections) continue;
} else if (direction == "right") {
if (!m_settings.showRightConnections) continue;
} else continue; // Ignore any other directions
QPoint pos = connection->relativePos(true);
connectionPainter.drawImage((pos.x() + borderDistance) * 16, (pos.y() + borderDistance) * 16, connection->render().toImage());
}
connectionPainter.end();
}
@@ -517,7 +527,8 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
|| (m_settings.showBGs && group == Event::Group::Bg)
|| (m_settings.showTriggers && group == Event::Group::Coord)
|| (m_settings.showHealLocations && group == Event::Group::Heal)) {
m_editor->project->loadEventPixmap(event);
m_project->loadEventPixmap(event);
eventPainter.setOpacity(event->getUsesDefaultPixmap() ? 0.7 : 1.0);
eventPainter.drawImage(QPoint(event->getPixelX() + pixelOffset, event->getPixelY() + pixelOffset), event->getPixmap().toImage());
}
}