From 6bf80ad506d6572b11ac38c791bd407a4cf8d6c3 Mon Sep 17 00:00:00 2001 From: Lychee Lynx <248896295+lychee-lynx@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:58:08 +0530 Subject: [PATCH 1/4] Feature: allow adding and deleting areas via GUI --- src/CMakeLists.txt | 2 + src/dlgConfigureAreas.cpp | 202 ++++++++++++++++++++++++++++++++++++++ src/dlgConfigureAreas.h | 45 +++++++++ src/dlgMapper.cpp | 5 + src/mudlet.cpp | 14 +++ src/mudlet.h | 1 + src/ui/configure_areas.ui | 70 +++++++++++++ 7 files changed, 339 insertions(+) create mode 100644 src/dlgConfigureAreas.cpp create mode 100644 src/dlgConfigureAreas.h create mode 100644 src/ui/configure_areas.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 84c35520417..7672f8cb98a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,7 @@ set(mudlet_SRCS dlgAliasMainArea.cpp dlgColorTrigger.cpp dlgComposer.cpp + dlgConfigureAreas.cpp dlgConnectionProfiles.cpp dlgIRC.cpp dlgKeysMainArea.cpp @@ -232,6 +233,7 @@ set(mudlet_UIS ui/aliases_main_area.ui ui/color_trigger.ui ui/composer.ui + ui/configure_areas.ui ui/connection_profiles.ui ui/dlgPackageExporter.ui ui/irc.ui diff --git a/src/dlgConfigureAreas.cpp b/src/dlgConfigureAreas.cpp new file mode 100644 index 00000000000..e4dafde492d --- /dev/null +++ b/src/dlgConfigureAreas.cpp @@ -0,0 +1,202 @@ +/*************************************************************************** + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "dlgConfigureAreas.h" +#include "dlgMapper.h" +#include "TArea.h" +#include "TMap.h" +#include "TRoomDB.h" + +#include "mudlet.h" + +#include +#include +#include +#include +#include +#include +#include + +enum Columns +{ + ColAreaName = 0, + ColAreaId, + ColRoomCount, + ColCount +}; + +dlgConfigureAreas::dlgConfigureAreas(TMap* map, QWidget* parent) : QDialog(parent), mpMap(map) +{ + setupUi(this); + areaTable->setSelectionBehavior(QAbstractItemView::SelectRows); + areaTable->setSelectionMode(QAbstractItemView::SingleSelection); + + setWindowTitle(tr("Configure Areas")); + resize(400, 300); + + areaTable->setColumnCount(ColCount); + areaTable->setColumnHidden(ColAreaId, true); + + areaTable->setHorizontalHeaderLabels({tr("Area Name"), QString(), tr("Rooms")}); + + connect(addAreaBtn, &QPushButton::clicked, this, &dlgConfigureAreas::slot_addArea); + connect(removeAreaBtn, &QPushButton::clicked, this, &dlgConfigureAreas::slot_removeArea); + + populateAreaList(); +} + +bool checkDefaultArea(TMap* map, int areaId) +{ + if (!map || !map->mpRoomDB) + { + return false; + } + + const QString defaultAreaName = map->getDefaultAreaName(); + const auto& areaNames = map->mpRoomDB->getAreaNamesMap(); + + return areaNames.value(areaId) == defaultAreaName; +} + +int dlgConfigureAreas::currentAreaId() const +{ + const auto selected = areaTable->selectionModel()->selectedRows(); + if (selected.isEmpty()) + { + return -1; + } + + return areaTable->item(selected.first().row(), ColAreaId)->text().toInt(); +} + +void dlgConfigureAreas::refreshAreas() +{ + QWidget* p = parentWidget(); + while (p) + { + const auto mappers = p->findChildren(); + for (dlgMapper* mapper : mappers) + { + mapper->updateAreaComboBox(); + } + p = p->parentWidget(); + } +} + +void dlgConfigureAreas::populateAreaList() +{ + if (!mpMap || !mpMap->mpRoomDB) + { + return; + } + + areaTable->setRowCount(0); + + const auto& areaNames = mpMap->mpRoomDB->getAreaNamesMap(); + + int row = 0; + for (auto it = areaNames.begin(); it != areaNames.end(); ++it) + { + const int areaId = it.key(); + const QString& areaName = it.value(); + + int roomCount = 0; + if (TArea* area = mpMap->mpRoomDB->getArea(areaId)) + { + roomCount = area->getAreaRooms().size(); + } + + areaTable->insertRow(row); + areaTable->setItem(row, ColAreaName, new QTableWidgetItem(areaName)); + areaTable->setItem(row, ColAreaId, new QTableWidgetItem(QString::number(areaId))); + areaTable->setItem(row, ColRoomCount, new QTableWidgetItem(QString::number(roomCount))); + ++row; + } + + areaTable->resizeColumnsToContents(); +} + +void dlgConfigureAreas::slot_addArea() +{ + if (!mpMap || !mpMap->mpRoomDB) + { + return; + } + + bool ok{}; + const QString name = QInputDialog::getText(this, tr("Add Area:"), tr("Area Name"), QLineEdit::Normal, QString(), &ok); + + if (!ok || name.trimmed().isEmpty()) + { + return; + } + + const int newId = mpMap->mpRoomDB->addArea(name); + if (newId <= 0) + { + QMessageBox::warning(this, tr("Cannot add area"), tr("Area with this name already exists.")); + return; + } + + populateAreaList(); + refreshAreas(); +} + +void dlgConfigureAreas::slot_removeArea() +{ + if (!mpMap || !mpMap->mpRoomDB) + { + return; + } + + const int areaId = currentAreaId(); + if (areaId <= 0) + { + return; + } + + if (checkDefaultArea(mpMap, areaId)) + { + QMessageBox::warning(this, tr("Cannot delete area"), tr("Default area cannot be deleted.")); + return; + } + + TArea* area = mpMap->mpRoomDB->getArea(areaId); + const int roomCount = area ? area->getAreaRooms().size() : 0; + + if (roomCount > 0) + { + QMessageBox::warning(this, tr("Cannot delete area"), tr("This area contains %1 rooms.\n" + "Move or delete the rooms first.").arg(roomCount)); + return; + } + + const QString areaName = mpMap->mpRoomDB->getAreaNamesMap().value(areaId); + + const auto ret = QMessageBox::warning(this, tr("Delete Area"), tr("Delete empty area \"%1\"?").arg(areaName), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + + if (ret != QMessageBox::Yes) + { + return; + } + + mpMap->mpRoomDB->removeArea(areaId); + + populateAreaList(); + refreshAreas(); +} diff --git a/src/dlgConfigureAreas.h b/src/dlgConfigureAreas.h new file mode 100644 index 00000000000..01425e3579e --- /dev/null +++ b/src/dlgConfigureAreas.h @@ -0,0 +1,45 @@ +#ifndef MUDLET_DLGCONFIGUREAREAS_H +#define MUDLET_DLGCONFIGUREAREAS_H + +/*************************************************************************** + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "ui_configure_areas.h" +#include + +class TMap; + +class dlgConfigureAreas : public QDialog, public Ui::configure_areas +{ + Q_OBJECT + +public: + dlgConfigureAreas(TMap* map, QWidget* parent = nullptr); + +public slots: + void slot_addArea(); + void slot_removeArea(); + +private: + void populateAreaList(); + void refreshAreas(); + int currentAreaId() const; + + TMap* mpMap = nullptr; +}; + +#endif //MUDLET_DLGCONFIGUREAREAS_H \ No newline at end of file diff --git a/src/dlgMapper.cpp b/src/dlgMapper.cpp index 9e4f58c7bfe..95dccc536c6 100644 --- a/src/dlgMapper.cpp +++ b/src/dlgMapper.cpp @@ -598,6 +598,11 @@ void dlgMapper::slot_setupMapperMenu() menu->addAction(show3DMapAction); #endif + menu->addSeparator(); + auto* newAreaConfigurator = new QAction(tr("Configure areas"), this); + connect(newAreaConfigurator, &QAction::triggered, mudlet::self(), &mudlet::slot_newAreaConfigurator); + menu->addAction(newAreaConfigurator); + // Add separator and Info submenu menu->addSeparator(); mpInfoMenu = menu->addMenu(tr("Info overlays")); diff --git a/src/mudlet.cpp b/src/mudlet.cpp index d96fd2ca036..f6a0966d866 100644 --- a/src/mudlet.cpp +++ b/src/mudlet.cpp @@ -50,6 +50,7 @@ #include "TToolBar.h" #include "XMLimport.h" #include "dlgAboutDialog.h" +#include "dlgConfigureAreas.h" #include "dlgConnectionProfiles.h" #include "dlgIRC.h" #include "dlgMapper.h" @@ -1767,6 +1768,19 @@ void mudlet::slot_newMapWindow() } } +void mudlet::slot_newAreaConfigurator() +{ + Host* pHost = getActiveHost(); + if (!pHost || !pHost->mpMap) + { + return; + } + + auto* dlg = new dlgConfigureAreas(pHost->mpMap.data(), this); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); +} + void mudlet::updateWindowMenu() { // Clean up existing window list actions diff --git a/src/mudlet.h b/src/mudlet.h index fede1a7bc60..01ba2327437 100644 --- a/src/mudlet.h +++ b/src/mudlet.h @@ -437,6 +437,7 @@ public slots: void slot_toggleAlwaysOnTop(); void slot_minimize(); void slot_newMapWindow(); + void slot_newAreaConfigurator(); void updateWindowMenu(); void slot_activateMainWindow(); void slot_activateDetachedWindow(); diff --git a/src/ui/configure_areas.ui b/src/ui/configure_areas.ui new file mode 100644 index 00000000000..5515902c475 --- /dev/null +++ b/src/ui/configure_areas.ui @@ -0,0 +1,70 @@ + + + configure_areas + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 10 + 10 + 381 + 192 + + + + + + + + + + Area Name + + + + + Rooms + + + + + + + 10 + 240 + 93 + 26 + + + + Add Area + + + + + + 110 + 240 + 93 + 26 + + + + Remove Area + + + + + + From a9b1c10c91a975dd41f1d894197aa6cb35e74538 Mon Sep 17 00:00:00 2001 From: Lychee Lynx <248896295+lychee-lynx@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:50:25 +0530 Subject: [PATCH 2/4] Fix: GUI area configurator structure --- src/dlgConfigureAreas.cpp | 136 +++++++++++++++----------------------- src/dlgConfigureAreas.h | 4 +- src/mudlet.cpp | 3 +- src/ui/configure_areas.ui | 132 +++++++++++++++++++----------------- 4 files changed, 129 insertions(+), 146 deletions(-) diff --git a/src/dlgConfigureAreas.cpp b/src/dlgConfigureAreas.cpp index e4dafde492d..bcea5f5be3d 100644 --- a/src/dlgConfigureAreas.cpp +++ b/src/dlgConfigureAreas.cpp @@ -23,13 +23,8 @@ #include "mudlet.h" -#include #include -#include -#include #include -#include -#include enum Columns { @@ -46,12 +41,13 @@ dlgConfigureAreas::dlgConfigureAreas(TMap* map, QWidget* parent) : QDialog(paren areaTable->setSelectionMode(QAbstractItemView::SingleSelection); setWindowTitle(tr("Configure Areas")); - resize(400, 300); - areaTable->setColumnCount(ColCount); - areaTable->setColumnHidden(ColAreaId, true); - - areaTable->setHorizontalHeaderLabels({tr("Area Name"), QString(), tr("Rooms")}); + areaTable->setColumnCount(3); + areaTable->setHorizontalHeaderLabels({tr("Area Name"), tr("Area ID"), tr("Rooms")}); + areaTable->setColumnHidden(1, true); + areaTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + areaTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + areaTable->horizontalHeader()->setStretchLastSection(true); connect(addAreaBtn, &QPushButton::clicked, this, &dlgConfigureAreas::slot_addArea); connect(removeAreaBtn, &QPushButton::clicked, this, &dlgConfigureAreas::slot_removeArea); @@ -59,64 +55,56 @@ dlgConfigureAreas::dlgConfigureAreas(TMap* map, QWidget* parent) : QDialog(paren populateAreaList(); } -bool checkDefaultArea(TMap* map, int areaId) -{ - if (!map || !map->mpRoomDB) - { - return false; - } - - const QString defaultAreaName = map->getDefaultAreaName(); - const auto& areaNames = map->mpRoomDB->getAreaNamesMap(); - - return areaNames.value(areaId) == defaultAreaName; -} - int dlgConfigureAreas::currentAreaId() const { const auto selected = areaTable->selectionModel()->selectedRows(); - if (selected.isEmpty()) - { + if (selected.isEmpty()) { return -1; } return areaTable->item(selected.first().row(), ColAreaId)->text().toInt(); } -void dlgConfigureAreas::refreshAreas() +void dlgConfigureAreas::refreshAreas(bool added, const QString& areaName) { - QWidget* p = parentWidget(); - while (p) - { - const auto mappers = p->findChildren(); - for (dlgMapper* mapper : mappers) - { - mapper->updateAreaComboBox(); - } - p = p->parentWidget(); + if(!mpMap || !mpMap->mpMapper) + return; + + auto mapper = mpMap->mpMapper; + mapper->updateAreaComboBox(); + QComboBox* combo = mapper->findChild(); + + QString targetArea; + if(added) { + targetArea = areaName; + } + else { + targetArea = mpMap->getDefaultAreaName(); } + + const int index = combo->findText(targetArea); + if(index >= 0) { + mapper->slot_switchArea(index); + combo->setCurrentIndex(index); + } + + mpMap->setUnsaved(__func__); } void dlgConfigureAreas::populateAreaList() { - if (!mpMap || !mpMap->mpRoomDB) - { + if (!mpMap || !mpMap->mpRoomDB) { return; } areaTable->setRowCount(0); - const auto& areaNames = mpMap->mpRoomDB->getAreaNamesMap(); - int row = 0; - for (auto it = areaNames.begin(); it != areaNames.end(); ++it) - { + for (auto it = areaNames.begin(); it != areaNames.end(); ++it) { const int areaId = it.key(); const QString& areaName = it.value(); - int roomCount = 0; - if (TArea* area = mpMap->mpRoomDB->getArea(areaId)) - { + if (TArea* area = mpMap->mpRoomDB->getArea(areaId)) { roomCount = area->getAreaRooms().size(); } @@ -132,71 +120,57 @@ void dlgConfigureAreas::populateAreaList() void dlgConfigureAreas::slot_addArea() { - if (!mpMap || !mpMap->mpRoomDB) - { + if (!mpMap || !mpMap->mpRoomDB) { return; } bool ok{}; - const QString name = QInputDialog::getText(this, tr("Add Area:"), tr("Area Name"), QLineEdit::Normal, QString(), &ok); - - if (!ok || name.trimmed().isEmpty()) - { + const QString rawName = QInputDialog::getText(this, tr("Add Area:"), tr("Area Name"), QLineEdit::Normal, QString(), &ok); + const QString name = rawName.trimmed(); + if (!ok || name.isEmpty()) { return; } - const int newId = mpMap->mpRoomDB->addArea(name); - if (newId <= 0) - { - QMessageBox::warning(this, tr("Cannot add area"), tr("Area with this name already exists.")); + if (mpMap->mpRoomDB->getAreaNamesMap().values().count(name) > 0) { + statusLabel->setText(tr("Area with this name already exists.")); return; } + mpMap->mpRoomDB->addArea(name); + statusLabel->clear(); + populateAreaList(); - refreshAreas(); + refreshAreas(true, name); } void dlgConfigureAreas::slot_removeArea() { - if (!mpMap || !mpMap->mpRoomDB) - { + if (!mpMap || !mpMap->mpRoomDB) { return; } const int areaId = currentAreaId(); - if (areaId <= 0) - { - return; - } - - if (checkDefaultArea(mpMap, areaId)) - { - QMessageBox::warning(this, tr("Cannot delete area"), tr("Default area cannot be deleted.")); + if (areaId == -1) { + statusLabel->setText(tr("Default area cannot be deleted.")); return; } TArea* area = mpMap->mpRoomDB->getArea(areaId); const int roomCount = area ? area->getAreaRooms().size() : 0; + if (roomCount > 0) { + const auto reply = QMessageBox::warning(this, tr("Delete area"), tr("This will also delete %1 rooms.\nDo you want to continue?").arg(roomCount), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); - if (roomCount > 0) - { - QMessageBox::warning(this, tr("Cannot delete area"), tr("This area contains %1 rooms.\n" - "Move or delete the rooms first.").arg(roomCount)); - return; - } - - const QString areaName = mpMap->mpRoomDB->getAreaNamesMap().value(areaId); - - const auto ret = QMessageBox::warning(this, tr("Delete Area"), tr("Delete empty area \"%1\"?").arg(areaName), - QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); - - if (ret != QMessageBox::Yes) - { - return; + if (reply != QMessageBox::Yes) { + return; + } } mpMap->mpRoomDB->removeArea(areaId); + statusLabel->clear(); populateAreaList(); - refreshAreas(); + refreshAreas(false, QString()); } + diff --git a/src/dlgConfigureAreas.h b/src/dlgConfigureAreas.h index 01425e3579e..2c1fb4c2949 100644 --- a/src/dlgConfigureAreas.h +++ b/src/dlgConfigureAreas.h @@ -36,10 +36,10 @@ public slots: private: void populateAreaList(); - void refreshAreas(); + void refreshAreas(bool added, const QString& areaName); int currentAreaId() const; TMap* mpMap = nullptr; }; -#endif //MUDLET_DLGCONFIGUREAREAS_H \ No newline at end of file +#endif //MUDLET_DLGCONFIGUREAREAS_H diff --git a/src/mudlet.cpp b/src/mudlet.cpp index f6a0966d866..f63090f7820 100644 --- a/src/mudlet.cpp +++ b/src/mudlet.cpp @@ -1771,8 +1771,7 @@ void mudlet::slot_newMapWindow() void mudlet::slot_newAreaConfigurator() { Host* pHost = getActiveHost(); - if (!pHost || !pHost->mpMap) - { + if (!pHost || !pHost->mpMap) { return; } diff --git a/src/ui/configure_areas.ui b/src/ui/configure_areas.ui index 5515902c475..aa0cbf20393 100644 --- a/src/ui/configure_areas.ui +++ b/src/ui/configure_areas.ui @@ -2,69 +2,79 @@ configure_areas - - - 0 - 0 - 400 - 300 - - - Dialog + Configure Areas - - - - 10 - 10 - 381 - 192 - - - - - - - - - - Area Name - - - - - Rooms - - - - - - - 10 - 240 - 93 - 26 - - - - Add Area - - - - - - 110 - 240 - 93 - 26 - - - - Remove Area - - + + + + + + + + + + + Area Name + + + + + Rooms + + + + + + + + + + + + color: red; + + + + + + + + + + + Add Area + + + + + + + + Remove Area + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + - + \ No newline at end of file From 32eae762ce856e44aa971a6615fb1138302e9fc4 Mon Sep 17 00:00:00 2001 From: Lychee Lynx <248896295+lychee-lynx@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:54:38 +0530 Subject: [PATCH 3/4] Improve: Update area configurator room count in realtime --- src/T2DMap.cpp | 2 ++ src/TMap.h | 1 + src/dlgConfigureAreas.cpp | 3 +++ 3 files changed, 6 insertions(+) diff --git a/src/T2DMap.cpp b/src/T2DMap.cpp index e06c5ad44d6..4209b54bced 100644 --- a/src/T2DMap.cpp +++ b/src/T2DMap.cpp @@ -3320,6 +3320,7 @@ void T2DMap::slot_createRoom() isCenterViewCall = true; mpMap->updateArea(mAreaID); isCenterViewCall = false; + emit mpMap->signal_roomsChanged(); mpMap->setUnsaved(__func__); } @@ -4106,6 +4107,7 @@ void T2DMap::slot_deleteRoom() mMultiSelectionListWidget.clear(); mMultiSelectionListWidget.hide(); repaint(); + emit mpMap->signal_roomsChanged(); mpMap->setUnsaved(__func__); } diff --git a/src/TMap.h b/src/TMap.h index 90ed91e92ef..9eabe9e33a2 100644 --- a/src/TMap.h +++ b/src/TMap.h @@ -76,6 +76,7 @@ class TMap : public QObject signals: void signal_saveErrorChanged(bool hasError); void signal_areaChanged(int areaId); + void signal_roomsChanged(); private: QString mDefaultAreaName; diff --git a/src/dlgConfigureAreas.cpp b/src/dlgConfigureAreas.cpp index bcea5f5be3d..502edb0646f 100644 --- a/src/dlgConfigureAreas.cpp +++ b/src/dlgConfigureAreas.cpp @@ -37,6 +37,9 @@ enum Columns dlgConfigureAreas::dlgConfigureAreas(TMap* map, QWidget* parent) : QDialog(parent), mpMap(map) { setupUi(this); + if(mpMap) { + connect(mpMap, &TMap::signal_roomsChanged, this, &dlgConfigureAreas::populateAreaList); + } areaTable->setSelectionBehavior(QAbstractItemView::SelectRows); areaTable->setSelectionMode(QAbstractItemView::SingleSelection); From e08df2e6234dd31f86ae6b6b2b7b91f1e4490eea Mon Sep 17 00:00:00 2001 From: Lychee Lynx <248896295+lychee-lynx@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:02:56 +0530 Subject: [PATCH 4/4] Fix: Table resizing when adding or deleting rooms --- src/dlgConfigureAreas.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dlgConfigureAreas.cpp b/src/dlgConfigureAreas.cpp index 502edb0646f..7bdbe8dc797 100644 --- a/src/dlgConfigureAreas.cpp +++ b/src/dlgConfigureAreas.cpp @@ -117,8 +117,6 @@ void dlgConfigureAreas::populateAreaList() areaTable->setItem(row, ColRoomCount, new QTableWidgetItem(QString::number(roomCount))); ++row; } - - areaTable->resizeColumnsToContents(); } void dlgConfigureAreas::slot_addArea()