diff --git a/IWin/IWin.h b/IWin/IWin.h index a67e64e..5d68bdf 100644 --- a/IWin/IWin.h +++ b/IWin/IWin.h @@ -56,7 +56,7 @@ public: protected: explicit IWin(Type type, IWin* parentWindow = nullptr); - void closeEvent(QCloseEvent *) override; + void closeEvent(QCloseEvent*) override; private: Type m_type; diff --git a/IWin/IWinStatus.h b/IWin/IWinStatus.h index b8bb5ff..abd90d2 100644 --- a/IWin/IWinStatus.h +++ b/IWin/IWinStatus.h @@ -51,6 +51,7 @@ public: IWin* getActiveWindow(); QList subWindows() const; IRC& getConnection() { return connection; } + const IRC& getConnection() const { return connection; } ILineEdit& getInputBox() { return *input; } private: diff --git a/IdealIRC/ConfigMgr.cpp b/IdealIRC/ConfigMgr.cpp index 17a4c5a..0e3936a 100644 --- a/IdealIRC/ConfigMgr.cpp +++ b/IdealIRC/ConfigMgr.cpp @@ -146,6 +146,13 @@ QString ConfigMgr::color(const QString& key) const return QString::fromStdString( ini.read("Color", keystdstr, defaultValue) ); } +QString ConfigMgr::window(const QString& key) const +{ + const auto keystdstr = key.toStdString(); + const auto& defaultValue = getDefault(defaultColor, keystdstr); + return QString::fromStdString( ini.read("Window", keystdstr, defaultValue) ); +} + std::optional ConfigMgr::prefixColor(const QChar& prefix) const { const std::string key = std::to_string(prefix.toLatin1()); @@ -214,6 +221,11 @@ void ConfigMgr::setCommon(const QString& key, const QString& value) ini.write("Common", key.toStdString(), value.toStdString()); } +void ConfigMgr::setWindow(const QString& key, const QString& value) +{ + ini.write("Window", key.toStdString(), value.toStdString()); +} + void ConfigMgr::setLogging(const QString& key, const QString& value) { ini.write("Logging", key.toStdString(), value.toStdString()); diff --git a/IdealIRC/ConfigMgr.h b/IdealIRC/ConfigMgr.h index 2065480..dd69d30 100644 --- a/IdealIRC/ConfigMgr.h +++ b/IdealIRC/ConfigMgr.h @@ -22,6 +22,7 @@ public: QString connection(const QString& key) const; QString common(const QString& key) const; QString color(const QString& key) const; + QString window(const QString& key) const; std::optional prefixColor(const QChar& prefix) const; QHash color() const; QVector> prefixColor() const; // QVector of pairs preserves the order. @@ -31,6 +32,7 @@ public: void setGeometry(const QString& key, const QString& value); void setConnection(const QString& key, const QString& value); void setCommon(const QString& key, const QString& value); + void setWindow(const QString& key, const QString& value); void setLogging(const QString& key, const QString& value); void setColorPalette(const QHash& palette); void setPrefixColorPalette(const QVector>& palette); diff --git a/IdealIRC/IdealIRC.cpp b/IdealIRC/IdealIRC.cpp index bb8a7a3..ffcc16c 100644 --- a/IdealIRC/IdealIRC.cpp +++ b/IdealIRC/IdealIRC.cpp @@ -80,6 +80,43 @@ IdealIRC::~IdealIRC() delete ui; } +void IdealIRC::saveSubwindowGeometries() +{ + for (auto* mdiwin : mdiManager->mdiChildren()) { + IWin* subwin = dynamic_cast(mdiwin->widget()); + if ((subwin->getType() == IWin::Type::Channel || subwin->getType() == IWin::Type::Private) + && !mdiwin->isMaximized()) + { + + QString network; + try { + const auto* status = dynamic_cast(subwin->getStatusParent()); + network = QString::fromStdString( status->getConnection().isupport().at("NETWORK") ); + } + catch (...) {} + + QString persistentWindowName; + if (network.isEmpty()) + persistentWindowName = subwin->getButtonText(); + else + persistentWindowName = QStringLiteral("%1_%2") + .arg(network) + .arg(subwin->getButtonText()); + + const auto pos = mdiwin->pos(); + const auto size = mdiwin->size(); + const auto geo = QStringLiteral("%1,%2,%3,%4") + .arg(pos.x()) + .arg(pos.y()) + .arg(size.width()) + .arg(size.height()); + + auto& conf = ConfigMgr::instance(); + conf.setWindow(persistentWindowName, geo); + } + } +} + void IdealIRC::closeEvent(QCloseEvent* evt) { ConfigMgr& conf = ConfigMgr::instance(); @@ -137,6 +174,7 @@ void IdealIRC::closeEvent(QCloseEvent* evt) evt->ignore(); } else { + saveSubwindowGeometries(); ConfigMgr::instance().save(); ScriptManager::event("exit"); ScriptManager::instance()->unloadAllScripts(); diff --git a/IdealIRC/IdealIRC.h b/IdealIRC/IdealIRC.h index 40bf3b0..e8ce783 100644 --- a/IdealIRC/IdealIRC.h +++ b/IdealIRC/IdealIRC.h @@ -53,6 +53,7 @@ private: Disconnect }; + void saveSubwindowGeometries(); void closeEvent(QCloseEvent* evt); void startup(); void subwindowSwitched(); diff --git a/IdealIRC/MdiManager.cpp b/IdealIRC/MdiManager.cpp index edd9820..2491743 100644 --- a/IdealIRC/MdiManager.cpp +++ b/IdealIRC/MdiManager.cpp @@ -118,7 +118,19 @@ IWin* MdiManager::createSubwindow(IWin* parent, const QString& buttonText, IWin: mdiwin->setAttribute(Qt::WA_DeleteOnClose); m_bbMgr.addButton(basePtr, mdiwin); - QRect spawnCoord = generateSpawnCoordinates(); + QString networkKey, windowKey; + if (windowType == IWin::Type::Channel || windowType == IWin::Type::Private) { + const auto& connection = dynamic_cast(parent)->getConnection(); + + try { + networkKey = QString::fromStdString(connection.isupport().at("NETWORK")); + } + catch (...) {} + + windowKey = buttonText; + } + + QRect spawnCoord = generateSpawnCoordinates(networkKey, windowKey); mdiwin->setGeometry(spawnCoord); qInfo() << "Created a subwindow of type" << IWin::TypeString[windowType] << "with button text" << buttonText; @@ -209,7 +221,7 @@ int MdiManager::connectionsOnlineCount() const IWin* subwin = dynamic_cast(mdiwin->widget()); if (subwin->getType() != IWin::Type::Status) continue; - IWinStatus* status = dynamic_cast(subwin); + auto* status = dynamic_cast(subwin); if (status->getConnection().isOnline()) ++c; } @@ -220,18 +232,19 @@ void MdiManager::broadcastProgramExit() { for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) { IWin* subwin = dynamic_cast(mdiwin->widget()); - if (subwin->getType() != IWin::Type::Status) - continue; - IWinStatus* status = dynamic_cast(subwin); - if (status->getConnection().isOnline()) { - connect(&status->getConnection(), &IRC::readyForExit, [this](){ - if (connectionsOnlineCount() == 0) - emit readyForExit(); - }); - - ConfigMgr& conf = ConfigMgr::instance(); - status->getConnection().disconnectForExit(conf.common("QuitMessage")); - } + if (subwin->getType() == IWin::Type::Status) { + auto* status = dynamic_cast(subwin); + if (status->getConnection().isOnline()) { + connect(&status->getConnection(), &IRC::readyForExit, + [this]() { + if (connectionsOnlineCount() == 0) + emit readyForExit(); + }); + + ConfigMgr& conf = ConfigMgr::instance(); + status->getConnection().disconnectForExit(conf.common("QuitMessage")); + } + } } } @@ -321,19 +334,64 @@ void MdiManager::subwinActivated(QMdiSubWindow* window) emit subwindowSwitched(); } -QRect MdiManager::generateSpawnCoordinates() +QRect MdiManager::generateSpawnCoordinates(const QString& networkKey, const QString& windowKey) { - QRect rectangle(nextXY, nextXY, WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT); - nextXY += WINDOW_STEP; - - if (nextXY > WINDOW_STEP_RESET_POSITION) { - nextXYadjust += WINDOW_STEP_RESET_ADJUST; - if (nextXYadjust > WINDOW_STEP_RESET_POSITION) - nextXYadjust = 0; - - nextXY = nextXYadjust; - } + auto generate = [this] { + QRect rectangle(nextXY, nextXY, WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT); + nextXY += WINDOW_STEP; + + if (nextXY > WINDOW_STEP_RESET_POSITION) { + nextXYadjust += WINDOW_STEP_RESET_ADJUST; + if (nextXYadjust > WINDOW_STEP_RESET_POSITION) + nextXYadjust = 0; + + nextXY = nextXYadjust; + } + + return rectangle; + }; + + if (windowKey.isEmpty()) { + return generate(); + } + else { + auto persistentKey = QStringLiteral("%1_%2") + .arg(networkKey) + .arg(windowKey); + if (networkKey.isEmpty()) + persistentKey = windowKey; + + const auto& conf = ConfigMgr::instance(); + auto geometryString = conf.window(persistentKey); + if (geometryString.isEmpty()) { + /* + * If the key (persistentKey) doesn't exist, check config if any window name that isn't + * tied to any network name exist, and use that. + */ + geometryString = conf.window(windowKey); + if (geometryString.isEmpty()) + return generate(); + } + + QStringList geometry = geometryString.split(','); + if (geometry.count() < 4) { + qWarning() << "Configuration for subwindow geometry '" << persistentKey << "' is invalid, resolving using generic positioning algorithm."; + return generate(); + } + + const int X = geometry[0].toInt(); + const int Y = geometry[1].toInt(); + const int W = geometry[2].toInt(); + const int H = geometry[3].toInt(); + return { X, Y, W, H }; + } +} - return rectangle; +QList MdiManager::mdiChildren() const +{ + QList ret; + for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) + ret << mdiwin; + return ret; } diff --git a/IdealIRC/MdiManager.h b/IdealIRC/MdiManager.h index 11d329c..ae5ae75 100644 --- a/IdealIRC/MdiManager.h +++ b/IdealIRC/MdiManager.h @@ -50,8 +50,9 @@ public: IWin* findWindow(IWin* statusParent, const QString& buttonText); QMdiSubWindow* toMdiwin(IWin* win); QList mdiChildrenOf(const IWin* statusParent, IWin::Type type = IWin::Type::Undefined) const; - QList childrenOf(const IWin* statusParent, IWin::Type type = IWin::Type::Undefined) const; - void showTrayInfo(const QString& title, const QString& message); + QList childrenOf(const IWin* statusParent, IWin::Type type = IWin::Type::Undefined) const; + QList mdiChildren() const; + void showTrayInfo(const QString& title, const QString& message); void showTrayWarn(const QString& title, const QString& message); int connectionsOnlineCount() const; void broadcastProgramExit(); @@ -88,7 +89,7 @@ private: int nextXY{ 0 }; int nextXYadjust{ 0 }; - QRect generateSpawnCoordinates(); + QRect generateSpawnCoordinates(const QString& networkKey, const QString& windowKey); static MdiManager* m_instance;