/***************************************************************************
 *   Copyright (C) 2008-2013 by Heiko Koehn - KoehnHeiko@googlemail.com    *
 *   Copyright (C) 2013-2016 by Stephen Lyons - slysven@virginmedia.com    *
 *   Copyright (C) 2014 by Ahmed Charles - acharles@outlook.com            *
 *                                                                         *
 *   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 "T2DMap.h"


#include "Host.h"
#include "TArea.h"
#include "TConsole.h"
#include "TEvent.h"
#include "TMap.h"
#include "TRoom.h"
#include "TRoomDB.h"
#include "dlgMapper.h"
#include "dlgRoomExits.h"

#include "pre_guard.h"
#include <QtEvents>
#include <QtUiTools>
#include <QAction>
#include <QCheckBox>
#include <QColorDialog>
#include <QComboBox>
#include <QDir>
#include <QElapsedTimer>
#include <QFileDialog>
#include <QFontDialog>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QListWidget>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QPixmap>
#include <QPushButton>
#include <QSignalMapper>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include "post_guard.h"


T2DMap::T2DMap(QWidget* parent)
: QWidget(parent)
, mDialogLock(false)
, mMultiSelectionListWidget(this)
, mMultiSelectionHighlightRoomId(-1)
, mIsSelectionSorting(true)
, mIsSelectionSortByNames(false)
, mIsSelectionUsingNames(false)
, mpMap()
, _rx()
, _ry()
, mTX()
, mTY()
, mChosenRoomColor()
, xspan()
, yspan()
, mMultiSelection()
, mPopupMenu()
, mRID()
, mAID()
, mOx()
, mOy()
, mOz()
, arealist_combobox()
, mpCurrentLineStyle()
, mpCurrentLineColor()
, mpCurrentLineArrow()
, mCurrentLineArrow()
, mShowGrid()
{
    mMultiSelectionListWidget.setColumnCount(2);
    mMultiSelectionListWidget.hideColumn(1);
    QStringList headerLabels;
    headerLabels << tr("Room Id") << tr("Room Name");
    mMultiSelectionListWidget.setHeaderLabels(headerLabels);
    mMultiSelectionListWidget.setToolTip(
            tr("<html><head/><body><p>Click on a line to select or deselect that room number (with the given name if the rooms are named) to add or remove the room from the selection.  Click on the "
               "relevant header to sort by that method.  Note that the name column willl only show if at least one of the rooms has a name.</p></body></html>"));
    mMultiSelectionListWidget.setUniformRowHeights(true);
    mMultiSelectionListWidget.setItemsExpandable(false);
    mMultiSelectionListWidget.setSelectionMode(QAbstractItemView::MultiSelection); // Was ExtendedSelection
    mMultiSelectionListWidget.setRootIsDecorated(false);
    QSizePolicy multiSelectionSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
    mMultiSelectionListWidget.setSizePolicy(multiSelectionSizePolicy);
    mMultiSelectionListWidget.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
    mMultiSelectionListWidget.setFrameShape(QFrame::NoFrame);
    mMultiSelectionListWidget.setFrameShadow(QFrame::Plain);
    mMultiSelectionListWidget.header()->setProperty("showSortIndicator", QVariant(true));
    mMultiSelectionListWidget.header()->setSectionsMovable(false);
    mMultiSelectionListWidget.header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    mMultiSelectionListWidget.header()->setStretchLastSection(true);
    mMultiSelectionListWidget.setSortingEnabled(mIsSelectionSorting);
    mMultiSelectionListWidget.resize(120, 100);
    mMultiSelectionListWidget.move(0, 0);
    mMultiSelectionListWidget.hide();
    connect(&mMultiSelectionListWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slot_roomSelectionChanged()));
    mMoveLabel = false;
    mLabelHilite = false;
    xzoom = 20;
    yzoom = 20;
    gzoom = 20;
    mPick = false;
    mTarget = 0;
    //mRoomSelection = 0;
    mStartSpeedWalk = false;
    mRoomBeingMoved = false;
    mPHighlightMove = QPoint(width() / 2, height() / 2);
    mNewMoveAction = false;
    mFontHeight = 20;
    mShowRoomID = false;
    rSize = 0.5;
    eSize = 3.0;
    mShiftMode = false;
    mShowInfo = true;
    mBubbleMode = false;
    mMapperUseAntiAlias = true;
    mMoveLabel = false;
    mCustomLineSelectedRoom = 0;
    mCustomLineSelectedExit = "";
    mCustomLineSelectedPoint = -1;
    mCustomLinesRoomFrom = 0;
    mCustomLinesRoomTo = 0;
    mSizeLabel = false;
    gridMapSizeChange = true;
    isCenterViewCall = false;
    mCurrentLineColor = QColor(255, 255, 100);
    //setFocusPolicy( Qt::ClickFocus);
}

void T2DMap::init()
{
    isCenterViewCall = false;
    QTime _time;
    _time.start();
    //setFocusPolicy( Qt::ClickFocus);

    if (!mpMap) {
        return;
    }
    gridMapSizeChange = false;
    eSize = mpMap->mpHost->mLineSize;
    rSize = mpMap->mpHost->mRoomSize;

    mMapperUseAntiAlias = mpHost->mMapperUseAntiAlias;
    mPixMap.clear();
    QFont f = QFont(QFont("Bitstream Vera Sans Mono", 20, QFont::Normal)); //( QFont("Monospace", 10, QFont::Courier) );
    f.setPointSize(gzoom);
    f.setBold(true);
    int j = 0;
    for (int k = 1; k < 17; k++) {
        for (int i = 1; i < 255; i++) {
            j++;
            QPixmap b(gzoom, gzoom);
            b.fill(Qt::transparent);
            QPainter p(&b);
            QColor c = _getColor(k);
            p.setPen(c);
            p.setFont(f);
            QRect r(0, 0, gzoom, gzoom);
            p.drawText(r, Qt::AlignHCenter | Qt::AlignVCenter, QChar(i));
            mPixMap[j] = b;
        }
    }
    mCurrentLineArrow = true;
    mCurrentLineColor = QColor("red");
    mCurrentLineStyle = QString("solid line"); // Configure custom line drawing starting points here
}

QColor T2DMap::_getColor(int id)
{
    QColor c;

    switch (id) {
    case 1:
        c = mpHost->mRed_2;
        break;

    case 2:
        c = mpHost->mGreen_2;
        break;
    case 3:
        c = mpHost->mYellow_2;
        break;

    case 4:
        c = mpHost->mBlue_2;
        break;

    case 5:
        c = mpHost->mMagenta_2;
        break;
    case 6:
        c = mpHost->mCyan_2;
        break;
    case 7:
        c = mpHost->mWhite_2;
        break;
    case 8:
        c = mpHost->mBlack_2;
        break;

    case 9:
        c = mpHost->mLightRed_2;
        break;

    case 10:
        c = mpHost->mLightGreen_2;
        break;
    case 11:
        c = mpHost->mLightYellow_2;
        break;

    case 12:
        c = mpHost->mLightBlue_2;
        break;

    case 13:
        c = mpHost->mLightMagenta_2;
        break;
    case 14:
        c = mpHost->mLightCyan_2;
        break;
    case 15:
        c = mpHost->mLightWhite_2;
        break;
    case 16:
        c = mpHost->mLightBlack_2;
    }
    return c;
}


QColor T2DMap::getColor(int id)
{
    QColor c;

    TRoom* pR = mpMap->mpRoomDB->getRoom(id);
    if (!pR) {
        return c;
    }

    int env = pR->environment;
    if (mpMap->envColors.contains(env)) {
        env = mpMap->envColors[env];
    } else {
        if (!mpMap->customEnvColors.contains(env)) {
            env = 1;
        }
    }
    switch (env) {
    case 1:
        c = mpHost->mRed_2;
        break;

    case 2:
        c = mpHost->mGreen_2;
        break;
    case 3:
        c = mpHost->mYellow_2;
        break;

    case 4:
        c = mpHost->mBlue_2;
        break;

    case 5:
        c = mpHost->mMagenta_2;
        break;
    case 6:
        c = mpHost->mCyan_2;
        break;
    case 7:
        c = mpHost->mWhite_2;
        break;
    case 8:
        c = mpHost->mBlack_2;
        break;

    case 9:
        c = mpHost->mLightRed_2;
        break;

    case 10:
        c = mpHost->mLightGreen_2;
        break;
    case 11:
        c = mpHost->mLightYellow_2;
        break;

    case 12:
        c = mpHost->mLightBlue_2;
        break;

    case 13:
        c = mpHost->mLightMagenta_2;
        break;
    case 14:
        c = mpHost->mLightCyan_2;
        break;
    case 15:
        c = mpHost->mLightWhite_2;
        break;
    case 16:
        c = mpHost->mLightBlack_2;
        break;
    default: //user defined room color
        if (!mpMap->customEnvColors.contains(env)) {
            break;
        }
        c = mpMap->customEnvColors[env];
    }
    return c;
}

void T2DMap::shiftDown()
{
    mShiftMode = true;
    mOy--;
    update();
}

void T2DMap::toggleShiftMode()
{
    mShiftMode = !mShiftMode;
    update();
}

void T2DMap::shiftUp()
{
    mShiftMode = true;
    mOy++;
    update();
}

void T2DMap::shiftLeft()
{
    mShiftMode = true;
    mOx--;
    update();
}

void T2DMap::shiftRight()
{
    mShiftMode = true;
    mOx++;
    update();
}
void T2DMap::shiftZup()
{
    mShiftMode = true;
    mOz++;
    update();
}

void T2DMap::shiftZdown()
{
    mShiftMode = true;
    mOz--;
    update();
}


void T2DMap::slot_switchArea(QString name)
{
    Host* pHost = mpHost;
    if (!pHost || !mpMap) {
        return;
    }

    int playerRoomId = mpMap->mRoomIdHash.value(pHost->getName());
    TRoom* pPlayerRoom = mpMap->mpRoomDB->getRoom(playerRoomId);
    int playerAreaId = -2; // Cannot be valid (but -1 can be)!
    if (pPlayerRoom) {
        playerAreaId = pPlayerRoom->getArea();
    }

    QMapIterator<int, QString> it(mpMap->mpRoomDB->getAreaNamesMap());
    while (it.hasNext()) {
        it.next();
        int areaID = it.key();

        QString _n = it.value();
        TArea* pA = mpMap->mpRoomDB->getArea(areaID);
        if (name == _n && pA) {
            mAID = areaID;
            mShiftMode = true;
            pA->calcSpan();

            if (areaID == playerAreaId) {
                // We are switching back to the area that has the player in it
                // recenter view on that room!
                mOx = pPlayerRoom->x;
                mOy = -pPlayerRoom->y; // Map y coordinates are reversed on 2D map!
                mOz = pPlayerRoom->z;
                repaint();
                mpMap->set3DViewCenter(mAID, mOx, -mOy, mOz); // Pass the coordinates to the TMap instance to pass to the 3D mapper
                return;                                       // escape early
            }

            bool isAValidRoomFound = false;
            if (!pA->ebenen.contains(mOz)) {
                // If the current map z-coordinate value is NOT one that is used
                // for this then get the FIRST room in the area and goto the
                // mathmatical midpoint of all the rooms on the same
                // z-coordinate.
                QSetIterator<int> itRoom(pA->getAreaRooms());
                QMap<int, int> roomsCountLevelMap; // key is z-coordinate, value is count of rooms on that level
                while (itRoom.hasNext()) {
                    int checkRoomId = itRoom.next();
                    TRoom* pR = mpMap->mpRoomDB->getRoom(checkRoomId);
                    if (pR) {
                        isAValidRoomFound = true;
                        if (roomsCountLevelMap.contains(pR->z)) {
                            ++roomsCountLevelMap[pR->z];
                        } else {
                            roomsCountLevelMap[pR->z] = 1;
                        }
                    }
                }

                if (isAValidRoomFound) {
                    QMapIterator<int, int> itRoomsCount(roomsCountLevelMap);
                    itRoomsCount.toBack();   // Start at highest value and work down
                    itRoomsCount.previous(); // This will be Okay as we KNOW there is at least one entry
                    int maxRoomCountOnLevel = 0;
                    int minLevelWithMaxRoomCount = itRoomsCount.key(); // Initalisation value, will get overwritten
                    itRoomsCount.next();                               // Return to the back so the previous() in the do loop works correctly
                    do {
                        itRoomsCount.previous();
                        if (maxRoomCountOnLevel < itRoomsCount.value()) {
                            maxRoomCountOnLevel = itRoomsCount.value();
                            minLevelWithMaxRoomCount = itRoomsCount.key();
                        }
                    } while (itRoomsCount.hasPrevious());

                    // We now have lowest level with the highest number of rooms
                    // Now find the geometry center of the rooms on THAT level
                    // In a similar manner to the getCenterSelection() method
                    itRoom.toFront();
                    float mean_x = 0.0;
                    float mean_y = 0.0;
                    uint processedRoomCount = 0;
                    QSet<TRoom*> pSRoom; // Hold on to relevent rooms for
                                         // following step
                    while (itRoom.hasNext()) {
                        TRoom* pR = mpMap->mpRoomDB->getRoom(itRoom.next());
                        if (!pR || pR->z != minLevelWithMaxRoomCount) {
                            continue;
                        }

                        pSRoom.insert(pR);
                        mean_x += (static_cast<float>(pR->x - mean_x)) / ++processedRoomCount;
                        mean_y += (static_cast<float>(pR->y - mean_y)) / processedRoomCount;
                    }

                    // We now have the position that is the "centre" of the
                    // rooms on this level - just need to find the room nearest
                    // to that:
                    QSetIterator<TRoom*> itpRoom(pSRoom);
                    float closestSquareDistance = -1.0;
                    TRoom* pClosestRoom = nullptr;
                    while (itpRoom.hasNext()) {
                        TRoom* pR = itpRoom.next();
                        QVector2D meanToRoom(static_cast<float>(pR->x) - mean_x, static_cast<float>(pR->y) - mean_y);
                        if (closestSquareDistance < -0.5) {
                            // Test for first time around loop - for initalisation
                            // Don't use an equality to zero test, we are using floats so
                            // need to allow for a little bit of fuzzzyness!
                            closestSquareDistance = meanToRoom.lengthSquared();
                            pClosestRoom = pR;
                        } else {
                            float currentRoomSquareDistance = meanToRoom.lengthSquared();
                            if (closestSquareDistance > currentRoomSquareDistance) {
                                closestSquareDistance = currentRoomSquareDistance;
                                pClosestRoom = pR;
                            }
                        }
                    }

                    mOx = pClosestRoom->x;
                    mOy = -pClosestRoom->y; // Map y coordinates are reversed on 2D map!
                    mOz = pClosestRoom->z;
                }

                if (!isAValidRoomFound) {
                    //no rooms, go to 0,0,0
                    mOx = 0;
                    mOy = 0;
                    mOz = 0;
                }
            } else {
                // Else the selected area DOES have rooms on the same z-coordinate
                // Now find the geometry center of the rooms on the given level
                // In a similar manner to the getCenterSelection() method
                float mean_x = 0.0;
                float mean_y = 0.0;
                uint processedRoomCount = 0;
                QSet<TRoom*> pSRoom; // Hold on to relevent rooms for
                                     // following step
                QSetIterator<int> itRoom(pA->getAreaRooms());
                while (itRoom.hasNext()) {
                    TRoom* pR = mpMap->mpRoomDB->getRoom(itRoom.next());
                    if (!pR || pR->z != mOz) {
                        continue;
                    }

                    pSRoom.insert(pR);
                    mean_x += (static_cast<float>(pR->x - mean_x)) / ++processedRoomCount;
                    mean_y += (static_cast<float>(pR->y - mean_y)) / processedRoomCount;
                }

                // We now have the position that is the "centre" of the
                // rooms on this level - just need to find the room nearest
                // to that:
                QSetIterator<TRoom*> itpRoom(pSRoom);
                float closestSquareDistance = -1.0;
                TRoom* pClosestRoom = nullptr;
                while (itpRoom.hasNext()) {
                    TRoom* pR = itpRoom.next();
                    QVector2D meanToRoom(static_cast<float>(pR->x) - mean_x, static_cast<float>(pR->y) - mean_y);
                    if (closestSquareDistance < -0.5) {
                        // Test for first time around loop - for initalisation
                        // Don't use an equality to zero test, we are using floats so
                        // need to allow for a little bit of fuzzzyness!
                        closestSquareDistance = meanToRoom.lengthSquared();
                        pClosestRoom = pR;
                    } else {
                        float currentRoomSquareDistance = meanToRoom.lengthSquared();
                        if (closestSquareDistance > currentRoomSquareDistance) {
                            closestSquareDistance = currentRoomSquareDistance;
                            pClosestRoom = pR;
                        }
                    }
                }

                if (pClosestRoom) {
                    mOx = pClosestRoom->x;
                    mOy = -pClosestRoom->y; // Map y coordinates are reversed on 2D map!
                }
            }
            repaint();
            mpMap->set3DViewCenter(mAID, mOx, -mOy, mOz); // Pass the coordinates to the TMap instance to pass to the 3D mapper
            return;
        }
    }
}


void T2DMap::paintEvent(QPaintEvent* e)
{
    if (!mpMap) {
        return;
    }
    bool __Pick = mPick;
    QElapsedTimer __time;
    __time.start();

    QPainter p(this);
    if (!p.isActive()) {
        return;
    }

    mAreaExitList.clear();

    float _w = width();
    float _h = height();

    if (_w < 10 || _h < 10) {
        return;
    }

    if (_w > _h) {
        xspan = xzoom * (_w / _h);
        yspan = xzoom;
    } else {
        xspan = yzoom;
        yspan = yzoom * (_h / _w);
    }

    float tx = _w / xspan;
    float ty = _h / yspan;

    mTX = tx;
    mTY = ty;


    int px, py;
    QList<int> exitList;
    QList<int> oneWayExits;
    TRoom* pPlayerRoom = mpMap->mpRoomDB->getRoom(mpMap->mRoomIdHash.value(mpHost->getName()));
    if (!pPlayerRoom) {
        p.drawText(_w / 2, _h / 2, "No map or no valid position.");

        return;
    }

    int ox, oy; // N/U: oz;
    if (mRID != mpMap->mRoomIdHash.value(mpHost->getName()) && mShiftMode) {
        mShiftMode = false;
    }
    TArea* pAID;
    TRoom* pRID;
    int playerArea = pPlayerRoom->getArea();
    if ((!__Pick && !mShiftMode) || mpMap->mNewMove) {
        mShiftMode = true;
        mpMap->mNewMove =
                false; // das ist nur hier von Interesse, weil es nur hier einen map editor gibt -> map wird unter Umstaenden nicht geupdated, deshalb force ich mit mNewRoom ein map update bei centerview()

        if (!mpMap->mpRoomDB->getArea(playerArea)) {
            return;
        }
        mRID = mpMap->mRoomIdHash.value(mpHost->getName());
        pRID = mpMap->mpRoomDB->getRoom(mRID);
        mAID = pRID->getArea();
        pAID = mpMap->mpRoomDB->getArea(mAID);
        if (!pRID || !pAID) {
            return;
        }
        ox = pRID->x;
        oy = pRID->y * -1;
        mOx = ox;
        mOy = oy;
        mOz = pRID->z;
    } else {
        pRID = mpMap->mpRoomDB->getRoom(mRID);
        pAID = mpMap->mpRoomDB->getArea(mAID);
        if (!pRID || !pAID) {
            return;
        }
        ox = mOx;
        oy = mOy;
    }
    if (ox * tx > xspan / 2 * tx) {
        _rx = -(tx * ox - xspan / 2 * tx);
    } else {
        _rx = xspan / 2 * tx - tx * ox;
    }
    if (oy * ty > yspan / 2 * ty) {
        _ry = -(ty * oy - yspan / 2 * ty);
    } else {
        _ry = yspan / 2 * ty - ty * oy;
    }

    px = ox * tx + _rx;
    py = oy * ty + _ry;

    TArea* pArea = pAID;

    int zEbene;
    zEbene = mOz;

    float wegBreite = 1 / eSize * tx * rSize;

    p.fillRect(0, 0, width(), height(), mpHost->mBgColor_2);

    QPen pen;

    pen = p.pen();
    pen.setColor(mpHost->mFgColor_2);
    pen.setWidthF(wegBreite);
    if (mMapperUseAntiAlias) {
        p.setRenderHint(QPainter::Antialiasing);
    } else {
        p.setRenderHint(QPainter::NonCosmeticDefaultPen);
    }
    p.setPen(pen);

    if (mpMap->mapLabels.contains(mAID)) {
        QMapIterator<int, TMapLabel> it(mpMap->mapLabels[mAID]);
        while (it.hasNext()) {
            it.next();
            if (it.value().pos.z() != mOz) {
                continue;
            }
            if (it.value().text.length() < 1) {
                mpMap->mapLabels[mAID][it.key()].text = "no text";
            }
            QPointF lpos;
            int _lx = it.value().pos.x() * tx + _rx;
            int _ly = it.value().pos.y() * ty * -1 + _ry;

            lpos.setX(_lx);
            lpos.setY(_ly);
            int _lw = abs(it.value().size.width()) * tx;
            int _lh = abs(it.value().size.height()) * ty;
            if (!((0 < _lx || 0 < _lx + _lw) && (_w > _lx || _w > _lx + _lw))) {
                continue;
            }
            if (!((0 < _ly || 0 < _ly + _lh) && (_h > _ly || _h > _ly + _lh))) {
                continue;
            }
            QRectF _drawRect = QRect(it.value().pos.x() * tx + _rx, it.value().pos.y() * ty * -1 + _ry, _lw, _lh);
            if (!it.value().showOnTop) {
                if (!it.value().noScaling) {
                    p.drawPixmap(lpos, it.value().pix.scaled(_drawRect.size().toSize()));
                    mpMap->mapLabels[mAID][it.key()].clickSize.setWidth(_drawRect.width());
                    mpMap->mapLabels[mAID][it.key()].clickSize.setHeight(_drawRect.height());
                } else {
                    p.drawPixmap(lpos, it.value().pix);
                    mpMap->mapLabels[mAID][it.key()].clickSize.setWidth(it.value().pix.width());
                    mpMap->mapLabels[mAID][it.key()].clickSize.setHeight(it.value().pix.height());
                }
            }

            if (it.value().hilite) {
                _drawRect.setSize(it.value().clickSize);
                p.fillRect(_drawRect, QColor(255, 155, 55, 190));
            }
        }
    }

    if (!pArea->gridMode) {
        int customLineDestinationTarget = 0;
        if (mCustomLinesRoomTo > 0) {
            customLineDestinationTarget = mCustomLinesRoomTo;
        } else if (mCustomLineSelectedRoom > 0 && !mCustomLineSelectedExit.isEmpty()) {
            TRoom* pSR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
            if (pSR) {
                if (mCustomLineSelectedExit == "NW" || mCustomLineSelectedExit == "nw") {
                    customLineDestinationTarget = pSR->getNorthwest();
                } else if (mCustomLineSelectedExit == "N" || mCustomLineSelectedExit == "n") {
                    customLineDestinationTarget = pSR->getNorth();
                } else if (mCustomLineSelectedExit == "NE" || mCustomLineSelectedExit == "ne") {
                    customLineDestinationTarget = pSR->getNortheast();
                } else if (mCustomLineSelectedExit == "UP" || mCustomLineSelectedExit == "up") {
                    customLineDestinationTarget = pSR->getUp();
                } else if (mCustomLineSelectedExit == "W" || mCustomLineSelectedExit == "w") {
                    customLineDestinationTarget = pSR->getWest();
                } else if (mCustomLineSelectedExit == "E" || mCustomLineSelectedExit == "e") {
                    customLineDestinationTarget = pSR->getEast();
                } else if (mCustomLineSelectedExit == "DOWN" || mCustomLineSelectedExit == "down") {
                    customLineDestinationTarget = pSR->getDown();
                } else if (mCustomLineSelectedExit == "SW" || mCustomLineSelectedExit == "sw") {
                    customLineDestinationTarget = pSR->getSouthwest();
                } else if (mCustomLineSelectedExit == "S" || mCustomLineSelectedExit == "s") {
                    customLineDestinationTarget = pSR->getSouth();
                } else if (mCustomLineSelectedExit == "SE" || mCustomLineSelectedExit == "se") {
                    customLineDestinationTarget = pSR->getSoutheast();
                } else if (mCustomLineSelectedExit == "IN" || mCustomLineSelectedExit == "in") {
                    customLineDestinationTarget = pSR->getIn();
                } else if (mCustomLineSelectedExit == "OUT" || mCustomLineSelectedExit == "out") {
                    customLineDestinationTarget = pSR->getOut();
                } else {
                    QMultiMap<int, QString> otherExits = pSR->getOtherMap();
                    QMapIterator<int, QString> otherExitIt = otherExits;
                    while (otherExitIt.hasNext()) {
                        otherExitIt.next();
                        if (otherExitIt.value().startsWith("0") || otherExitIt.value().startsWith("1")) {
                            if (otherExitIt.value().mid(1) == mCustomLineSelectedExit) {
                                customLineDestinationTarget = otherExitIt.key();
                                break;
                            }
                        } else if (otherExitIt.value() == mCustomLineSelectedExit) {
                            customLineDestinationTarget = otherExitIt.key();
                            break;
                        }
                    }
                }
            }
        }
        QSetIterator<int> itRoom2(pArea->getAreaRooms());
        while (itRoom2.hasNext()) {
            int _id = itRoom2.next();
            TRoom* pR = mpMap->mpRoomDB->getRoom(_id);
            if (!pR) {
                continue;
            }
            float rx = pR->x * tx + _rx;
            float ry = pR->y * -1 * ty + _ry;
            int rz = pR->z;

            if (rz != zEbene) {
                continue;
            }

            if (pR->customLines.size() == 0) {
                if (rx < 0 || ry < 0 || rx > _w || ry > _h) {
                    continue;
                }
            } else {
                float miny = pR->min_y * -1 * ty + (float)_ry;
                float maxy = pR->max_y * -1 * ty + (float)_ry;
                float minx = pR->min_x * tx + (float)_rx;
                float maxx = pR->max_x * tx + (float)_rx;

                if (!((minx > 0.0 || maxx > 0.0) && ((float)_w > minx || (float)_w > maxx))) {
                    continue;
                }
                if (!((miny > 0.0 || maxy > 0.0) && ((float)_h > miny || (float)_h > maxy))) {
                    continue;
                }
            }

            pR->rendered = true;

            exitList.clear();
            oneWayExits.clear();
            if (pR->customLines.size() > 0) {
                if (!pR->customLines.contains("N")) {
                    exitList.push_back(pR->getNorth());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getNorth());
                    if (pER) {
                        if (pER->getSouth() != _id) {
                            oneWayExits.push_back(pR->getNorth());
                        }
                    }
                }
                if (!pR->customLines.contains("NW")) {
                    exitList.push_back(pR->getNorthwest());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getNorthwest());
                    if (pER) {
                        if (pER->getSoutheast() != _id) {
                            oneWayExits.push_back(pR->getNorthwest());
                        }
                    }
                }
                if (!pR->customLines.contains("E")) {
                    exitList.push_back(pR->getEast());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getEast());
                    if (pER) {
                        if (pER->getWest() != _id) {
                            oneWayExits.push_back(pR->getEast());
                        }
                    }
                }
                if (!pR->customLines.contains("SE")) {
                    exitList.push_back(pR->getSoutheast());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getSoutheast());
                    if (pER) {
                        if (pER->getNorthwest() != _id) {
                            oneWayExits.push_back(pR->getSoutheast());
                        }
                    }
                }
                if (!pR->customLines.contains("S")) {
                    exitList.push_back(pR->getSouth());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getSouth());
                    if (pER) {
                        if (pER->getNorth() != _id) {
                            oneWayExits.push_back(pR->getSouth());
                        }
                    }
                }
                if (!pR->customLines.contains("SW")) {
                    exitList.push_back(pR->getSouthwest());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getSouthwest());
                    if (pER) {
                        if (pER->getNortheast() != _id) {
                            oneWayExits.push_back(pR->getSouthwest());
                        }
                    }
                }
                if (!pR->customLines.contains("W")) {
                    exitList.push_back(pR->getWest());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getWest());
                    if (pER) {
                        if (pER->getEast() != _id) {
                            oneWayExits.push_back(pR->getWest());
                        }
                    }
                }
                if (!pR->customLines.contains("NE")) {
                    exitList.push_back(pR->getNortheast());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getNortheast());
                    if (pER) {
                        if (pER->getSouthwest() != _id) {
                            oneWayExits.push_back(pR->getNortheast());
                        }
                    }
                }
            } else {
                if (pR->getNorth() > 0) {
                    exitList.push_back(pR->getNorth());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getNorth());
                    if (pER) {
                        if (pER->getSouth() != _id) {
                            oneWayExits.push_back(pR->getNorth());
                        }
                    }
                }
                if (pR->getNorthwest() > 0) {
                    exitList.push_back(pR->getNorthwest());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getNorthwest());
                    if (pER) {
                        if (pER->getSoutheast() != _id) {
                            oneWayExits.push_back(pR->getNorthwest());
                        }
                    }
                }
                if (pR->getEast() > 0) {
                    exitList.push_back(pR->getEast());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getEast());
                    if (pER) {
                        if (pER->getWest() != _id) {
                            oneWayExits.push_back(pR->getEast());
                        }
                    }
                }
                if (pR->getSoutheast() > 0) {
                    exitList.push_back(pR->getSoutheast());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getSoutheast());
                    if (pER) {
                        if (pER->getNorthwest() != _id) {
                            oneWayExits.push_back(pR->getSoutheast());
                        }
                    }
                }
                if (pR->getSouth() > 0) {
                    exitList.push_back(pR->getSouth());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getSouth());
                    if (pER) {
                        if (pER->getNorth() != _id) {
                            oneWayExits.push_back(pR->getSouth());
                        }
                    }
                }
                if (pR->getSouthwest() > 0) {
                    exitList.push_back(pR->getSouthwest());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getSouthwest());
                    if (pER) {
                        if (pER->getNortheast() != _id) {
                            oneWayExits.push_back(pR->getSouthwest());
                        }
                    }
                }
                if (pR->getWest() > 0) {
                    exitList.push_back(pR->getWest());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getWest());
                    if (pER) {
                        if (pER->getEast() != _id) {
                            oneWayExits.push_back(pR->getWest());
                        }
                    }
                }
                if (pR->getNortheast() > 0) {
                    exitList.push_back(pR->getNortheast());
                    TRoom* pER = mpMap->mpRoomDB->getRoom(pR->getNortheast());
                    if (pER) {
                        if (pER->getSouthwest() != _id) {
                            oneWayExits.push_back(pR->getNortheast());
                        }
                    }
                }
            }

            if (pR->customLines.size() > 0) {
                QPen oldPen = p.pen();
                QMapIterator<QString, QList<QPointF>> itk(pR->customLines);
                while (itk.hasNext()) {
                    itk.next();
                    QColor _color;
                    if (_id == mCustomLineSelectedRoom && itk.key() == mCustomLineSelectedExit) {
                        _color.setRed(255);
                        _color.setGreen(155);
                        _color.setBlue(55);
                    } else if (pR->customLinesColor[itk.key()].size() == 3) {
                        _color.setRed(pR->customLinesColor[itk.key()][0]);
                        _color.setGreen(pR->customLinesColor[itk.key()][1]);
                        _color.setBlue(pR->customLinesColor[itk.key()][2]);
                    } else {
                        _color = QColor(Qt::red);
                    }
                    bool _arrow = pR->customLinesArrow[itk.key()];
                    QString _style = pR->customLinesStyle[itk.key()];
                    QPointF _cstartP;
                    float ex = pR->x * tx + _rx;
                    float ey = pR->y * ty * -1 + _ry;
                    if (itk.key() == "N") {
                        _cstartP = QPoint(ex, ey - ty / 2);
                    } else if (itk.key() == "NW") {
                        _cstartP = QPoint(ex - tx / 2, ey - ty / 2);
                    } else if (itk.key() == "NE") {
                        _cstartP = QPoint(ex + tx / 2, ey - ty / 2);
                    } else if (itk.key() == "S") {
                        _cstartP = QPoint(ex, ey + ty / 2);
                    } else if (itk.key() == "SW") {
                        _cstartP = QPoint(ex - tx / 2, ey + ty / 2);
                    } else if (itk.key() == "SE") {
                        _cstartP = QPoint(ex + tx / 2, ey + ty / 2);
                    } else if (itk.key() == "W") {
                        _cstartP = QPoint(ex - tx / 2, ey);
                    } else if (itk.key() == "E") {
                        _cstartP = QPoint(ex + tx / 2, ey);
                    } else {
                        _cstartP = QPointF(ex, ey);
                    }
                    QPointF ursprung = QPointF(ex, ey);
                    QPen customLinePen = p.pen();
                    customLinePen.setCosmetic(mMapperUseAntiAlias);
                    customLinePen.setColor(_color);
                    customLinePen.setCapStyle(Qt::RoundCap);
                    customLinePen.setJoinStyle(Qt::RoundJoin);

                    if (_style == "solid line") {
                        customLinePen.setStyle(Qt::SolidLine);
                    } else if (_style == "dot line") {
                        customLinePen.setStyle(Qt::DotLine);
                    } else if (_style == "dash line") {
                        customLinePen.setStyle(Qt::DashLine);
                    } else if (_style == "dash dot line") {
                        customLinePen.setStyle(Qt::DashDotLine);
                    } else {
                        customLinePen.setStyle(Qt::DashDotDotLine);
                    }

                    QList<QPointF> _pL = itk.value();
                    if (_pL.size() > 0) {
                        p.setPen(customLinePen);
                        p.drawLine(ursprung, _cstartP);
                    }
                    for (int pk = 0; pk < _pL.size(); pk++) {
                        QPointF _cendP;
                        _cendP.setX(_pL[pk].x() * tx + _rx);
                        _cendP.setY(_pL[pk].y() * ty * -1 + _ry);
                        p.drawLine(_cstartP, _cendP);

                        if (_id == mCustomLineSelectedRoom && itk.key() == mCustomLineSelectedExit) {
                            QPen _savedPen = p.pen();
                            QPen _pen;
                            QBrush _brush = p.brush();
                            if (pk == mCustomLineSelectedPoint) {
                                _pen = QPen(QColor(255, 255, 55), _savedPen.width(), Qt::SolidLine, Qt::FlatCap, _savedPen.joinStyle()); // Draw the selected point in yellow not orange.
                            } else {
                                _pen = QPen(_savedPen.color(), _savedPen.width(), Qt::SolidLine, Qt::FlatCap, _savedPen.joinStyle());
                            }
                            p.setBrush(Qt::NoBrush); // Draw hollow circles not default filled ones!
                            p.setPen(_pen);
                            p.drawEllipse(_cendP, mTX / 4, mTX / 4);
                            p.setPen(_savedPen);
                            p.setBrush(_brush);
                        }

                        if (pk == _pL.size() - 1 && _arrow) {
                            QLineF l0 = QLineF(_cendP, _cstartP);
                            l0.setLength(wegBreite * 5);
                            QPointF _p1 = l0.p1();
                            QPointF _p2 = l0.p2();
                            QLineF l1 = QLineF(l0);
                            qreal w1 = l1.angle() - 90.0;
                            QLineF l2;
                            l2.setP1(_p2);
                            l2.setAngle(w1);
                            l2.setLength(wegBreite * 2);
                            QPointF _p3 = l2.p2();
                            l2.setAngle(l2.angle() + 180.0);
                            QPointF _p4 = l2.p2();
                            QPolygonF _poly;
                            _poly.append(_p1);
                            _poly.append(_p3);
                            _poly.append(_p4);
                            QBrush brush = p.brush();
                            brush.setColor(_color);
                            brush.setStyle(Qt::SolidPattern);
                            QPen arrowPen = p.pen();
                            arrowPen.setCosmetic(mMapperUseAntiAlias);
                            arrowPen.setStyle(Qt::SolidLine);
                            p.setPen(arrowPen);
                            p.setBrush(brush);
                            p.drawPolygon(_poly);
                        }
                        _cstartP = _cendP;
                    }
                }
                p.setPen(oldPen);
            }

            // N/U:             int e = pR->z;

            // draw exit stubs
            QMap<int, QVector3D> unitVectors = mpMap->unitVectors;
            for (int direction : pR->exitStubs) {
                QVector3D uDirection = unitVectors[direction];
                p.drawLine(rx + rSize * (int)uDirection.x() / 2, ry + rSize * (int)uDirection.y(), rx + (int)uDirection.x() * (rSize * 3 / 4 * tx), ry + uDirection.y() * (rSize * 3 / 4 * ty));
            }

            QPen __pen;
            for (int& k : exitList) {
                int rID = k;
                if (rID <= 0) {
                    continue;
                }

                bool areaExit;

                TRoom* pE = mpMap->mpRoomDB->getRoom(rID);
                if (!pE) {
                    continue;
                }

                if (pE->getArea() != mAID) {
                    areaExit = true;
                } else {
                    areaExit = false;
                }
                float ex = pE->x * tx + _rx;
                float ey = pE->y * ty * -1 + _ry;
                int ez = pE->z;

                QVector3D p1(ex, ey, ez);
                QVector3D p2(rx, ry, rz);
                QLine _line;
                if (!areaExit) {
                    // one way exit or 2 way exit?
                    if (!oneWayExits.contains(rID)) {
                        p.drawLine((int)p1.x(), (int)p1.y(), (int)p2.x(), (int)p2.y());
                    } else {
                        // one way exit draw arrow

                        QLineF l0 = QLineF(p2.x(), p2.y(), p1.x(), p1.y());
                        QLineF k0 = l0;
                        k0.setLength((l0.length() - wegBreite * 5) * 0.5);
                        qreal dx = k0.dx();
                        qreal dy = k0.dy();
                        QPen _tp = p.pen();
                        QPen _tp2 = _tp;
                        _tp2.setStyle(Qt::DotLine);
                        p.setPen(_tp2);
                        p.drawLine(l0);
                        p.setPen(_tp);
                        l0.setLength(wegBreite * 5);
                        QPointF _p1 = l0.p2();
                        QPointF _p2 = l0.p1();
                        QLineF l1 = QLineF(l0);
                        qreal w1 = l1.angle() - 90.0;
                        QLineF l2;
                        l2.setP1(_p2);
                        l2.setAngle(w1);
                        l2.setLength(wegBreite * 2);
                        QPointF _p3 = l2.p2();
                        l2.setAngle(l2.angle() + 180.0);
                        QPointF _p4 = l2.p2();
                        QPolygonF _poly;
                        _poly.append(_p1);
                        _poly.append(_p3);
                        _poly.append(_p4);

                        QBrush brush = p.brush();
                        brush.setColor(QColor(255, 100, 100));
                        brush.setStyle(Qt::SolidPattern);
                        QPen arrowPen = p.pen();
                        arrowPen.setCosmetic(mMapperUseAntiAlias);
                        arrowPen.setStyle(Qt::SolidLine);
                        p.setPen(arrowPen);
                        p.setBrush(brush);
                        p.drawPolygon(_poly.translated(dx, dy));
                    }

                } else {
                    __pen = p.pen();
                    QPoint _p;
                    pen = p.pen();
                    pen.setWidthF(wegBreite);
                    pen.setCosmetic(mMapperUseAntiAlias);
                    pen.setColor(getColor(k));
                    p.setPen(pen);
                    if (pR->getSouth() == rID) {
                        _line = QLine(p2.x(), p2.y() + ty, p2.x(), p2.y());
                        _p = QPoint(p2.x(), p2.y() + ty / 2);
                    } else if (pR->getNorth() == rID) {
                        _line = QLine(p2.x(), p2.y() - ty, p2.x(), p2.y());
                        _p = QPoint(p2.x(), p2.y() - ty / 2);
                    } else if (pR->getWest() == rID) {
                        _line = QLine(p2.x() - tx, p2.y(), p2.x(), p2.y());
                        _p = QPoint(p2.x() - tx / 2, p2.y());
                    } else if (pR->getEast() == rID) {
                        _line = QLine(p2.x() + tx, p2.y(), p2.x(), p2.y());
                        _p = QPoint(p2.x() + tx / 2, p2.y());
                    } else if (pR->getNorthwest() == rID) {
                        _line = QLine(p2.x() - tx, p2.y() - ty, p2.x(), p2.y());
                        _p = QPoint(p2.x() - tx / 2, p2.y() - ty / 2);
                    } else if (pR->getNortheast() == rID) {
                        _line = QLine(p2.x() + tx, p2.y() - ty, p2.x(), p2.y());
                        _p = QPoint(p2.x() + tx / 2, p2.y() - ty / 2);
                    } else if (pR->getSoutheast() == rID) {
                        _line = QLine(p2.x() + tx, p2.y() + ty, p2.x(), p2.y());
                        _p = QPoint(p2.x() + tx / 2, p2.y() + ty / 2);
                    } else if (pR->getSouthwest() == rID) {
                        _line = QLine(p2.x() - tx, p2.y() + ty, p2.x(), p2.y());
                        _p = QPoint(p2.x() - tx / 2, p2.y() + ty / 2);
                    }
                    p.drawLine(_line);
                    mAreaExitList[k] = _p;
                    QLineF l0 = QLineF(_line);
                    l0.setLength(wegBreite * 5);
                    QPointF _p1 = l0.p1();
                    QPointF _p2 = l0.p2();
                    QLineF l1 = QLineF(l0);
                    qreal w1 = l1.angle() - 90.0;
                    QLineF l2;
                    l2.setP1(_p2);
                    l2.setAngle(w1);
                    l2.setLength(wegBreite * 2);
                    QPointF _p3 = l2.p2();
                    l2.setAngle(l2.angle() + 180.0);
                    QPointF _p4 = l2.p2();
                    QPolygonF _poly;
                    _poly.append(_p1);
                    _poly.append(_p3);
                    _poly.append(_p4);
                    QBrush brush = p.brush();
                    brush.setColor(getColor(k));
                    brush.setStyle(Qt::SolidPattern);
                    QPen arrowPen = p.pen();
                    arrowPen.setCosmetic(mMapperUseAntiAlias);
                    p.setPen(arrowPen);
                    p.setBrush(brush);
                    p.drawPolygon(_poly);
                    p.setPen(__pen);
                }
                // doors
                if (pR->doors.size() > 0) {
                    int doorStatus = 0;
                    if (pR->getSouth() == rID && pR->doors.contains("s")) {
                        doorStatus = pR->doors["s"];
                    } else if (pR->getNorth() == rID && pR->doors.contains("n")) {
                        doorStatus = pR->doors["n"];
                    } else if (pR->getSouthwest() == rID && pR->doors.contains("sw")) {
                        doorStatus = pR->doors["sw"];
                    } else if (pR->getSoutheast() == rID && pR->doors.contains("se")) {
                        doorStatus = pR->doors["se"];
                    } else if (pR->getNortheast() == rID && pR->doors.contains("ne")) {
                        doorStatus = pR->doors["ne"];
                    } else if (pR->getNorthwest() == rID && pR->doors.contains("nw")) {
                        doorStatus = pR->doors["nw"];
                    } else if (pR->getWest() == rID && pR->doors.contains("w")) {
                        doorStatus = pR->doors["w"];
                    } else if (pR->getEast() == rID && pR->doors.contains("e")) {
                        doorStatus = pR->doors["e"];
                    }
                    if (doorStatus > 0) {
                        QLineF k0;
                        QRectF rect;
                        rect.setWidth(0.25 * mTX);
                        rect.setHeight(0.25 * mTY);
                        if (areaExit) {
                            k0 = QLineF(_line);
                        } else {
                            k0 = QLineF(p2.x(), p2.y(), p1.x(), p1.y());
                        }
                        k0.setLength((k0.length()) * 0.5);
                        rect.moveCenter(k0.p2());
                        QPen arrowPen = p.pen();
                        QPen _tp = p.pen();
                        arrowPen.setCosmetic(mMapperUseAntiAlias);
                        arrowPen.setStyle(Qt::SolidLine);
                        if (doorStatus == 1) { //open door
                            arrowPen.setColor(QColor(10, 155, 10));
                        } else if (doorStatus == 2) { //closed door
                            arrowPen.setColor(QColor(155, 155, 10));
                        } else { //locked door
                            arrowPen.setColor(QColor(155, 10, 10));
                        }
                        QBrush brush;
                        QBrush oldBrush;
                        p.setPen(arrowPen);
                        p.setBrush(brush);
                        p.drawRect(rect);
                        p.setBrush(oldBrush);
                        p.setPen(_tp);
                    }
                }
            } // End of for( exitList )

            // Indicate destination for custom exit line drawing - double size
            // target yellow hollow circle
            // Similar code is now also used to indicate center target of
            // multiple selected rooms but that must be done after all the rooms
            // have been drawn otherwise later drawn rooms will overwrite the
            // mark, especially on areas in gridmode.
            if (customLineDestinationTarget > 0 && customLineDestinationTarget == _id) {
                QPen savePen = p.pen();
                QBrush saveBrush = p.brush();
                float _radius = tx * 1.2;
                float _diagonal = tx * 1.2;
                QPointF _center = QPointF(rx, ry);

                QPen myPen(QColor(255, 255, 50, 192)); // Quarter opaque yellow pen
                myPen.setWidth(tx * 0.1);
                QPainterPath myPath;
                p.setPen(myPen);
                p.setBrush(Qt::NoBrush);
                myPath.addEllipse(_center, _radius, _radius);
                myPath.addEllipse(_center, _radius / 2.0, _radius / 2.0);
                myPath.moveTo(rx - _diagonal, ry - _diagonal);
                myPath.lineTo(rx + _diagonal, ry + _diagonal);
                myPath.moveTo(rx + _diagonal, ry - _diagonal);
                myPath.lineTo(rx - _diagonal, ry + _diagonal);
                p.drawPath(myPath);
                p.setPen(savePen);
                p.setBrush(saveBrush);
            }
        } // End of for( area Rooms )
    }     // End of NOT area gridmode

    // Draw label sizing or group selection box
    if (mSizeLabel) {
        p.fillRect(mMultiRect, QColor(250, 190, 0, 190));
    } else {
        p.fillRect(mMultiRect, QColor(190, 190, 190, 60));
    }

    QSetIterator<int> itRoom(pArea->getAreaRooms());
    while (itRoom.hasNext()) {
        int currentAreaRoom = itRoom.next();
        TRoom* pR = mpMap->mpRoomDB->getRoom(currentAreaRoom);
        if (!pR) {
            continue; // Was missing this safety step to skip missing rooms
        }
        float rx = pR->x * tx + _rx;
        float ry = pR->y * -1 * ty + _ry;
        int rz = pR->z;

        if (rz != zEbene) {
            continue;
        }
        if (rx < 0 || ry < 0 || rx > _w || ry > _h) {
            continue;
        }

        pR->rendered = false;
        QRectF dr;
        if (pArea->gridMode) {
            dr = QRectF(rx - tx / 2, ry - ty / 2, tx, ty);
        } else {
            dr = QRectF(rx - (tx * rSize) / 2, ry - (ty * rSize) / 2, tx * rSize, ty * rSize);
        }

        QColor c;
        int env = pR->environment;
        if (mpMap->envColors.contains(env)) {
            env = mpMap->envColors[env];
        } else {
            if (!mpMap->customEnvColors.contains(env)) {
                env = 1;
            }
        }
        switch (env) {
        case 1:
            c = mpHost->mRed_2;
            break;

        case 2:
            c = mpHost->mGreen_2;
            break;
        case 3:
            c = mpHost->mYellow_2;
            break;

        case 4:
            c = mpHost->mBlue_2;
            break;

        case 5:
            c = mpHost->mMagenta_2;
            break;
        case 6:
            c = mpHost->mCyan_2;
            break;
        case 7:
            c = mpHost->mWhite_2;
            break;
        case 8:
            c = mpHost->mBlack_2;
            break;

        case 9:
            c = mpHost->mLightRed_2;
            break;

        case 10:
            c = mpHost->mLightGreen_2;
            break;
        case 11:
            c = mpHost->mLightYellow_2;
            break;

        case 12:
            c = mpHost->mLightBlue_2;
            break;

        case 13:
            c = mpHost->mLightMagenta_2;
            break;
        case 14:
            c = mpHost->mLightCyan_2;
            break;
        case 15:
            c = mpHost->mLightWhite_2;
            break;
        case 16:
            c = mpHost->mLightBlack_2;
            break;
        default: //user defined room color
            if (mpMap->customEnvColors.contains(env)) {
                c = mpMap->customEnvColors[env];
            }
        }
        if (((mPick || __Pick) && mPHighlight.x() >= dr.x() - (tx * rSize) && mPHighlight.x() <= dr.x() + (tx * rSize) && mPHighlight.y() >= dr.y() - (ty * rSize)
             && mPHighlight.y() <= dr.y() + (ty * rSize))
            || mMultiSelectionSet.contains(currentAreaRoom)) {
            p.fillRect(dr, QColor(255, 155, 55));
            mPick = false;
            if (mStartSpeedWalk) {
                mStartSpeedWalk = false;
                float _radius = (0.8 * tx) / 2;
                QPointF _center = QPointF(rx, ry);
                QRadialGradient _gradient(_center, _radius);
                _gradient.setColorAt(0.95, QColor(255, 0, 0, 150));
                _gradient.setColorAt(0.80, QColor(150, 100, 100, 150));
                _gradient.setColorAt(0.799, QColor(150, 100, 100, 100));
                _gradient.setColorAt(0.7, QColor(255, 0, 0, 200));
                _gradient.setColorAt(0, QColor(255, 255, 255, 255));
                QPen myPen(Qt::transparent);
                QPainterPath myPath;
                p.setBrush(_gradient);
                p.setPen(myPen);
                myPath.addEllipse(_center, _radius, _radius);
                p.drawPath(myPath);


                mTarget = currentAreaRoom;
                if (mpMap->mpRoomDB->getRoom(mTarget)) {
                    mpMap->mTargetID = mTarget;
                    if (mpMap->findPath(mpMap->mRoomIdHash.value(mpHost->getName()), mpMap->mTargetID)) {
                        mpHost->startSpeedWalk();
                    } else {
                        QString msg = "Mapper: Cannot find a path to this room using known exits.\n";
                        mpHost->mpConsole->printSystemMessage(msg);
                    }
                }
            }
        } else {
            char _ch = pR->c;
            if (_ch >= 33 /* && _ch < 255 seems that _ch is a signed char so will always be less than 255 */) {
                int _color = (265 - 257) * 254 + _ch; //(mpMap->rooms[pArea->rooms[i]]->environment - 257 ) * 254 + _ch;

                if (c.red() + c.green() + c.blue() > 260) {
                    _color = (7) * 254 + _ch;
                } else {
                    _color = (6) * 254 + _ch;
                }

                p.fillRect(dr, c);
                if (mPixMap.contains(_color)) {
                    QPixmap pix = mPixMap[_color].scaled(dr.width(), dr.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
                    p.drawPixmap(dr.topLeft(), pix);
                }
            } else {
                if (mBubbleMode) {
                    float _radius = (rSize * tx) / 2;
                    QPointF _center = QPointF(rx, ry);
                    QRadialGradient _gradient(_center, _radius);
                    _gradient.setColorAt(0.85, c);
                    _gradient.setColorAt(0, QColor(255, 255, 255, 255));
                    QPen myPen(Qt::transparent);
                    QPainterPath myPath;
                    p.setBrush(_gradient);
                    p.setPen(myPen);
                    myPath.addEllipse(_center, _radius, _radius);
                    p.drawPath(myPath);
                } else {
                    p.fillRect(dr, c);
                }
            }

            if (pR->highlight) {
                float _radius = (pR->highlightRadius * tx) / 2;
                QPointF _center = QPointF(rx, ry);
                QRadialGradient _gradient(_center, _radius);
                _gradient.setColorAt(0.85, pR->highlightColor);
                _gradient.setColorAt(0, pR->highlightColor2);
                QPen myPen(Qt::transparent);
                QPainterPath myPath;
                p.setBrush(_gradient);
                p.setPen(myPen);
                myPath.addEllipse(_center, _radius, _radius);
                p.drawPath(myPath);
            }

            if (mShowRoomID) {
                QPen __pen = p.pen();
                QColor lc;
                if (c.red() + c.green() + c.blue() > 200) {
                    lc = QColor(Qt::black);
                } else {
                    lc = QColor(Qt::white);
                }
                p.setPen(QPen(lc));
                p.drawText(dr, Qt::AlignHCenter | Qt::AlignVCenter, QString::number(currentAreaRoom));
                p.setPen(__pen);
            }

            if (mShiftMode && currentAreaRoom == mpMap->mRoomIdHash.value(mpHost->getName())) {
                float _radius = (1.2 * tx) / 2;
                QPointF _center = QPointF(rx, ry);
                QRadialGradient _gradient(_center, _radius);
                _gradient.setColorAt(0.95, QColor(255, 0, 0, 150));
                _gradient.setColorAt(0.80, QColor(150, 100, 100, 150));
                _gradient.setColorAt(0.799, QColor(150, 100, 100, 100));
                _gradient.setColorAt(0.7, QColor(255, 0, 0, 200));
                _gradient.setColorAt(0, QColor(255, 255, 255, 255));
                QPen myPen(Qt::transparent);
                QPainterPath myPath;
                p.setBrush(_gradient);
                p.setPen(myPen);
                myPath.addEllipse(_center, _radius, _radius);
                p.drawPath(myPath);
            }
        }

        QColor lc;
        if (c.red() + c.green() + c.blue() > 200) {
            lc = QColor(Qt::black);
        } else {
            lc = QColor(Qt::white);
        }
        pen = p.pen();
        pen.setColor(lc);
        pen.setWidthF(0); //wegBreite?);
        pen.setCosmetic(mMapperUseAntiAlias);
        pen.setCapStyle(Qt::RoundCap);
        pen.setJoinStyle(Qt::RoundJoin);
        p.setPen(pen);

        //FIXME: redo exit stubs here since the room will draw over up/down stubs -- its repetitive though
        QMap<int, QVector3D> unitVectors = mpMap->unitVectors;
        for (int k = 0; k < pR->exitStubs.size(); k++) {
            int direction = pR->exitStubs[k];
            QVector3D uDirection = unitVectors[direction];
            if (direction > 8) {
                float rx = pR->x * tx + _rx;
                float ry = pR->y * -1 * ty + _ry;
                QPolygonF _poly;
                QPointF _pt;
                _pt = QPointF(rx, ry + (ty * rSize) * uDirection.z() / 20);
                _poly.append(_pt);
                _pt = QPointF(rx + (tx * rSize) / 3.1, ry + (ty * rSize) * uDirection.z() / 3.1);
                _poly.append(_pt);
                _pt = QPointF(rx - (tx * rSize) / 3.1, ry + (ty * rSize) * uDirection.z() / 3.1);
                _poly.append(_pt);
                QBrush brush = p.brush();
                brush.setColor(Qt::black);
                brush.setStyle(Qt::NoBrush);
                p.setBrush(brush);
                p.drawPolygon(_poly);
            }
        }

        if (pR->getUp() > 0) {
            QPolygonF _poly;
            QPointF _pt;
            _pt = QPointF(rx, ry + (ty * rSize) / 20);
            _poly.append(_pt);
            _pt = QPointF(rx - (tx * rSize) / 3.1, ry + (ty * rSize) / 3.1);
            _poly.append(_pt);
            _pt = QPointF(rx + (tx * rSize) / 3.1, ry + (ty * rSize) / 3.1);
            _poly.append(_pt);
            QBrush brush = p.brush();
            brush.setColor(Qt::black);
            brush.setStyle(Qt::SolidPattern);
            p.setBrush(brush);
            p.drawPolygon(_poly);
        }

        if (pR->getDown() > 0) {
            QPolygonF _poly;
            QPointF _pt;
            _pt = QPointF(rx, ry - (ty * rSize) / 20);
            _poly.append(_pt);
            _pt = QPointF(rx - (tx * rSize) / 3.1, ry - (ty * rSize) / 3.1);
            _poly.append(_pt);
            _pt = QPointF(rx + (tx * rSize) / 3.1, ry - (ty * rSize) / 3.1);
            _poly.append(_pt);
            QBrush brush = p.brush();
            brush.setColor(Qt::black);
            brush.setStyle(Qt::SolidPattern);
            p.setBrush(brush);
            p.drawPolygon(_poly);
        }

        if (pR->getIn() > 0) {
            QPolygonF _poly;
            QPointF _pt;
            _pt = QPointF(rx + (tx * rSize) / 20, ry);
            _poly.append(_pt);
            _pt = QPointF(rx - (tx * rSize) / 3.1, ry - (ty * rSize) / 3.1);
            _poly.append(_pt);
            _pt = QPointF(rx - (tx * rSize) / 3.1, ry + (ty * rSize) / 3.1);
            _poly.append(_pt);
            QBrush brush = p.brush();
            brush.setColor(Qt::black);
            brush.setStyle(Qt::SolidPattern);
            p.setBrush(brush);
            p.drawPolygon(_poly);
        }

        if (pR->getOut() > 0) {
            QPolygonF _poly;
            QPointF _pt;
            _pt = QPointF(rx - (tx * rSize) / 20, ry);
            _poly.append(_pt);
            _pt = QPointF(rx + (tx * rSize) / 3.1, ry - (ty * rSize) / 3.1);
            _poly.append(_pt);
            _pt = QPointF(rx + (tx * rSize) / 3.1, ry + (ty * rSize) / 3.1);
            _poly.append(_pt);
            QBrush brush = p.brush();
            brush.setColor(Qt::black);
            brush.setStyle(Qt::SolidPattern);
            p.setBrush(brush);
            p.drawPolygon(_poly);
        }

        if (pArea->gridMode) {
            QMapIterator<int, QPoint> it(mAreaExitList);
            while (it.hasNext()) {
                it.next();
                QPoint P = it.value();
                int rx = P.x();
                int ry = P.y();

                QRectF dr = QRectF(rx - tx / 2, ry - ty / 2, tx, ty);
                if (((mPick || __Pick) && mPHighlight.x() >= dr.x() - tx / 2 && mPHighlight.x() <= dr.x() + tx / 2 && mPHighlight.y() >= dr.y() - ty / 2 && mPHighlight.y() <= dr.y() + ty / 2)
                    || mMultiSelectionSet.contains(currentAreaRoom)) {
                    p.fillRect(dr, QColor(50, 255, 50));
                    mPick = false;
                    mTarget = it.key();
                    if (mpMap->mpRoomDB->getRoom(mTarget)) {
                        mpMap->mTargetID = mTarget;
                        if (mpMap->findPath(mpMap->mRoomIdHash.value(mpHost->getName()), mpMap->mTargetID)) {
                            mpHost->startSpeedWalk();
                        } else {
                            QString msg = "Mapper: Cannot find a path to this room using known exits.\n";
                            mpHost->mpConsole->printSystemMessage(msg);
                        }
                    }
                }
            }
        } else {
            QMapIterator<int, QPoint> it(mAreaExitList);
            while (it.hasNext()) {
                it.next();
                QPoint P = it.value();
                int rx = P.x();
                int ry = P.y();

                QRectF dr = QRectF(rx, ry, tx * rSize, ty * rSize); //rx-(tx*rSize)/2,ry-(ty*rSize)/2,tx*rSize,ty*rSize);
                if (((mPick || __Pick) && mPHighlight.x() >= dr.x() - tx / 3 && mPHighlight.x() <= dr.x() + tx / 3 && mPHighlight.y() >= dr.y() - ty / 3 && mPHighlight.y() <= dr.y() + ty / 3)
                    && mStartSpeedWalk) {
                    mStartSpeedWalk = false;
                    float _radius = (0.8 * tx) / 2;
                    QPointF _center = QPointF(rx, ry);
                    QRadialGradient _gradient(_center, _radius);
                    _gradient.setColorAt(0.95, QColor(255, 0, 0, 150));
                    _gradient.setColorAt(0.80, QColor(150, 100, 100, 150));
                    _gradient.setColorAt(0.799, QColor(150, 100, 100, 100));
                    _gradient.setColorAt(0.7, QColor(255, 0, 0, 200));
                    _gradient.setColorAt(0, QColor(255, 255, 255, 255));
                    QPen myPen(Qt::transparent);
                    QPainterPath myPath;
                    p.setBrush(_gradient);
                    p.setPen(myPen);
                    myPath.addEllipse(_center, _radius, _radius);
                    p.drawPath(myPath);

                    mPick = false;
                    mTarget = it.key();
                    if (mpMap->mpRoomDB->getRoom(mTarget)) {
                        mpMap->mTargetID = mTarget;
                        if (mpMap->findPath(mpMap->mRoomIdHash.value(mpHost->getName()), mpMap->mTargetID)) {
                            mpMap->mpHost->startSpeedWalk();
                        } else {
                            QString msg = "Mapper: Cannot find a path to this room using known exits.\n";
                            mpHost->mpConsole->printSystemMessage(msg);
                        }
                    }
                }
            }
        }
    }

    if (mpMap->mapLabels.contains(mAID)) {
        QMapIterator<int, TMapLabel> it(mpMap->mapLabels[mAID]);
        while (it.hasNext()) {
            it.next();
            if (it.value().pos.z() != mOz) {
                continue;
            }
            if (it.value().text.length() < 1) {
                mpMap->mapLabels[mAID][it.key()].text = "no text";
            }
            QPointF lpos;
            int _lx = it.value().pos.x() * tx + _rx;
            int _ly = it.value().pos.y() * ty * -1 + _ry;

            lpos.setX(_lx);
            lpos.setY(_ly);
            int _lw = abs(it.value().size.width()) * tx;
            int _lh = abs(it.value().size.height()) * ty;

            if (!((0 < _lx || 0 < _lx + _lw) && (_w > _lx || _w > _lx + _lw))) {
                continue;
            }
            if (!((0 < _ly || 0 < _ly + _lh) && (_h > _ly || _h > _ly + _lh))) {
                continue;
            }
            QRectF _drawRect = QRect(it.value().pos.x() * tx + _rx, it.value().pos.y() * ty * -1 + _ry, _lw, _lh);
            if (it.value().showOnTop) {
                if (!it.value().noScaling) {
                    p.drawPixmap(lpos, it.value().pix.scaled(_drawRect.size().toSize()));
                    mpMap->mapLabels[mAID][it.key()].clickSize.setWidth(_drawRect.width());
                    mpMap->mapLabels[mAID][it.key()].clickSize.setHeight(_drawRect.height());
                } else {
                    p.drawPixmap(lpos, it.value().pix);
                    mpMap->mapLabels[mAID][it.key()].clickSize.setWidth(it.value().pix.width());
                    mpMap->mapLabels[mAID][it.key()].clickSize.setHeight(it.value().pix.height());
                }
            }
            if (it.value().hilite) {
                _drawRect.setSize(it.value().clickSize);
                p.fillRect(_drawRect, QColor(255, 155, 55, 190));
            }
        }
    }

    // Similar code was used to indicate target of custom exit line selected for
    // editing but this could not be done there because gridmode areas don't hit
    // that bit of code and later rooms would overwrite the target...
    if (mMultiSelectionHighlightRoomId > 0 && mMultiSelectionSet.size() > 1) {
        TRoom* pR_multiSelectionHighlight = mpMap->mpRoomDB->getRoom(mMultiSelectionHighlightRoomId);
        if (pR_multiSelectionHighlight) {
            float r_mSx = pR_multiSelectionHighlight->x * tx + _rx;
            float r_mSy = pR_multiSelectionHighlight->y * -1 * ty + _ry;
            QPen savePen = p.pen();
            QBrush saveBrush = p.brush();
            float _radius = tx * 1.2;
            float _diagonal = tx * 1.2;
            QPointF _center = QPointF(r_mSx, r_mSy);

            QPen myPen(QColor(255, 255, 50, 192)); // Quarter opaque yellow pen
            myPen.setWidth(tx * 0.1);
            QPainterPath myPath;
            p.setPen(myPen);
            p.setBrush(Qt::NoBrush);
            myPath.addEllipse(_center, _radius, _radius);
            myPath.addEllipse(_center, _radius / 2.0, _radius / 2.0);
            myPath.moveTo(r_mSx - _diagonal, r_mSy - _diagonal);
            myPath.lineTo(r_mSx + _diagonal, r_mSy + _diagonal);
            myPath.moveTo(r_mSx + _diagonal, r_mSy - _diagonal);
            myPath.lineTo(r_mSx - _diagonal, r_mSy + _diagonal);
            p.drawPath(myPath);
            p.setPen(savePen);
            p.setBrush(saveBrush);
        }
    }

    QColor infoColor;
    if (mpHost->mBgColor_2.lightness() > 127) {
        infoColor = QColor(Qt::black);
    } else {
        infoColor = QColor(Qt::white);
    }

    // Draw central red circle:
    if (!mShiftMode) {
        p.save();
        QPen myPen(Qt::transparent);
        QPainterPath myPath;
        if (mpHost->mMapStrongHighlight) { // Never set, no means to except via XMLImport, as dlgMapper class's
            // slot_toggleStrongHighlight is not wired up to anything
            QRectF dr = QRectF(px - (tx * rSize) / 2, py - (ty * rSize) / 2, tx * rSize, ty * rSize);
            p.fillRect(dr, QColor(255, 0, 0, 150));

            float _radius = (1.9 * tx) / 2;
            QPointF _center = QPointF(px, py);
            QRadialGradient _gradient(_center, _radius);
            _gradient.setColorAt(0.95, QColor(255, 0, 0, 150));
            _gradient.setColorAt(0.80, QColor(150, 100, 100, 150));
            _gradient.setColorAt(0.799, QColor(150, 100, 100, 100));
            _gradient.setColorAt(0.7, QColor(255, 0, 0, 200));
            _gradient.setColorAt(0, QColor(255, 255, 255, 255));
            p.setBrush(_gradient);
            p.setPen(myPen);
            myPath.addEllipse(_center, _radius, _radius);
        } else {
            float _radius = (1.9 * tx) / 2;
            QPointF _center = QPointF(px, py);
            QRadialGradient _gradient(_center, _radius);
            _gradient.setColorAt(0.95, QColor(255, 0, 0, 150));
            _gradient.setColorAt(0.80, QColor(150, 100, 100, 150));
            _gradient.setColorAt(0.799, QColor(150, 100, 100, 100));
            _gradient.setColorAt(0.3, QColor(150, 150, 150, 100));
            _gradient.setColorAt(0.1, QColor(255, 255, 255, 100));
            _gradient.setColorAt(0, QColor(255, 255, 255, 255));
            p.setBrush(_gradient);
            p.setPen(myPen);
            myPath.addEllipse(_center, _radius, _radius);
        }
        p.drawPath(myPath);
        p.restore();
    }


    // Work out text for information box, need to offset if room selection widget is present
    if (mShowInfo) {
        QString infoText;
        int __rid = mRID;
        if (!isCenterViewCall && mMultiSelectionSet.size()) {
            if (mpMap->mpRoomDB->getRoom(*(mMultiSelectionSet.constBegin()))) {
                __rid = mMultiSelectionHighlightRoomId;
            }
        }

        p.save(); // Save painter state
        QFont f = p.font();
        TRoom* _prid = mpMap->mpRoomDB->getRoom(__rid);
        if (_prid) {
            int _iaid = _prid->getArea();
            TArea* _paid = mpMap->mpRoomDB->getArea(_iaid);
            QString _paid_name = mpMap->mpRoomDB->getAreaNamesMap().value(_iaid);
            if (_paid) {
                infoText = tr("Area: %1 ID:%2 x:%3 <-> %4 y:%5 <-> %6 z:%7 <-> %8\n")
                                   .arg(_paid_name,
                                        QString::number(_iaid),
                                        QString::number(_paid->min_x),
                                        QString::number(_paid->max_x),
                                        QString::number(_paid->min_y),
                                        QString::number(_paid->max_y),
                                        QString::number(_paid->min_z),
                                        QString::number((_paid->max_z)));
            } else {
                infoText = QStringLiteral("\n");
            }

            if (!_prid->name.isEmpty()) {
                infoText.append(tr("Room Name: %1\n").arg(_prid->name));
            }

            uint selectionSize = mMultiSelectionSet.size();
            // Italicise the text if the current display area {mAID} is not the
            // same as the displayed text information - which happens when NO
            // room is selected AND the current area is NOT the one the player
            // is in (to emphasis that the displayed data is {mostly} not about
            // the CURRENTLY VISIBLE area)... make it bold if the player room IS
            // in the displayed map

            // If one or more rooms are selected - make the text slightly orange.
            switch (selectionSize) {
            case 0:
                infoText.append(tr("Room ID: %1 (Current) Position on Map: (%2,%3,%4)\n").arg(QString::number(__rid), QString::number(_prid->x), QString::number(_prid->y), QString::number(_prid->z)));
                if (playerArea != mAID) {
                    f.setItalic(true);
                } else {
                    f.setBold(true);
                }
                break;
            case 1:
                infoText.append(
                        tr("Room ID: %1 (Selected) Position on Map: (%2,%3,%4)\n").arg(QString::number(__rid), QString::number(_prid->x), QString::number(_prid->y), QString::number(_prid->z)));
                f.setBold(true);
                if (infoColor.lightness() > 127) {
                    infoColor = QColor(255, 223, 191); // Slightly orange white
                } else {
                    infoColor = QColor(96, 48, 0); // Dark, slightly orange grey
                }
                break;
            default:
                infoText.append(tr("Room ID: %1 (%5 Selected) Position on Map: (%2,%3,%4)\n")
                                        .arg(QString::number(__rid), QString::number(_prid->x), QString::number(_prid->y), QString::number(_prid->z), QString::number(selectionSize)));
                f.setBold(true);
                if (infoColor.lightness() > 127) {
                    infoColor = QColor(255, 223, 191); // Slightly orange white
                } else {
                    infoColor = QColor(96, 48, 0); // Dark, slightly orange grey
                }
                break;
            }
        }

        infoText.append(tr("render time: %1S mO: (%2,%3,%4)").arg(__time.nsecsElapsed() * 1.0e-9, 0, 'f', 3).arg(QString::number(mOx), QString::number(mOy), QString::number(mOz)));

        uint infoLeftSideAvoid = 10;                 // Left margin for info widget
        if (mMultiSelectionListWidget.isVisible()) { // Room Selection Widget showing, so increase margin to avoid
            infoLeftSideAvoid += mMultiSelectionListWidget.x() + mMultiSelectionListWidget.rect().width();
        }

        uint infoHeight = 5 + mFontHeight; // Account for first iteration
        QRect testRect;
        // infoRect has a 10 margin on either side and on top to widget frame.
        mMapInfoRect = QRect(infoLeftSideAvoid, 10, width() - 10 - infoLeftSideAvoid, infoHeight);
        do {
            infoHeight += mFontHeight;
            mMapInfoRect.setHeight(infoHeight);
            // Test in a rectangle that is 10 less on all sides:
            testRect =
                    p.boundingRect(mMapInfoRect.left() + 10, mMapInfoRect.top() + 10, mMapInfoRect.width() - 20, mMapInfoRect.height() - 20, Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop, infoText);
        } while ((testRect.height() > mMapInfoRect.height() - 20 || testRect.width() > mMapInfoRect.width() - 20)
                 && infoHeight < height()); // Last term is needed to prevent runaway under "odd" conditions

        p.fillRect(mMapInfoRect, QColor(150, 150, 150, 80)); // Restore Grey translucent background, was useful for debugging!
        p.setPen(infoColor);
        p.setFont(f);
        p.drawText(mMapInfoRect.left() + 10, mMapInfoRect.top() + 10, mMapInfoRect.width() - 20, mMapInfoRect.height() - 20, Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop, infoText);
        p.restore(); //forget about font size changing and bolding/italicisation
    }

    static bool isAreaWidgetValid = true; // Remember between uses
    QFont _f = mpMap->mpMapper->showArea->font();
    if (isAreaWidgetValid) {
        if (mAID == -1                                    // * the map being shown is the "default" area
            && !mpMap->mpMapper->getDefaultAreaShown()) { // * the area widget is not showing the "default" area

            isAreaWidgetValid = false; // So the widget CANNOT indicate the correct area
            // Set the area widget to indicate the area widget is NOT
            // showing valid text - so make it italic and crossed out
            _f.setItalic(true);
            _f.setUnderline(true);
            _f.setStrikeOut(true);
            _f.setOverline(true);
        }
    } else {
        if (!(mAID == -1 && !mpMap->mpMapper->getDefaultAreaShown())) {
            isAreaWidgetValid = true; // So the widget CAN now indicate the correct area
            // Reset to normal
            _f.setItalic(false);
            _f.setUnderline(false);
            _f.setStrikeOut(false);
            _f.setOverline(false);
        }
    }

    mpMap->mpMapper->showArea->setFont(_f);

    if (mHelpMsg.size() > 0) {
        p.setPen(QColor(255, 155, 50));
        QFont _f = p.font();
        QFont _f2 = _f;
        _f.setPointSize(12); // 20 was a little large
        _f.setBold(true);
        p.setFont(_f);
        QRect _r = QRect(0, 0, _w, _h);
        p.drawText(_r, Qt::AlignHCenter | Qt::AlignBottom | Qt::TextWordWrap, mHelpMsg);
        // Now draw text centered at bottom, so it does not clash with info window
        p.setFont(_f2);
    }
}


void T2DMap::mouseDoubleClickEvent(QMouseEvent* event)
{
    if (mDialogLock) {
        return;
    }
    int x = event->x();
    int y = event->y();
    mPHighlight = QPoint(x, y);
    mPick = true;
    mStartSpeedWalk = true;
    repaint();
}

void T2DMap::createLabel(QRectF labelRect)
{
    // N/U:     QRectF selectedRegion = labelRect;
    TMapLabel label;
    QFont _font;
    QString t = "no text";
    QString imagePath;

    mHelpMsg.clear();

    QMessageBox msgBox;
    msgBox.setText("Text label or image label?");
    QPushButton* textButton = msgBox.addButton(tr("Text Label"), QMessageBox::ActionRole);
    QPushButton* imageButton = msgBox.addButton(tr("Image Label"), QMessageBox::ActionRole);
    msgBox.setStandardButtons(QMessageBox::Cancel);
    msgBox.exec();
    if (msgBox.clickedButton() == textButton) {
        QString title = "Enter label text.";
        _font = QFontDialog::getFont(nullptr);
        t = QInputDialog::getText(nullptr, title, title);
        if (t.length() < 1) {
            t = "no text";
        }
        label.text = t;
        label.bgColor = QColorDialog::getColor(QColor(50, 50, 150, 100), nullptr, "Background color");
        label.fgColor = QColorDialog::getColor(QColor(255, 255, 50, 255), nullptr, "Foreground color");
    } else if (msgBox.clickedButton() == imageButton) {
        label.bgColor = QColor(50, 50, 150, 100);
        label.fgColor = QColor(255, 255, 50, 255);
        label.text = "";
        imagePath = QFileDialog::getOpenFileName(nullptr, "Select image");
    } else {
        return;
    }

    QMessageBox msgBox2;
    msgBox2.setStandardButtons(QMessageBox::Cancel);
    msgBox2.setText("Draw label as background or on top of everything?");
    QPushButton* textButton2 = msgBox2.addButton(tr("Background"), QMessageBox::ActionRole);
    QPushButton* imageButton2 = msgBox2.addButton(tr("Foreground"), QMessageBox::ActionRole);
    msgBox2.exec();
    bool showOnTop = false;
    if (msgBox2.clickedButton() == textButton2) {
        showOnTop = false;
    } else if (msgBox2.clickedButton() == imageButton2) {
        showOnTop = true;
    } else {
        return;
    }

    label.showOnTop = showOnTop;
    QPixmap pix(abs(labelRect.width()), abs(labelRect.height()));
    QRect drawRect = labelRect.normalized().toRect();
    drawRect.moveTo(0, 0);
    //pix.fill(QColor(0,255,0,0));
    QPainter lp(&pix);
    QPen lpen;
    lp.setFont(_font);
    lpen.setColor(label.fgColor);
    lp.setPen(lpen);
    lp.fillRect(drawRect, label.bgColor);
    if (msgBox.clickedButton() == textButton) {
        lp.drawText(drawRect, Qt::AlignHCenter | Qt::AlignCenter, t, nullptr);
    } else {
        QPixmap imagePixmap = QPixmap(imagePath);
        lp.drawPixmap(QPoint(0, 0), imagePixmap.scaled(drawRect.size()));
    }
    label.pix = pix.copy(drawRect);
    labelRect = labelRect.normalized();
    float mx = labelRect.topLeft().x() / mTX + mOx;
    float my = labelRect.topLeft().y() / mTY + mOy;
    mx = mx - xspan / 2;
    my = yspan / 2 - my;

    float mx2 = labelRect.bottomRight().x() / mTX + mOx;
    float my2 = labelRect.bottomRight().y() / mTY + mOy;
    mx2 = mx2 - xspan / 2;
    my2 = yspan / 2 - my2;
    label.pos = QVector3D(mx, my, mOz);
    label.size = QRectF(QPointF(mx, my), QPointF(mx2, my2)).normalized().size();
    if (!mpMap->mpRoomDB->getArea(mAID)) {
        return;
    }
    int labelID;
    if (!mpMap->mapLabels.contains(mAID)) {
        QMap<int, TMapLabel> m;
        m[0] = label;
        mpMap->mapLabels[mAID] = m;
    } else {
        labelID = mpMap->createMapLabelID(mAID);
        mpMap->mapLabels[mAID].insert(labelID, label);
    }
    update();
}

void T2DMap::mouseReleaseEvent(QMouseEvent* e)
{
    if (mMoveLabel) {
        mMoveLabel = false;
    }

    //move map with left mouse button + ALT (->
    if (mpMap->mLeftDown) {
        mpMap->mLeftDown = false;
    }

    if (e->button() & Qt::LeftButton) {
        mMultiSelection = false; // End drag-to-select rectangle resizing
        mHelpMsg.clear();
        if (mSizeLabel) {
            mSizeLabel = false;
            QRectF labelRect = mMultiRect;
            createLabel(labelRect);
        }
        mMultiRect = QRect(0, 0, 0, 0);
        update();
    }
}

bool T2DMap::event(QEvent* event)
{
    //NOTE: key events aren't being forwarded to T2DMap because the widget currently never has focus because it's more comfortable for the user to always have focus on the command line
    //      If this were to be changed some day the setFocusPolicy() calls in the constructor need to be uncommented

    if (event->type() == QEvent::KeyPress) {
        QKeyEvent* ke = static_cast<QKeyEvent*>(event);
        //        if( ke->key() == Qt::Key_Delete )
        //        {
        //            if( mCustomLineSelectedRoom != 0  )
        //            {
        //                if( mpMap->rooms.contains(mCustomLineSelectedRoom) )
        //                {
        //                    TRoom * pR = mpMap->rooms[mCustomLineSelectedRoom];
        //                    if( pR->customLines.contains( mCustomLineSelectedExit) )
        //                    {
        //                        pR->customLines.remove(mCustomLineSelectedExit);
        //                        repaint();
        //                        mCustomLineSelectedRoom = 0;
        //                        mCustomLineSelectedExit = "";
        //                        mCustomLineSelectedPoint = -1;
        //                        return QWidget::event(event);
        //                    }
        //                }
        //            }
        //        }
    } else if (event->type() == QEvent::Resize) { // Tweak the room selection widget to fit
        resizeMultiSelectionWidget();
    }
    return QWidget::event(event);
}

void T2DMap::mousePressEvent(QMouseEvent* event)
{
    mNewMoveAction = true;
    if (event->buttons() & Qt::LeftButton) {
        // move map with left mouse button + ALT
        if (event->modifiers().testFlag(Qt::AltModifier)) {
            mpMap->mLeftDown = true;
        }

        // drawing new custom exit line
        if (mCustomLinesRoomFrom > 0) {
            if (mDialogLock) {
                return; // Prevent any line drawing until ready
            }

            TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLinesRoomFrom);
            if (pR) {
                float mx = event->pos().x() / mTX + mOx;
                float my = event->pos().y() / mTY + mOy;
                mx = mx - xspan / 2;
                my = yspan / 2 - my;
                // might be useful to have a snap to grid type option
                pR->customLines[mCustomLinesRoomExit].push_back(QPointF(mx, my));
                pR->calcRoomDimensions();
                repaint();
                return;
            }
        }

        // check click on custom exit lines
        if (mMultiSelectionSet.isEmpty()) { // But NOT if got one or more rooms already selected!
            TArea* pA = mpMap->mpRoomDB->getArea(mAID);
            if (pA) {
                TArea* pArea = pA;
                float mx = event->pos().x() / mTX + mOx;
                float my = event->pos().y() / mTY + mOy;
                mx = mx - xspan / 2;
                my = yspan / 2 - my;
                QPointF pc = QPointF(mx, my);
                QSetIterator<int> itRoom = pArea->rooms;
                while (itRoom.hasNext()) {
                    int currentRoomId = itRoom.next();
                    TRoom* pR = mpMap->mpRoomDB->getRoom(currentRoomId);
                    if (!pR) {
                        continue;
                    }
                    QMapIterator<QString, QList<QPointF>> it(pR->customLines);
                    while (it.hasNext()) {
                        it.next();
                        const QList<QPointF>& _pL = it.value();
                        if (_pL.size()) {
                            // The way this code is structured means that EARLIER
                            // points are selected in preference to later ones!
                            // This might not be intuative to the users...
                            float olx, oly, lx, ly;
                            for (int j = 0; j < _pL.size(); j++) {
                                if (j == 0) { // First segment of a custom line
                                              // start it at the centre of the room
                                    olx = pR->x;
                                    oly = pR->y; //FIXME: exit richtung beachten, um den Linienanfangspunkt zu berechnen
                                    lx = _pL[0].x();
                                    ly = _pL[0].y();
                                } else { // Not the first segment of a custom line
                                         // so start it at the end of the previous one
                                    olx = lx;
                                    oly = ly;
                                    lx = _pL[j].x();
                                    ly = _pL[j].y();
                                }
                                // End of each custom line segment is given

                                // click auf einen edit - punkt
                                if (mCustomLineSelectedRoom != 0) {                     // We have already choosen a line to edit
                                    if (abs(mx - lx) <= 0.25 && abs(my - ly) <= 0.25) { // And this looks close enough to a point that we should edit it
                                        mCustomLineSelectedPoint = j;
                                        return;
                                    }
                                }

                                // We have not previously choosen a line to edit
                                QLineF line = QLineF(olx, oly, lx, ly);
                                QLineF normal = line.normalVector();
                                QLineF tl;
                                tl.setP1(pc);
                                tl.setAngle(normal.angle());
                                tl.setLength(0.1);
                                QLineF tl2;
                                tl2.setP1(pc);
                                tl2.setAngle(normal.angle());
                                tl2.setLength(-0.1);
                                QPointF pi;
                                if ((line.intersect(tl, &pi) == QLineF::BoundedIntersection)
                                    || (line.intersect(tl2, &pi) == QLineF::BoundedIntersection)) { // Choose THIS line to edit as we have clicked close enough to it...
                                    mCustomLineSelectedRoom = pR->getId();
                                    mCustomLineSelectedExit = it.key();
                                    repaint();
                                    return;
                                }
                            }
                        }
                    }
                }
            }
            mCustomLineSelectedRoom = 0;
            mCustomLineSelectedExit = "";
        }

        if (mRoomBeingMoved) // Moving rooms so end that
        {
            mPHighlightMove = event->pos();
            mPick = true;

            setMouseTracking(false);
            mRoomBeingMoved = false;
        } else if (!mPopupMenu) // Not in a context menu, so start selection mode - including drag to select
        {
            mMultiSelection = true;
            mMultiRect = QRect(event->pos(), event->pos());
            int _roomID = mRID;
            if (!mpMap->mpRoomDB->getRoom(_roomID)) {
                return;
            }
            int _areaID = mAID;
            TArea* pArea = mpMap->mpRoomDB->getArea(_areaID);
            if (!pArea) {
                return;
            }
            float _rx = xspan / 2 * mTX - mTX * mOx;
            float _ry = yspan / 2 * mTY - mTY * mOy;

            if (!event->modifiers().testFlag(Qt::ControlModifier)) { // If control key NOT down then clear selection, and put up helpful text
                mHelpMsg = tr("Drag to select multiple rooms or labels, release to finish...");
                mMultiSelectionSet.clear();
            }

            QSetIterator<int> itRoom(pArea->getAreaRooms());
            while (itRoom.hasNext()) { // Scan to find rooms in selection
                int currentAreaRoom = itRoom.next();
                TRoom* pR = mpMap->mpRoomDB->getRoom(currentAreaRoom);
                if (!pR) {
                    continue;
                }
                int rx = pR->x * mTX + _rx;
                int ry = pR->y * -1 * mTY + _ry;
                int rz = pR->z;

                int mx = event->pos().x();
                int my = event->pos().y();
                int mz = mOz;
                if ((abs(mx - rx) < mTX * rSize / 2) && (abs(my - ry) < mTY * rSize / 2) && (mz == rz)) {
                    if (mMultiSelectionSet.contains(currentAreaRoom) && event->modifiers().testFlag(Qt::ControlModifier)) {
                        mMultiSelectionSet.remove(currentAreaRoom);
                    } else {
                        mMultiSelectionSet.insert(currentAreaRoom);
                    }
                    if (mMultiSelectionSet.size() > 0) {
                        mMultiSelection = false;
                    }
                }
            }
            switch (mMultiSelectionSet.size()) {
            case 0:
                mMultiSelectionHighlightRoomId = -1;
                break;
            case 1:
                mMultiSelection = false; // OK, found one room so stop
                mMultiSelectionHighlightRoomId = mMultiSelectionSet.toList().first();
                mHelpMsg.clear();
                break;
            default:
                mMultiSelection = false; // OK, found more than one room so stop
                mHelpMsg.clear();
                getCenterSelection();
            }

            // select labels
            if (mpMap->mapLabels.contains(mAID)) {
                QMapIterator<int, TMapLabel> it(mpMap->mapLabels[mAID]);
                while (it.hasNext()) {
                    it.next();
                    if (it.value().pos.z() != mOz) {
                        continue;
                    }

                    QPointF lpos;
                    float _lx = it.value().pos.x() * mTX + _rx;
                    float _ly = it.value().pos.y() * mTY * -1 + _ry;

                    lpos.setX(_lx);
                    lpos.setY(_ly);
                    int mx = event->pos().x();
                    int my = event->pos().y();
                    // N/U:                         int mz = mOz;
                    QPoint click = QPoint(mx, my);
                    QRectF br = QRect(_lx, _ly, it.value().clickSize.width(), it.value().clickSize.height());
                    if (br.contains(click)) {
                        if (!it.value().hilite) {
                            mLabelHilite = true;
                            mpMap->mapLabels[mAID][it.key()].hilite = true;
                        } else {
                            mpMap->mapLabels[mAID][it.key()].hilite = false;
                            mLabelHilite = false;
                        }
                        update();
                        return;
                    }
                }
            }

            mLabelHilite = false;
            update();

            if (mMultiSelection && mMultiSelectionSet.size() > 0 && (event->modifiers().testFlag(Qt::ControlModifier))) {
                // We were dagging multi-selection rectange, we had selected at
                // least one room and the user has <CTRL>-clicked with the mouse
                // so switch off the dragging
                mMultiSelection = false;
                mHelpMsg.clear();
            }

        } else { // In popup menu, so end that
            mPopupMenu = false;
        }

        // display room selection list widget if more than 1 room has been selected
        // -> user can manually change current selection if rooms are overlapping
        if (mMultiSelectionSet.size() > 1) {
            mMultiSelectionListWidget.blockSignals(true); // We don't want to cause calls to slot_roomSelectionChanged() here!
            mIsSelectionSorting = mMultiSelectionListWidget.isSortingEnabled();
            mIsSelectionSortByNames = (mMultiSelectionListWidget.sortColumn() == 1);
            mMultiSelectionListWidget.clear();
            mMultiSelectionListWidget.setSortingEnabled(false); // Do NOT sort whilst inserting items!
            QSetIterator<int> itRoom = mMultiSelectionSet;
            mIsSelectionUsingNames = false;
            while (itRoom.hasNext()) {
                auto _item = new QTreeWidgetItem;
                int multiSelectionRoomId = itRoom.next();
                _item->setText(0, QStringLiteral("%1").arg(multiSelectionRoomId, 7)); // Pad with spaces so sorting works
                _item->setTextAlignment(0, Qt::AlignRight);
                TRoom* pR_multiSelection = mpMap->mpRoomDB->getRoom(multiSelectionRoomId);
                if (pR_multiSelection) {
                    QString multiSelectionRoomName = pR_multiSelection->name;
                    if (!multiSelectionRoomName.isEmpty()) {
                        _item->setText(1, multiSelectionRoomName);
                        _item->setTextAlignment(1, Qt::AlignLeft);
                        mIsSelectionUsingNames = true;
                    }
                }
                mMultiSelectionListWidget.addTopLevelItem(_item);
            }
            mMultiSelectionListWidget.setColumnHidden(1, !mIsSelectionUsingNames);
            // Can't sort if nothing to sort on, switch to sorting by room number
            if ((!mIsSelectionUsingNames) && mIsSelectionSortByNames && mIsSelectionSorting) {
                mIsSelectionSortByNames = false;
            }
            mMultiSelectionListWidget.sortByColumn(mIsSelectionSortByNames ? 1 : 0);
            mMultiSelectionListWidget.setSortingEnabled(mIsSelectionSorting);
            resizeMultiSelectionWidget();
            mMultiSelectionListWidget.selectAll();
            mMultiSelectionListWidget.blockSignals(false);
            mMultiSelectionListWidget.show();
            update();
        } else {
            mMultiSelectionListWidget.hide();
        }
    }


    if (event->buttons() & Qt::RightButton) {
        auto popup = new QMenu(this);
        popup->setToolTipsVisible(true);

        if (mCustomLinesRoomFrom > 0) {
            if (mDialogLock) {
                return;
            }

            TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLinesRoomFrom);
            if (pR) {
                QAction* action = new QAction("undo", this);
                action->setToolTip(tr("Undo last point"));
                if (pR->customLines.value(mCustomLinesRoomExit).count() > 1) {
                    connect(action, SIGNAL(triggered()), this, SLOT(slot_undoCustomLineLastPoint()));
                } else {
                    action->setEnabled(false);
                }

                QAction* action2 = new QAction("properties", this);
                action2->setText("properties...");
                action2->setToolTip(tr("Change the properties of this line"));
                connect(action2, SIGNAL(triggered()), this, SLOT(slot_customLineProperties()));

                QAction* action3 = new QAction("finish", this);
                action3->setToolTip(tr("Finish drawing this line"));
                connect(action3, SIGNAL(triggered()), this, SLOT(slot_doneCustomLine()));

                mPopupMenu = true;

                pR->calcRoomDimensions();
                popup->addAction(action);
                popup->addAction(action2);
                popup->addAction(action3);

                popup->popup(mapToGlobal(event->pos()));
                update();
                return;
            }
        }

        if (!mLabelHilite && mCustomLineSelectedRoom == 0) {
            QAction* action = new QAction("move", this);
            action->setToolTip(tr("Move room"));
            connect(action, SIGNAL(triggered()), this, SLOT(slot_moveRoom()));
            QAction* action2 = new QAction("delete", this);
            action2->setToolTip(tr("Delete room"));
            connect(action2, SIGNAL(triggered()), this, SLOT(slot_deleteRoom()));
            QAction* action3 = new QAction("color", this);
            action3->setToolTip(tr("Change room color"));
            connect(action3, SIGNAL(triggered()), this, SLOT(slot_changeColor()));
            QAction* action4 = new QAction("spread", this);
            action4->setToolTip(tr("Increase map X-Y spacing for the selected group of rooms"));
            connect(action4, SIGNAL(triggered()), this, SLOT(slot_spread()));
            QAction* action9 = new QAction("shrink", this);
            action9->setToolTip(tr("Decrease map X-Y spacing for the selected group of rooms"));
            connect(action9, SIGNAL(triggered()), this, SLOT(slot_shrink()));

            //QAction * action5 = new QAction("user data", this );
            //action5->setToolTip(tr("Set user data"));
            //connect( action5, SIGNAL(triggered()), this, SLOT(slot_setUserData()));
            QAction* action6 = new QAction("lock", this);
            action6->setToolTip(tr("Lock room for speed walks"));
            connect(action6, SIGNAL(triggered()), this, SLOT(slot_lockRoom()));
            QAction* action17 = new QAction("unlock", this);
            action17->setToolTip(tr("Unlock room for speed walks"));
            connect(action17, SIGNAL(triggered()), this, SLOT(slot_unlockRoom()));
            QAction* action7 = new QAction("weight", this);
            action7->setToolTip(tr("Set room weight"));
            connect(action7, SIGNAL(triggered()), this, SLOT(slot_setRoomWeight()));
            QAction* action8 = new QAction("exits", this);
            action8->setToolTip(tr("Set room exits"));
            connect(action8, SIGNAL(triggered()), this, SLOT(slot_setExits()));
            QAction* action10 = new QAction("letter", this);
            action10->setToolTip(tr("Set a letter to mark special rooms"));
            connect(action10, SIGNAL(triggered()), this, SLOT(slot_setCharacter()));
            //        QAction * action11 = new QAction("image", this );
            //        action11->setToolTip(tr("Set an image to mark special rooms"));
            //        connect( action11, SIGNAL(triggered()), this, SLOT(slot_setImage()));

            QAction* action12 = new QAction("move to", this);
            action12->setToolTip(tr("Move selected group to a given position"));
            connect(action12, SIGNAL(triggered()), this, SLOT(slot_movePosition()));

            QAction* action13 = new QAction("area", this);
            action13->setToolTip(tr("Set room area ID"));
            connect(action13, SIGNAL(triggered()), this, SLOT(slot_setArea()));

            QAction* action14 = new QAction("custom exit lines", this);
            TArea* pArea = mpMap->mpRoomDB->getArea(mAID);
            if (!pArea) {
                return;
            }
            if (pArea->gridMode) { // Disable custom exit lines in grid mode as they aren't visible anyway
                action14->setToolTip(tr("Custom exit lines are not shown and are not editable in grid mode"));
                action14->setEnabled(false);
            } else {
                action14->setToolTip(tr("Replace an exit line with a custom line"));
                connect(action14, SIGNAL(triggered()), this, SLOT(slot_setCustomLine()));
            }

            QAction* action15 = new QAction("create Label", this);
            action15->setToolTip(tr("Create labels to show text or images."));
            connect(action15, SIGNAL(triggered()), this, SLOT(slot_createLabel()));

            QAction* action16 = new QAction("set location", this);
            if (mMultiSelectionSet.size() == 1) { // Only enable if ONE room is highlighted
                action16->setToolTip(tr("Set player current location to here"));
                connect(action16, SIGNAL(triggered()), this, SLOT(slot_setPlayerLocation()));
            } else {
                action16->setEnabled(false);
                action16->setToolTip(tr("Cannot set location when not exactly one room selected"));
            }

            mPopupMenu = true;

            popup->addAction(action);
            popup->addAction(action8);
            popup->addAction(action14);
            popup->addAction(action3);
            popup->addAction(action10);
            //popup->addAction( action11 );
            popup->addAction(action4);
            popup->addAction(action9);
            //popup->addAction( action5 );
            popup->addAction(action6);
            popup->addAction(action17);
            popup->addAction(action7);
            popup->addAction(action2);
            popup->addAction(action12);

            popup->addAction(action13);

            popup->addAction(action15);
            popup->addAction(action16);

            popup->popup(mapToGlobal(event->pos()));
        } else if (mLabelHilite) {
            QAction* action = new QAction("move", this);
            action->setToolTip(tr("Move label"));
            connect(action, SIGNAL(triggered()), this, SLOT(slot_moveLabel()));
            QAction* action2 = new QAction("delete", this);
            action2->setToolTip(tr("Delete label"));
            connect(action2, SIGNAL(triggered()), this, SLOT(slot_deleteLabel()));
            mPopupMenu = true;
            popup->addAction(action);
            popup->addAction(action2);
            popup->popup(mapToGlobal(event->pos()));
        } else { // seems that if we get here then we have right clicked on a selected custom line?
                 //            qDebug("T2DMap::mousePressEvent(): reached else case, mCustomLineSelectedRoom=%i, Exit=%s, Point=%i",
                 //                   mCustomLineSelectedRoom,
                 //                   qPrintable(mCustomLineSelectedExit),
                 //                   mCustomLineSelectedPoint);

            if (mCustomLineSelectedRoom > 0) {
                TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
                if (pR) {
                    QAction* action = new QAction("add point", this);
                    if (mCustomLineSelectedPoint > -1)
                    // The first user manipulable point IS zero - line is
                    // drawn to it from a point around room symbol dependent
                    // on the exit direction - and we can now add even to it
                    {
                        connect(action, SIGNAL(triggered()), this, SLOT(slot_customLineAddPoint()));
                        action->setToolTip(tr("Divide segment by adding a new point mid-way along"));
                    } else {
                        action->setEnabled(false);
                        action->setToolTip(tr("Select a point first, then add a new point mid-way along the segment towards room"));
                    }

                    QAction* action2 = new QAction("remove point", this);
                    // Permit this to be enabled if the current point is 0 or greater, but not if there is no others
                    if (mCustomLineSelectedPoint > -1) {
                        if (pR->customLines.value(mCustomLineSelectedExit).count() > 1) {
                            connect(action2, SIGNAL(triggered()), this, SLOT(slot_customLineRemovePoint()));
                            if ((mCustomLineSelectedPoint + 1) < pR->customLines.value(mCustomLineSelectedExit).count()) {
                                action2->setToolTip(tr("Merge pair of segments by removing this point"));
                            } else {
                                action2->setToolTip(tr("Remove last segment by removing this point"));
                            }
                        } else {
                            action2->setEnabled(false);
                            action2->setToolTip(tr(R"(use "delete line" to remove the only segment ending in an editable point)"));
                        }
                    } else {
                        action2->setEnabled(false);
                        action2->setToolTip(tr("Select a point first, then remove it"));
                    }

                    QAction* action3 = new QAction("properties", this);
                    action3->setText("properties...");
                    action3->setToolTip(tr("Change the properties of this custom line"));
                    connect(action3, SIGNAL(triggered()), this, SLOT(slot_customLineProperties()));

                    QAction* action4 = new QAction("delete line", this);
                    action4->setToolTip(tr("Delete all of this custom line"));
                    connect(action4, SIGNAL(triggered()), this, SLOT(slot_deleteCustomExitLine()));

                    mPopupMenu = true;

                    popup->addAction(action);
                    popup->addAction(action2);
                    popup->addAction(action3);
                    popup->addAction(action4);
                    popup->popup(mapToGlobal(event->pos()));
                }
            }
        } //this is placed at the end since it is likely someone will want to hook anywhere
        QMap<QString, QMenu*> userMenus;
        QMapIterator<QString, QStringList> it(mUserMenus);
        while (it.hasNext()) {
            it.next();
            QStringList menuInfo = it.value();
            QString displayName = menuInfo[1];
            auto userMenu = new QMenu(displayName, this);
            userMenus.insert(it.key(), userMenu);
        }
        it.toFront();
        while (it.hasNext()) { //take care of nested menus now since they're all made
            it.next();
            QStringList menuInfo = it.value();
            QString menuParent = menuInfo[0];
            if (menuParent == "") { //parentless
                popup->addMenu(userMenus[it.key()]);
            } else { //has a parent
                userMenus[menuParent]->addMenu(userMenus[it.key()]);
            }
        }
        //add our actions
        QMapIterator<QString, QStringList> it2(mUserActions);
        auto mapper = new QSignalMapper(this);
        while (it2.hasNext()) {
            it2.next();
            QStringList actionInfo = it2.value();
            auto action = new QAction(actionInfo[2], this);
            if (actionInfo[1] == "") { //no parent
                popup->addAction(action);
            } else if (userMenus.contains(actionInfo[1])) {
                userMenus[actionInfo[1]]->addAction(action);
            } else {
                delete action;
                continue;
            }
            mapper->setMapping(action, it2.key());
            connect(action, SIGNAL(triggered()), mapper, SLOT(map()));
        }
        connect(mapper, SIGNAL(mapped(QString)), this, SLOT(slot_userAction(QString)));
        mLastMouseClick = event->localPos();
    }
    update();
}

// Used both by "properties..." context menu item for existing lines AND
// during drawing new ones.
void T2DMap::slot_customLineProperties()
{
    QString exit;
    TRoom* pR;

    if (mCustomLineSelectedRoom > 0) {
        pR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
        exit = mCustomLineSelectedExit;
    } else {
        pR = mpMap->mpRoomDB->getRoom(mCustomLinesRoomFrom);
        exit = mCustomLinesRoomExit;
    }

    if (pR) {
        if (exit.isEmpty()) {
            qDebug("T2DMap::slot_customLineProperties() called but no exit is selected...");
            return;
        }
        if (pR->customLines.contains(exit)) {
            QUiLoader loader;

            QFile file(":/ui/custom_lines_properties.ui");
            file.open(QFile::ReadOnly);
            QDialog* d = qobject_cast<QDialog*>(loader.load(&file, this));
            file.close();
            if (!d) {
                qWarning("T2DMap::slot_customLineProperties() ERROR: failed to create the dialog!");
                return;
            }
            d->setWindowIcon(QIcon(QStringLiteral(":/icons/mudlet_custom_exit_properties.png")));
            QLineEdit* le_toId = d->findChild<QLineEdit*>("toId");
            QLineEdit* le_fromId = d->findChild<QLineEdit*>("fromId");
            QLineEdit* le_cmd = d->findChild<QLineEdit*>("cmd");

            mpCurrentLineStyle = d->findChild<QComboBox*>("lineStyle");
            mpCurrentLineColor = d->findChild<QPushButton*>("lineColor");
            mpCurrentLineArrow = d->findChild<QCheckBox*>("arrow");
            if (!le_toId || !le_cmd || !le_fromId || !mpCurrentLineStyle || !mpCurrentLineColor || !mpCurrentLineArrow) {
                qWarning("T2DMap::slot_customLineProperties() ERROR: failed to find an element in the dialog!");
                return;
            }
            le_cmd->setText(exit);
            le_fromId->setText(QString::number(pR->getId()));
            if (exit == "NW") {
                le_toId->setText(QString::number(pR->getNorthwest()));
            } else if (exit == "N") {
                le_toId->setText(QString::number(pR->getNorth()));
            } else if (exit == "NE") {
                le_toId->setText(QString::number(pR->getNortheast()));
            } else if (exit == "UP") {
                le_toId->setText(QString::number(pR->getUp()));
            } else if (exit == "W") {
                le_toId->setText(QString::number(pR->getWest()));
            } else if (exit == "E") {
                le_toId->setText(QString::number(pR->getEast()));
            } else if (exit == "DOWN") {
                le_toId->setText(QString::number(pR->getDown()));
            } else if (exit == "SW") {
                le_toId->setText(QString::number(pR->getSouthwest()));
            } else if (exit == "S") {
                le_toId->setText(QString::number(pR->getSouth()));
            } else if (exit == "SE") {
                le_toId->setText(QString::number(pR->getSoutheast()));
            } else if (exit == "IN") {
                le_toId->setText(QString::number(pR->getIn()));
            } else if (exit == "OUT") {
                le_toId->setText(QString::number(pR->getOut()));
            } else {
                bool isFound = false;
                QMultiMap<int, QString> otherExits = pR->getOtherMap();
                QMapIterator<int, QString> otherExitIt = otherExits;
                while (otherExitIt.hasNext()) {
                    otherExitIt.next();
                    if (otherExitIt.value().startsWith("0") || otherExitIt.value().startsWith("1")) {
                        if (otherExitIt.value().mid(1) == exit) {
                            le_toId->setText(QString::number(otherExitIt.key()));
                            isFound = true;
                            break;
                        }
                    } else if (otherExitIt.value() == exit) {
                        le_toId->setText(QString::number(otherExitIt.key()));
                        isFound = true;
                        break;
                    }
                }
                if (!isFound) {
                    qWarning(R"(T2DMap::slot_customLineProperties() - WARNING: missing command "%s" from custom lines for room id %i)", qPrintable(exit), pR->getId());
                }
            }

            QStringList _lineStyles;
            _lineStyles << "solid line"
                        << "dot line"
                        << "dash line"
                        << "dash dot line"
                        << "dash dot dot line";
            mpCurrentLineStyle->addItems(_lineStyles);
            QString _lineStyle = pR->customLinesStyle.value(exit);
            mpCurrentLineStyle->setCurrentIndex(mpCurrentLineStyle->findText(_lineStyle));

            mpCurrentLineArrow->setChecked(pR->customLinesArrow.value(exit));
            mCurrentLineColor.setRed(pR->customLinesColor.value(exit).at(0));
            mCurrentLineColor.setGreen(pR->customLinesColor.value(exit).at(1));
            mCurrentLineColor.setBlue(pR->customLinesColor.value(exit).at(2));

            QString _styleSheet = QString("background-color:" + mCurrentLineColor.name());
            mpCurrentLineColor->setStyleSheet(_styleSheet);
            connect(mpCurrentLineColor, SIGNAL(clicked()), this, SLOT(slot_customLineColor()));

            if (d->exec() == QDialog::Accepted) {
                // Make the changes
                pR->customLinesStyle[exit] = mpCurrentLineStyle->currentText();
                mCurrentLineStyle = mpCurrentLineStyle->currentText();

                pR->customLinesColor[exit][0] = mCurrentLineColor.red();
                pR->customLinesColor[exit][1] = mCurrentLineColor.green();
                pR->customLinesColor[exit][2] = mCurrentLineColor.blue();

                pR->customLinesArrow[exit] = mpCurrentLineArrow->checkState();
                mCurrentLineArrow = mpCurrentLineArrow->checkState();
            }
        }
        repaint();
    } else {
        qDebug("T2DMap::slot_customLineProperties() called but no line is selected...");
    }
}


void T2DMap::slot_customLineAddPoint()
{
    TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
    if (!pR) {
        return;
    }

    if (mCustomLineSelectedPoint > 0) {
        QLineF segment = QLineF(pR->customLines.value(mCustomLineSelectedExit).at(mCustomLineSelectedPoint - 1), pR->customLines.value(mCustomLineSelectedExit).at(mCustomLineSelectedPoint));
        segment.setLength(segment.length() / 2.0);
        pR->customLines[mCustomLineSelectedExit].insert(mCustomLineSelectedPoint, segment.p2());
        mCustomLineSelectedPoint++;
        repaint();
    } else if (mCustomLineSelectedPoint == 0) {
        // The first user manipulable point IS zero - line is drawn to it from a
        // point around room symbol dependent on the exit direction
        // The first segment of custom line stick out half of the distance
        // between two rooms at a map unit vector distance apart. so an added
        // point inserted before the first must be placed halfway between
        // the offset point and the previosu first point
        QPointF customLineStartPoint;
        if (mCustomLineSelectedExit == "N") {
            customLineStartPoint = QPointF(pR->x, pR->y + 0.5);
        } else if (mCustomLineSelectedExit == "S") {
            customLineStartPoint = QPointF(pR->x, pR->y - 0.5);
        } else if (mCustomLineSelectedExit == "E") {
            customLineStartPoint = QPointF(pR->x + 0.5, pR->y);
        } else if (mCustomLineSelectedExit == "W") {
            customLineStartPoint = QPointF(pR->x - 0.5, pR->y);
        } else if (mCustomLineSelectedExit == "NE") {
            customLineStartPoint = QPointF(pR->x + 0.5, pR->y + 0.5);
        } else if (mCustomLineSelectedExit == "NW") {
            customLineStartPoint = QPointF(pR->x - 0.5, pR->y + 0.5);
        } else if (mCustomLineSelectedExit == "SE") {
            customLineStartPoint = QPointF(pR->x + 0.5, pR->y - 0.5);
        } else if (mCustomLineSelectedExit == "SW") {
            customLineStartPoint = QPointF(pR->x - 0.5, pR->y - 0.5);
        } else {
            customLineStartPoint = QPointF(pR->x, pR->y);
        }
        QLineF segment = QLineF(customLineStartPoint, pR->customLines.value(mCustomLineSelectedExit).at(0));
        segment.setLength(segment.length() / 2.0);
        pR->customLines[mCustomLineSelectedExit].insert(mCustomLineSelectedPoint, segment.p2());
        mCustomLineSelectedPoint++;
        repaint();
    }
}


void T2DMap::slot_customLineRemovePoint()
{
    TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
    if (!pR) {
        return;
    }

    if (mCustomLineSelectedPoint > 0) {
        pR->customLines[mCustomLineSelectedExit].removeAt(mCustomLineSelectedPoint);
        mCustomLineSelectedPoint--;
        repaint();
    } else if (mCustomLineSelectedPoint == 0 && pR->customLines.value(mCustomLineSelectedExit).count() > 1) {
        // The first user manipulable point IS zero - line is drawn to it from a
        // point around room symbol dependent on the exit direction.  We can only
        // allow it's deletion if there is at least another one left.
        pR->customLines[mCustomLineSelectedExit].removeAt(mCustomLineSelectedPoint);
        repaint();
    }
}


void T2DMap::slot_undoCustomLineLastPoint()
{
    if (mCustomLinesRoomFrom > 0) {
        TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLinesRoomFrom);
        if (pR) {
            if (pR->customLines.value(mCustomLinesRoomExit).count() > 0) {
                pR->customLines[mCustomLinesRoomExit].pop_back();
            }
            pR->calcRoomDimensions();
        }
        repaint();
    }
}

void T2DMap::slot_doneCustomLine()
{
    if (mpCustomLinesDialog) {
        mpCustomLinesDialog->accept();
        mpCustomLinesDialog = nullptr;
    }
    mHelpMsg = "";
    mCustomLinesRoomFrom = 0;
    mCustomLinesRoomTo = 0;
    mCustomLinesRoomExit.clear();
    if (mMultiSelectionSet.size() > 0) {
        TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
        if (pR) {
            pR->calcRoomDimensions();
        }
    }
    update();
}

void T2DMap::slot_deleteCustomExitLine()
{
    if (mCustomLineSelectedRoom > 0) {
        TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
        if (pR) {
            pR->customLinesArrow.remove(mCustomLineSelectedExit);
            pR->customLinesColor.remove(mCustomLineSelectedExit);
            pR->customLinesStyle.remove(mCustomLineSelectedExit);
            pR->customLines.remove(mCustomLineSelectedExit);
            mCustomLineSelectedRoom = 0;
            mCustomLineSelectedExit = "";
            mCustomLineSelectedPoint = -1;
            repaint();
            pR->calcRoomDimensions();
            TArea* pA = mpMap->mpRoomDB->getArea(pR->getArea());
            if (pA) {
                pA->calcSpan();
            }
        }
    }
}

void T2DMap::slot_moveLabel()
{
    mMoveLabel = true;
}

void T2DMap::slot_deleteLabel()
{
    if (mpMap->mapLabels.contains(mAID)) {
        QList<int> deleteList;
        QMapIterator<int, TMapLabel> it(mpMap->mapLabels[mAID]);
        while (it.hasNext()) {
            it.next();
            int _zlevel = static_cast<int>(it.value().pos.z());
            if (_zlevel != mOz) {
                continue;
            }
            if (it.value().hilite) {
                deleteList.push_back(it.key());
            }
        }
        for (int& i : deleteList) {
            mpMap->mapLabels[mAID].remove(i);
        }
    }
    update();
}

void T2DMap::slot_editLabel()
{
}

void T2DMap::slot_setPlayerLocation()
{
    if (mMultiSelectionSet.size() != 1) {
        return; // Was <= 1 but that can't be right, and >1 doesn't seem right either
    }

    int _newRoomId = *(mMultiSelectionSet.constBegin());
    if (mpMap->mpRoomDB->getRoom(_newRoomId)) {
        // No need to check it is a DIFFERENT room - that is taken care of by en/dis-abling the control
        mpMap->mRoomIdHash[mpHost->getName()] = _newRoomId;
        mpMap->mNewMove = true;
        TEvent manualSetEvent;
        manualSetEvent.mArgumentList.append(QLatin1String("sysManualLocationSetEvent"));
        manualSetEvent.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
        manualSetEvent.mArgumentList.append(QString::number(_newRoomId));
        manualSetEvent.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
        mpHost->raiseEvent(manualSetEvent);
        update();
    }
}

void T2DMap::slot_userAction(QString uniqueName)
{
    TEvent event;
    QStringList userEvent = mUserActions[uniqueName];
    event.mArgumentList.append(userEvent[0]);
    event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
    event.mArgumentList.append(uniqueName);
    event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
    QSetIterator<int> itRoom(mMultiSelectionSet);
    if (itRoom.hasNext()) {
        while (itRoom.hasNext()) {
            event.mArgumentList.append(QString::number(itRoom.next()));
            event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
        }
        mpHost->raiseEvent(event);
    }
    // Unreachable code as had effectively the same test as the previous "if"
    // mMultiSelectionList is now mMultiSelectionSet:
    //    else if( mMultiSelectionList.size() > 0 )
    //    {
    //        event.mArgumentList.append(QString::number(mMultiSelectionList[0]));
    //        event.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER);
    //        mpHost->raiseEvent( & event );
    //    }
    else {
        event.mArgumentList.append(uniqueName);
        event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
        for (int i = 0; i < userEvent.size(); i++) {
            event.mArgumentList.append(userEvent[i]);
            event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
        }
        mpHost->raiseEvent(event);
    }
}

void T2DMap::slot_movePosition()
{
    if (!getCenterSelection()) {
        return;
    }

    TRoom* pR_start = mpMap->mpRoomDB->getRoom(mMultiSelectionHighlightRoomId);
    // pR has already been validated by getCenterSelection()

    auto pD = new QDialog(this);
    auto pL = new QGridLayout;
    pD->setLayout(pL);
    pD->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
    pD->setContentsMargins(0, 0, 0, 0);
    auto pLEx = new QLineEdit(pD);
    auto pLEy = new QLineEdit(pD);
    auto pLEz = new QLineEdit(pD);

    pLEx->setText(QString::number(pR_start->x));
    pLEy->setText(QString::number(pR_start->y));
    pLEz->setText(QString::number(pR_start->z));
    QLabel* pLa0 = new QLabel(tr("Move the selection, centered on\nthe highlighted room (%1) to:").arg(mMultiSelectionHighlightRoomId));
    // Record the starting coordinates - can be a help when working out how to move a block of rooms!
    QLabel* pLa1 = new QLabel(tr("x coordinate (was %1):").arg(pR_start->x));
    QLabel* pLa2 = new QLabel(tr("y coordinate (was %1):").arg(pR_start->y));
    QLabel* pLa3 = new QLabel(tr("z coordinate (was %1):").arg(pR_start->z));
    pL->addWidget(pLa0, 0, 0, 1, 2, Qt::AlignCenter);
    pL->addWidget(pLa1, 1, 0, Qt::AlignVCenter | Qt::AlignRight);
    pL->addWidget(pLEx, 1, 1, Qt::AlignVCenter | Qt::AlignLeft);
    pL->addWidget(pLa2, 2, 0, Qt::AlignVCenter | Qt::AlignRight);
    pL->addWidget(pLEy, 2, 1, Qt::AlignVCenter | Qt::AlignLeft);
    pL->addWidget(pLa3, 3, 0, Qt::AlignVCenter | Qt::AlignRight);
    pL->addWidget(pLEz, 3, 1, Qt::AlignVCenter | Qt::AlignLeft);
    auto pButtonBar = new QWidget(pD);

    auto pL2 = new QHBoxLayout;
    pButtonBar->setLayout(pL2);
    pButtonBar->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));

    auto pB_ok = new QPushButton(pButtonBar);
    pB_ok->setText("Ok");
    pL2->addWidget(pB_ok);
    connect(pB_ok, SIGNAL(clicked()), pD, SLOT(accept()));

    auto pB_abort = new QPushButton(pButtonBar);
    pB_abort->setText("Cancel");
    connect(pB_abort, SIGNAL(clicked()), pD, SLOT(reject()));
    pL2->addWidget(pB_abort);
    pL->addWidget(pButtonBar, 4, 0, 1, 2, Qt::AlignCenter);

    if (pD->exec() == QDialog::Accepted) {
        int dx = pLEx->text().toInt() - pR_start->x;
        int dy = pLEy->text().toInt() - pR_start->y;
        int dz = pLEz->text().toInt() - pR_start->z;

        mMultiRect = QRect(0, 0, 0, 0);

        QSetIterator<int> itRoom = mMultiSelectionSet;
        while (itRoom.hasNext()) {
            TRoom* pR = mpMap->mpRoomDB->getRoom(itRoom.next());
            if (!pR) {
                continue;
            }

            pR->x += dx;
            pR->y += dy;
            pR->z += dz;
        }
    }
    repaint();
}


void T2DMap::slot_moveRoom()
{
    mRoomBeingMoved = true;
    setMouseTracking(true);
    mNewMoveAction = true;
}

// TODO: Convert pR->c (a single ASCII character) to pR->symbol (a single Unicode Grapheme, possibly containing several QChars - as a QString)
void T2DMap::slot_setCharacter()
{
    // Now analysises and reports the existing letters used in ALL the selected
    // rooms if more than once (and sorts by their frequency)
    // Also allows the existing letters to be deleted (by clearing all the displayed
    // letters) as previous code DID NOT - and the Cancel option works as well!
    if (mMultiSelectionSet.size() < 1) {
        return;
    }

    // First scan and count all the different letters used
    QMap<QString, uint> usedLetters;
    QSetIterator<int> itRoom = mMultiSelectionSet;
    TRoom* pR;
    while (itRoom.hasNext()) {
        pR = mpMap->mpRoomDB->getRoom(itRoom.next());
        if (!pR) {
            continue;
        }

        if (pR->c) {
            QString thisLetter = QString(pR->c);
            if (!thisLetter.isEmpty()) {
                if (usedLetters.contains(thisLetter)) {
                    usedLetters[thisLetter] += 1;
                } else {
                    usedLetters[thisLetter] = 1;
                }
            }
        }
    }

    QString newLetterText;
    bool isOk = false;
    // Choose most appropriate text depending on number of rooms selected and
    // how many of them have letters already:
    if (mMultiSelectionSet.size() == 1) {
        if (usedLetters.isEmpty()) {
            newLetterText = QInputDialog::getText(this,                    // QWidget * parent
                                                  tr("Enter room marker"), // const QString & title
                                                  tr("Enter new (not space)\n"
                                                     "marker letter:"), // const QString & label
                                                  QLineEdit::Normal,    // QLineEdit::EchoMode mode = QLineEdit::Normal
                                                  QString(),            // const QString & text = QString()
                                                  &isOk,                // bool * ok = 0
                                                  Qt::Widget,           // Qt::WindowFlags flags = 0
                                                  Qt::ImhLatinOnly);    // Qt::InputMethodHints inputMethodHints = Qt::ImhNone
        } else {
            newLetterText = QInputDialog::getText(this,
                                                  tr("Enter room marker"),
                                                  tr("Delete the existing, or\n"
                                                     "enter a new (non-space),\n"
                                                     "marker letter:"),
                                                  QLineEdit::Normal,
                                                  usedLetters.keys().first(),
                                                  &isOk,
                                                  Qt::Widget,
                                                  Qt::ImhLatinOnly);
        }
    } else {
        if (usedLetters.isEmpty()) {
            newLetterText = QInputDialog::getText(this,
                                                  tr("Enter room marker"),
                                                  tr("Enter new (not space)\n"
                                                     "marker letter:"),
                                                  QLineEdit::Normal,
                                                  QString(),
                                                  &isOk,
                                                  Qt::Widget,
                                                  Qt::ImhLatinOnly);
        } else if (usedLetters.size() == 1) {
            newLetterText = QInputDialog::getText(this,
                                                  tr("Enter room marker"),
                                                  tr("Delete the (only)\n"
                                                     "existing (in some rooms),\n"
                                                     "or enter a new (not\n"
                                                     "space) marker letter for\n"
                                                     "all selected rooms:"),
                                                  QLineEdit::Normal,
                                                  usedLetters.keys().first(),
                                                  &isOk,
                                                  Qt::Widget,
                                                  Qt::ImhLatinOnly);
        } else {
            QMapIterator<QString, uint> itSymbolUsed = usedLetters;
            QSet<uint> symbolCountsSet;
            while (itSymbolUsed.hasNext()) {
                itSymbolUsed.next();
                symbolCountsSet.insert(itSymbolUsed.value());
            }
            QList<uint> symbolCountsList = symbolCountsSet.toList();
            if (symbolCountsList.size() > 1) {
                std::sort(symbolCountsList.begin(), symbolCountsList.end());
            }
            QStringList displayStrings;
            for (int i = symbolCountsList.size() - 1; i >= 0; --i) {
                itSymbolUsed.toFront();
                while (itSymbolUsed.hasNext()) {
                    itSymbolUsed.next();
                    if (itSymbolUsed.value() == symbolCountsList.at(i)) {
                        displayStrings.append(tr("%1 {count:%2}").arg(itSymbolUsed.key(), itSymbolUsed.value()));
                    }
                }
            }
            newLetterText = QInputDialog::getItem(this,                    // QWidget * parent
                                                  tr("Enter room marker"), // const QString & title
                                                  tr("Choose an existing marker\n"
                                                     "letter) from the list\n"
                                                     "(sorted by most commonly\n"
                                                     "used first) or enter a\n"
                                                     "new single ASCII\n"
                                                     "character (not space) for\n"
                                                     "all selected rooms:",
                                                     // Intentional comment to separate two strings!
                                                     "Use line feeds to format text into a reasonable rectangle."), // const QString & label
                                                  displayStrings,                                                   // QStringList & items
                                                  0,                                                                // int current = 0
                                                  true,                                                             // bool editable = true
                                                  &isOk,                                                            // bool * ok = 0
                                                  nullptr,                                                                // Qt::WindowFlags flags = 0
                                                  Qt::ImhLatinOnly);                                                // Qt::InputMethodHints inputMethodHints = Qt::ImhNone,
                                                                                                                    // to change when I rework the room symbols for any Unicode Grapheme!
        }
    }

    QString newLetter;
    if (isOk) { // Don't proceed if cancel was pressed, will need revision to extract the
        // first grapheme when we support all printable unicode characters as room symbols!
        if (newLetterText.isEmpty()) {
            newLetter = QStringLiteral(" ");
        } else if (!newLetterText.at(0).isHighSurrogate()) { // The first QChar of the QString is NOT the start of a Unicode character beyond the BMP!
            if (newLetterText.at(0).row() == 0) {            // The first QChar of the QString has a zero high byte
                if (newLetterText.at(0).cell() < 128) {      // The first QChar of the QString has a low byte within the range for ASCII
                    newLetter = newLetterText.at(0);
                } else {
                    isOk = false; // Prevent any change if the value is not reasonable
                }
            } else {
                isOk = false; // Prevent any change if the value is not reasonable
            }
        } else {
            isOk = false; // Prevent any change if the value is not reasonable
        }
    }

    if (isOk) {
        itRoom.toFront();
        while (itRoom.hasNext()) {
            pR = mpMap->mpRoomDB->getRoom(itRoom.next());
            if (!pR) {
                continue;
            }

            if (newLetter.at(0) == QStringLiteral(" ")) {
                if (pR->c) {
                    pR->c = 0;
                }
            } else {
                pR->c = newLetter.at(0).toLatin1();
            }
        }
    }
}

void T2DMap::slot_setImage()
{
}


void T2DMap::slot_deleteRoom()
{
    mpMap->mpRoomDB->removeRoom(mMultiSelectionSet);
    // mMultiSelectionSet gets cleared as rooms are removed by
    // TRoomDB::removeRoom() so no need to clear it here!
    mMultiRect = QRect(0, 0, 0, 0);
    mMultiSelectionListWidget.clear();
    mMultiSelectionListWidget.hide();
    repaint();
}

void T2DMap::slot_selectRoomColor(QListWidgetItem* pI)
{
    mChosenRoomColor = pI->text().toInt();
}

void T2DMap::slot_defineNewColor()
{
    auto color = QColorDialog::getColor(mpHost->mRed, this);
    if (color.isValid()) {
        mpMap->customEnvColors[mpMap->customEnvColors.size() + 257 + 16] = color;
        slot_changeColor();
    }
    repaint();
}

void T2DMap::slot_changeColor()
{
    mChosenRoomColor = 5;
    auto pD = new QDialog(this);
    auto pL = new QVBoxLayout;
    pD->setLayout(pL);
    pD->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
    pD->setContentsMargins(0, 0, 0, 0);
    auto pLW = new QListWidget(pD);
    pLW->setViewMode(QListView::IconMode);

    connect(pLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)), pD, SLOT(accept()));
    connect(pLW, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slot_selectRoomColor(QListWidgetItem*)));

    pL->addWidget(pLW);
    auto pButtonBar = new QWidget(pD);

    auto pL2 = new QHBoxLayout;
    pButtonBar->setLayout(pL2);
    pButtonBar->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
    auto pB_newColor = new QPushButton(pButtonBar);
    pB_newColor->setText("define new color");

    connect(pB_newColor, SIGNAL(clicked()), pD, SLOT(reject()));
    connect(pB_newColor, SIGNAL(clicked()), this, SLOT(slot_defineNewColor()));

    pL2->addWidget(pB_newColor);

    auto pB_ok = new QPushButton(pButtonBar);
    pB_ok->setText("ok");
    pL2->addWidget(pB_ok);
    connect(pB_ok, SIGNAL(clicked()), pD, SLOT(accept()));

    auto pB_abort = new QPushButton(pButtonBar);
    pB_abort->setText("abort");
    connect(pB_abort, SIGNAL(clicked()), pD, SLOT(reject()));
    pL2->addWidget(pB_abort);
    pL->addWidget(pButtonBar);

    QMapIterator<int, QColor> it(mpMap->customEnvColors);
    while (it.hasNext()) {
        it.next();
        QColor c;
        c = it.value();
        auto pI = new QListWidgetItem(pLW);
        QPixmap pix = QPixmap(50, 50);
        pix.fill(c);
        QIcon mi(pix);
        pI->setIcon(mi);
        pI->setText(QString::number(it.key()));
        pLW->addItem(pI);
    }

    if (pD->exec() == QDialog::Accepted && mpMap->customEnvColors.contains(mChosenRoomColor)) { // Only proceed if OK - "abort" now prevents change AND check for a valid
                                                                                                // color here rather than inside the room change loop as before (only test
                                                                                                // once rather than for each room)
        mMultiRect = QRect(0, 0, 0, 0);
        QSetIterator<int> itSelectedRoom(mMultiSelectionSet);
        while (itSelectedRoom.hasNext()) {
            TRoom* pR = mpMap->mpRoomDB->getRoom(itSelectedRoom.next());
            if (pR) {
                pR->environment = mChosenRoomColor;
            }
        }

        update();
    }
}

void T2DMap::slot_spread()
{
    if (mMultiSelectionSet.size() < 2) { // nothing to do!
        return;
    }

    TRoom* pR_centerRoom = mpMap->mpRoomDB->getRoom(mMultiSelectionHighlightRoomId);
    if (!pR_centerRoom) {
        return;
    }

    // Move the dialog down to here so it doesn't fire up for some already
    // determined to be null (no change) case, also handle "Cancel" being pressed
    bool isOk = false;
    int spread = QInputDialog::getInt(this,
                                      tr("Spread out rooms"),
                                      tr("Increase the spacing of\n"
                                         "the selected rooms,\n"
                                         "centered on the\n"
                                         "highlighted room by a\n"
                                         "factor of:"),
                                      5,          // Initial value
                                      1,          // Minimum value
                                      2147483647, // Maximum value
                                      1,          // Step
                                      &isOk);
    if (spread == 1 || !isOk) {
        return;
    }

    mMultiRect = QRect(0, 0, 0, 0);
    int dx = pR_centerRoom->x * (1 - spread);
    int dy = pR_centerRoom->y * (1 - spread);
    QSetIterator<int> itSelectionRoom = mMultiSelectionSet;
    while (itSelectionRoom.hasNext()) {
        TRoom* pMovingR = mpMap->mpRoomDB->getRoom(itSelectionRoom.next());
        if (!pMovingR) {
            continue;
        }

        pMovingR->x *= spread;
        pMovingR->y *= spread;
        pMovingR->x += dx;
        pMovingR->y += dy;
        QMapIterator<QString, QList<QPointF>> itCustomLine(pMovingR->customLines);
        QMap<QString, QList<QPointF>> newCustomLinePointsMap;
        while (itCustomLine.hasNext()) {
            itCustomLine.next();
            QList<QPointF> customLinePoints = itCustomLine.value();
            for (auto& customLinePoint : customLinePoints) {
                QPointF movingPoint = customLinePoint;
                customLinePoint.setX((float)(movingPoint.x() * spread + dx));
                customLinePoint.setY((float)(movingPoint.y() * spread + dy));
            }
            newCustomLinePointsMap.insert(itCustomLine.key(), customLinePoints);
        }
        pMovingR->customLines = newCustomLinePointsMap;
        pMovingR->calcRoomDimensions();
    }
    repaint();
}

void T2DMap::slot_shrink()
{
    if (mMultiSelectionSet.size() < 2) { // nothing to do!
        return;
    }

    TRoom* pR_centerRoom = mpMap->mpRoomDB->getRoom(mMultiSelectionHighlightRoomId);
    if (!pR_centerRoom) {
        return;
    }

    // Move the dialog down to here so it doesn't fire up for some already
    // determined to be null (no change) case, also handle "Cancel" being pressed
    bool isOk = false;
    int spread = QInputDialog::getInt(this,
                                      tr("Shrink in rooms"),
                                      tr("Decrease the spacing of\n"
                                         "the selected rooms,\n"
                                         "centered on the\n"
                                         "highlighted room by a\n"
                                         "factor of:"),
                                      5,          // Initial value
                                      1,          // Minimum value
                                      2147483647, // Maximum value
                                      1,          // Step
                                      &isOk);
    if (spread == 1 || !isOk) {
        return;
    }

    mMultiRect = QRect(0, 0, 0, 0);
    int dx = pR_centerRoom->x * (1 - 1 / spread);
    int dy = pR_centerRoom->y * (1 - 1 / spread);

    QSetIterator<int> itSelectionRoom(mMultiSelectionSet);
    while (itSelectionRoom.hasNext()) {
        TRoom* pMovingR = mpMap->mpRoomDB->getRoom(itSelectionRoom.next());
        if (!pMovingR) {
            continue;
        }
        pMovingR->x /= spread;
        pMovingR->y /= spread;
        pMovingR->x += dx;
        pMovingR->y += dy;
        QMapIterator<QString, QList<QPointF>> itCustomLine(pMovingR->customLines);
        QMap<QString, QList<QPointF>> newCustomLinePointsMap;
        while (itCustomLine.hasNext()) {
            itCustomLine.next();
            QList<QPointF> customLinePoints = itCustomLine.value();
            for (auto& customLinePoint : customLinePoints) {
                QPointF movingPoint = customLinePoint;
                customLinePoint.setX((float)(movingPoint.x() / spread + dx));
                customLinePoint.setY((float)(movingPoint.y() / spread + dy));
            }
            newCustomLinePointsMap.insert(itCustomLine.key(), customLinePoints);
        }
        pMovingR->customLines = newCustomLinePointsMap;
        pMovingR->calcRoomDimensions();
    }
    repaint();
}

void T2DMap::slot_setExits()
{
    if (mMultiSelectionSet.size() < 1) {
        return;
    }
    if (mpMap->mpRoomDB->getRoom(mMultiSelectionHighlightRoomId)) {
        auto pD = new dlgRoomExits(mpHost, this);
        pD->init(mMultiSelectionHighlightRoomId);
        pD->show();
        pD->raise();
    }
}


void T2DMap::slot_setUserData()
{
}

void T2DMap::slot_lockRoom()
{
    if (mMultiSelectionSet.size() < 1) {
        return;
    }

    mMultiRect = QRect(0, 0, 0, 0);
    QSetIterator<int> itSelectedRoom(mMultiSelectionSet);
    while (itSelectedRoom.hasNext()) {
        TRoom* pR = mpMap->mpRoomDB->getRoom(itSelectedRoom.next());
        if (pR) {
            pR->isLocked = true;
            mpMap->mMapGraphNeedsUpdate = true;
        }
    }
}

void T2DMap::slot_unlockRoom()
{
    if (mMultiSelectionSet.size() < 1) {
        return;
    }

    mMultiRect = QRect(0, 0, 0, 0);
    QSetIterator<int> itSelectedRoom(mMultiSelectionSet);
    while (itSelectedRoom.hasNext()) {
        TRoom* pR = mpMap->mpRoomDB->getRoom(itSelectedRoom.next());
        if (pR) {
            pR->isLocked = false;
            mpMap->mMapGraphNeedsUpdate = true;
        }
    }
}

void T2DMap::slot_setRoomWeight()
{
    if (mMultiSelectionSet.isEmpty()) {
        return;
    }

    // First scan and count all the different weights used
    QMap<uint, uint> usedWeights; // key is weight, value is count of uses
    QSetIterator<int> itSelectedRoom = mMultiSelectionSet;
    TRoom* pR;
    while (itSelectedRoom.hasNext()) {
        pR = mpMap->mpRoomDB->getRoom(itSelectedRoom.next());
        if (!pR) {
            continue;
        }

        int w = pR->getWeight();
        if (w > 0) {
            if (usedWeights.contains(w)) {
                usedWeights[w] += 1;
            } else {
                usedWeights[w] = 1;
            }
        }
    }

    int newWeight = 1;
    bool isOk = false;
    // Choose most appropriate weight dialog on number of rooms selected and
    // how many of them have different weights already:
    if (mMultiSelectionSet.size() == 1) { // Just one room selected
        newWeight = QInputDialog::getInt(this,
                                         tr("Enter room weight"),
                                         tr("Enter new roomweight\n"
                                            "(= travel time), mimimum\n"
                                            "(and default) is 1:",
                                            "Use line feeds to format text into a reasonable rectangle."),
                                         usedWeights.keys().first(),
                                         1,
                                         2147483647,
                                         1,
                                         &isOk);
    } else { // More than one room selected
        if (usedWeights.size() == 1) {
            newWeight = QInputDialog::getInt(this,
                                             tr("Enter room weight"),
                                             tr("Enter new roomweight\n"
                                                "(= travel time) for all\n"
                                                "selected rooms, mimimum\n"
                                                "(and default) is 1 and\n"
                                                "the only current value\n"
                                                "used is:",
                                                "Use line feeds to format text into a reasonable rectangle."),
                                             usedWeights.keys().first(),
                                             1,
                                             2147483647,
                                             1,
                                             &isOk);
        } else {
            QMapIterator<uint, uint> itWeightsUsed = usedWeights;
            // Obtain a set of "used" weights
            QSet<uint> weightCountsSet;
            while (itWeightsUsed.hasNext()) {
                itWeightsUsed.next();
                weightCountsSet.insert(itWeightsUsed.value());
            }
            // Obtains a list of those weights sorted in ascending count of used
            QList<uint> weightCountsList = weightCountsSet.toList();
            if (weightCountsList.size() > 1) {
                std::sort(weightCountsList.begin(), weightCountsList.end());
            }
            // Build a list of the "used" weights in decending count of use
            QStringList displayStrings;
            for (int i = weightCountsList.size() - 1; i >= 0; --i) {
                itWeightsUsed.toFront();
                while (itWeightsUsed.hasNext()) {
                    itWeightsUsed.next();
                    if (itWeightsUsed.value() == weightCountsList.at(i)) {
                        if (itWeightsUsed.key() == 1) { // Indicate the "default" value which is unity weight
                            displayStrings.append(tr("%1 {count:%2, default}").arg(itWeightsUsed.key(), itWeightsUsed.value()));
                        } else {
                            displayStrings.append(tr("%1 {count:%2}").arg(itWeightsUsed.key(), itWeightsUsed.value()));
                        }
                    }
                }
            }
            if (!usedWeights.contains(1)) { // If unity weight was not used insert it at end of list
                displayStrings.append(tr("1 {count 0, default}"));
            }
            QString newWeightText = QInputDialog::getItem(this,                    // QWidget * parent
                                                          tr("Enter room weight"), // const QString & title
                                                          tr("Choose an existing\n"
                                                             "roomweight (= travel\n"
                                                             "time) from the list\n"
                                                             "(sorted by most commonly\n"
                                                             "used first) or enter a\n"
                                                             "new (positive) integer\n"
                                                             "value for all selected\n"
                                                             "rooms:",
                                                             "Use line feeds to format text into a reasonable rectangle."), // const QString & label
                                                          displayStrings,                                                   // QStringList & items
                                                          0,                                                                // int current = 0, last value in list
                                                          true,                                                             // bool editable = true
                                                          &isOk,                                                            // bool * ok = 0
                                                          nullptr,                                                                // Qt::WindowFlags flags = 0
                                                          Qt::ImhDigitsOnly);                                               // Qt::InputMethodHints inputMethodHints = Qt::ImhNone
            newWeight = 1;
            if (isOk) { // Don't do anything if cancel was pressed
                if (newWeightText.toInt() > 0) {
                    newWeight = newWeightText.toInt();
                } else {
                    isOk = false; // Prevent any change if the value is not reasonable
                }
            }
        }
    }

    if (isOk && newWeight > 0) { // Don't proceed if cancel was pressed or the value is not valid
        itSelectedRoom.toFront();
        while (itSelectedRoom.hasNext()) {
            pR = mpMap->mpRoomDB->getRoom(itSelectedRoom.next());
            if (!pR) {
                continue;
            }

            pR->setWeight(newWeight);
        }
        mpMap->mMapGraphNeedsUpdate = true;
        repaint();
    }
}

void T2DMap::slot_setArea()
{
    QUiLoader loader;

    QFile file(":/ui/set_room_area.ui");
    file.open(QFile::ReadOnly);
    QDialog* set_room_area_dialog = dynamic_cast<QDialog*>(loader.load(&file, this));
    file.close();
    if (!set_room_area_dialog) {
        return;
    }
    arealist_combobox = set_room_area_dialog->findChild<QComboBox*>("arealist_combobox");
    if (!arealist_combobox) {
        return;
    }

    QMapIterator<int, QString> it(mpMap->mpRoomDB->getAreaNamesMap());
    while (it.hasNext()) {
        it.next();
        int areaID = it.key();
        if (areaID > 0) {
            arealist_combobox->addItem(QStringLiteral("%1 (%2)").arg(it.value(), QString::number(areaID)), QVariant(areaID));
        }
    }

    if (set_room_area_dialog->exec() == QDialog::Rejected) { // Don't proceed if "cancel" was pressed
        return;
    }

    int newAreaId = arealist_combobox->itemData(arealist_combobox->currentIndex()).toInt();
    mMultiRect = QRect(0, 0, 0, 0);
    QSetIterator<int> itSelectedRoom = mMultiSelectionSet;
    while (itSelectedRoom.hasNext()) {
        int currentRoomId = itSelectedRoom.next();
        if (itSelectedRoom.hasNext()) { // NOT the last room in set -  so defer some area related recalculations
            mpMap->setRoomArea(currentRoomId, newAreaId, true);
        } else {
            // Is the LAST room, so be careful to do all that is needed to clean
            // up the affected areas (triggered by last "false" argument in next
            // line)...
            if (!(mpMap->setRoomArea(currentRoomId, newAreaId, false))) {
                // Failed on the last of multiple room area move so do the missed
                // out recalculations for the dirtied areas
                QSetIterator<TArea*> itpArea = mpMap->mpRoomDB->getAreaPtrList().toSet();
                while (itpArea.hasNext()) {
                    TArea* pArea = itpArea.next();
                    if (pArea->mIsDirty) {
                        pArea->determineAreaExits();
                        pArea->calcSpan();
                        pArea->mIsDirty = false;
                    }
                }
            }
        }
    }
    repaint();
}


void T2DMap::mouseMoveEvent(QMouseEvent* event)
{
    if (mpMap->mLeftDown && !mpMap->m2DPanMode && event->modifiers().testFlag(Qt::AltModifier)) {
        mpMap->m2DPanXStart = event->x();
        mpMap->m2DPanYStart = event->y();
        mpMap->m2DPanMode = true;
    }
    if (mpMap->m2DPanMode && !event->modifiers().testFlag(Qt::AltModifier)) {
        mpMap->m2DPanMode = false;
        mpMap->mLeftDown = false;
    }
    if (mpMap->m2DPanMode) {
        int x = event->x();
        int y = height() - event->y();
        if ((mpMap->m2DPanXStart - x) > 1) {
            shiftRight();
            mpMap->m2DPanXStart = x;
        } else if ((mpMap->m2DPanXStart - x) < -1) {
            shiftLeft();
            mpMap->m2DPanXStart = x;
        }
        if ((mpMap->m2DPanYStart - y) > 1) {
            shiftDown();
            mpMap->m2DPanYStart = y;
        } else if ((mpMap->m2DPanYStart - y) < -1) {
            shiftUp();
            mpMap->m2DPanYStart = y;
        }
        return;
    }

    if (mCustomLineSelectedRoom != 0 && mCustomLineSelectedPoint >= 0) {
        TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLineSelectedRoom);
        if (pR) {
            if (pR->customLines.contains(mCustomLineSelectedExit)) {
                if (pR->customLines[mCustomLineSelectedExit].size() > mCustomLineSelectedPoint) {
                    float mx = event->pos().x() / mTX + mOx;
                    float my = event->pos().y() / mTY + mOy;
                    mx = mx - xspan / 2;
                    my = yspan / 2 - my;
                    QPointF pc = QPointF(mx, my);
                    pR->customLines[mCustomLineSelectedExit][mCustomLineSelectedPoint] = pc;
                    pR->calcRoomDimensions();
                    repaint();
                    return;
                }
            }
        }
    }

    mCustomLineSelectedPoint = -1;

    //FIXME:
    if (mLabelHilite) //mMoveLabel )//&& mLabelHilite )
    {
        if (mpMap->mapLabels.contains(mAID)) {
            QMapIterator<int, TMapLabel> it(mpMap->mapLabels[mAID]);
            while (it.hasNext()) {
                it.next();
                if (it.value().pos.z() != mOz) {
                    continue;
                }
                if (!it.value().hilite) {
                    continue;
                }
                int mx = event->pos().x() / mTX + mOx;
                int my = event->pos().y() / mTY + mOy;
                mx = mx - xspan / 2;
                my = yspan / 2 - my;
                QVector3D p = QVector3D(mx, my, mOz);
                mpMap->mapLabels[mAID][it.key()].pos = p;
            }
        }
        update();
    } else {
        mMoveLabel = false;
    }

    if ((mMultiSelection && !mRoomBeingMoved) || mSizeLabel) {
        //    (The drag to select (or size) rectangle is being actively resized and rooms are not being moved)
        // OR (We are sizing up a label)
        if (mNewMoveAction) {
            mMultiRect = QRect(event->pos(), event->pos());
            mNewMoveAction = false;
        } else {
            mMultiRect.setBottomLeft(event->pos());
        }

        int _roomID = mRID;
        if (!mpMap->mpRoomDB->getRoom(_roomID)) {
            return;
        }
        int _areaID = mAID;
        TArea* pArea = mpMap->mpRoomDB->getArea(_areaID);
        if (!pArea) {
            return;
        }

        float _rx = xspan / 2 * mTX - mTX * mOx;
        float _ry = yspan / 2 * mTY - mTY * mOy;

        if (!mSizeLabel) { // NOT sizing a label
            mMultiSelectionSet.clear();
            QSetIterator<int> itSelectedRoom(pArea->getAreaRooms());
            while (itSelectedRoom.hasNext()) {
                int currentRoomId = itSelectedRoom.next();
                TRoom* pR = mpMap->mpRoomDB->getRoom(currentRoomId);
                if (!pR) {
                    continue;
                }
                int rx = pR->x * mTX + _rx;
                int ry = pR->y * -1 * mTY + _ry;
                int rz = pR->z;

                // copy rooms on all z-levels if the shift key is being pressed
                // CHECK: Consider adding z-level to multi-selection Widget?
                if (rz != mOz && !(event->modifiers().testFlag(Qt::ShiftModifier))) {
                    continue;
                }

                QRectF dr;
                if (pArea->gridMode) {
                    dr = QRectF(rx - mTX / 2, ry - mTY / 2, mTX, mTY);
                } else {
                    dr = QRectF(rx - (mTX * rSize) / 2, ry - (mTY * rSize) / 2, mTX * rSize, mTY * rSize);
                }
                if (mMultiRect.contains(dr)) {
                    mMultiSelectionSet.insert(currentRoomId);
                }
            }
            switch (mMultiSelectionSet.size()) {
            case 0:
                mMultiSelectionHighlightRoomId = -1;
                break;
            case 1:
                mMultiSelectionHighlightRoomId = mMultiSelectionSet.toList().first();
                break;
            default:
                getCenterSelection(); // Sets mMultiSelectionHighlightRoomId to (a) central room
            }

            int mx = event->pos().x() / mTX;
            int my = event->pos().y() / mTY;
            mx = mx - xspan / 2 + 1;
            my = yspan / 2 - my - 1;
            TRoom* pR = mpMap->mpRoomDB->getRoom(mRID);
            if (pR) {
                mx += pR->x;
                my += pR->y;
                mOldMousePos = QPoint(mx, my);
            }

            if (mMultiSelectionSet.size() > 1) {
                mMultiSelectionListWidget.blockSignals(true);                       // We don't want to cause calls to slot_roomSelectionChanged() here!
                mIsSelectionSorting = mMultiSelectionListWidget.isSortingEnabled(); // Save sorting state before we switch it off
                mIsSelectionSortByNames = (mMultiSelectionListWidget.sortColumn() == 1);
                mMultiSelectionListWidget.clear();
                mMultiSelectionListWidget.setSortingEnabled(false); // Do NOT sort whilst inserting items!
                QSetIterator<int> itRoom = mMultiSelectionSet;
                mIsSelectionUsingNames = false;
                while (itRoom.hasNext()) {
                    auto _item = new QTreeWidgetItem;
                    int multiSelectionRoomId = itRoom.next();
                    _item->setText(0, QStringLiteral("%1").arg(multiSelectionRoomId, 7));
                    _item->setTextAlignment(0, Qt::AlignRight);
                    TRoom* pR_multiSelection = mpMap->mpRoomDB->getRoom(multiSelectionRoomId);
                    if (pR_multiSelection) {
                        QString multiSelectionRoomName = pR_multiSelection->name;
                        if (!multiSelectionRoomName.isEmpty()) {
                            _item->setText(1, multiSelectionRoomName);
                            _item->setTextAlignment(1, Qt::AlignLeft);
                            mIsSelectionUsingNames = true;
                        }
                    }
                    mMultiSelectionListWidget.addTopLevelItem(_item);
                }
                mMultiSelectionListWidget.setColumnHidden(1, !mIsSelectionUsingNames);
                // Can't sort if nothing to sort on, switch to sorting by room number
                if ((!mIsSelectionUsingNames) && mIsSelectionSortByNames && mIsSelectionSorting) {
                    mIsSelectionSortByNames = false;
                }
                mMultiSelectionListWidget.sortByColumn(mIsSelectionSortByNames ? 1 : 0);
                mMultiSelectionListWidget.setSortingEnabled(mIsSelectionSorting);
                resizeMultiSelectionWidget();
                mMultiSelectionListWidget.selectAll();
                mMultiSelectionListWidget.blockSignals(false);
                mMultiSelectionListWidget.show();
            } else {
                mMultiSelectionListWidget.hide();
            }
        }

        update();
        return;
    }

    if (mRoomBeingMoved && !mSizeLabel && !mMultiSelectionSet.isEmpty()) {
        mMultiRect = QRect(0, 0, 0, 0);
        int _roomID = mRID;
        if (!mpMap->mpRoomDB->getRoom(_roomID)) {
            return;
        }
        int _areaID = mAID;
        TArea* pArea = mpMap->mpRoomDB->getArea(_areaID);
        if (!pArea) {
            return;
        }

        // N/U:        int ox = mOx;
        // N/U:        int oy = mOy;
        // N/U:        float _rx;
        // N/U:        float _ry;
        // These tests are pointless - both branches produce the same results
        //        if( ox*mTX > xspan/2*mTX )
        //            _rx = -(mTX*ox-xspan/2*mTX);
        //        else
        //            _rx = xspan/2*mTX-mTX*ox;
        //        if( oy*mTY > yspan/2*mTY )
        //            _ry = -(mTY*oy-yspan/2*mTY);
        //        else
        //            _ry = yspan/2*mTY-mTY*oy;
        int mx = event->pos().x() / mTX + mOx;
        int my = event->pos().y() / mTY + mOy;
        mx = mx - xspan / 2 + 1;
        my = yspan / 2 - my - 1;

        int dx, dy;

        if (!getCenterSelection()) {
            return;
        }

        TRoom* pR = mpMap->mpRoomDB->getRoom(mMultiSelectionHighlightRoomId);
        if (!pR) {
            return;
        }

        dx = mx - pR->x;
        dy = my - pR->y;
        QSetIterator<int> itRoom = mMultiSelectionSet;
        while (itRoom.hasNext()) {
            pR = mpMap->mpRoomDB->getRoom(itRoom.next());
            if (pR) {
                pR->x += dx;
                pR->y += dy;
                pR->z = mOz; // allow groups to be moved to a different z-level with the map editor

                QMapIterator<QString, QList<QPointF>> itk(pR->customLines);
                QMap<QString, QList<QPointF>> newMap;
                while (itk.hasNext()) {
                    itk.next();
                    QList<QPointF> _pL = itk.value();
                    for (auto& point : _pL) {
                        QPointF op = point;
                        point.setX((float)(op.x() + dx));
                        point.setY((float)(op.y() + dy));
                    }
                    newMap.insert(itk.key(), _pL);
                }
                pR->customLines = newMap;
                pR->calcRoomDimensions();
            }
        }
        repaint();
    }
}

// Replacement for getTopLeftCenter - determines a room closest to geometrical
// mean of all the room selected, the result is stored in the class member
// mMultiSelectionHighlightRoomId and this returns true on successfully finding
// such a room
bool T2DMap::getCenterSelection()
{
    mMultiSelectionHighlightRoomId = -1;
    if (mMultiSelectionSet.isEmpty()) {
        return false;
    }

    QSetIterator<int> itRoom = mMultiSelectionSet;
    float mean_x = 0.0;
    float mean_y = 0.0;
    float mean_z = 0.0;
    uint processedRoomCount = 0;
    while (itRoom.hasNext()) {
        int currentRoomId = itRoom.next();
        TRoom* pR = mpMap->mpRoomDB->getRoom(currentRoomId);
        if (!pR) {
            continue;
        }

        mean_x += (static_cast<float>(pR->x - mean_x)) / ++processedRoomCount;
        mean_y += (static_cast<float>(pR->y - mean_y)) / processedRoomCount;
        mean_z += (static_cast<float>(pR->z - mean_z)) / processedRoomCount;
    }

    if (processedRoomCount) {
        itRoom.toFront();
        float closestSquareDistance = -1.0;
        while (itRoom.hasNext()) {
            int currentRoomId = itRoom.next();
            TRoom* pR = mpMap->mpRoomDB->getRoom(currentRoomId);
            if (!pR) {
                continue;
            }

            QVector3D meanToRoom(static_cast<float>(pR->x) - mean_x, static_cast<float>(pR->y) - mean_y, static_cast<float>(pR->z) - mean_z);
            if (closestSquareDistance < -0.5) {
                // Don't use an equality to zero test, we are using floats so
                // need to allow for a little bit of fuzzzyness!
                closestSquareDistance = meanToRoom.lengthSquared();
                mMultiSelectionHighlightRoomId = currentRoomId;
            } else {
                float currentRoomSquareDistance = meanToRoom.lengthSquared();
                if (closestSquareDistance > currentRoomSquareDistance) {
                    closestSquareDistance = currentRoomSquareDistance;
                    mMultiSelectionHighlightRoomId = currentRoomId;
                }
            }
        }
        return true;
    } else {
        return false;
    }
}

void T2DMap::exportAreaImage(int id)
{
    paintMap();
}

void T2DMap::wheelEvent(QWheelEvent* e)
{
    // If the mouse wheel is scrolling up and down through the
    // mMultiSelectionListWidget the wheelevents from that get passed up to here
    // when the end of the list is reached (i.e when it rejects those events)
    // - so that the mapper window doesn't THEN zoom in or out, swallow the
    // events, i.e. accept() them and return before hitting the zoom altering
    // code that follows.
    // However the event "pos()" depends on the widget it came from so we have
    // to use "globalPos()" instead and see how it lies in relation to the child
    // widget:
    QRect selectionListWidgetGlobalRect = QRect(mapToGlobal(mMultiSelectionListWidget.frameRect().topLeft()), mapToGlobal(mMultiSelectionListWidget.frameRect().bottomRight()));
    if (mMultiSelectionListWidget.isVisible() && selectionListWidgetGlobalRect.contains(e->globalPos())) {
        e->accept();
        return;
    }

    if (!(mpMap->mpRoomDB->getRoom(mRID) && mpMap->mpRoomDB->getArea(mAID))) {
        return;
    }

    // int delta = e->delta() / 8 / 15; // Deprecated in Qt 5.x ...!
    int delta = e->angleDelta().y() / (8 * 15);
    if (e->modifiers() & Qt::ControlModifier) { // Increase rate 10-fold if control key down - it makes scrolling through
                                                // a large nuber of items in a listwidget's contents easier AND this make it
                                                // easier to zoom in and out on LARGE area maps
        delta *= 10;
    }
    if (delta != 0) {
        mPick = false;
        xzoom += delta;
        yzoom += delta;
        if (yzoom < 3) { // At present xzoom and yzoom alway will be the same I think
            yzoom = 3;
        }
        if (xzoom < 3) {
            xzoom = 3;
        }
        update();
        e->accept();
        return;
    }
    e->ignore();
    return;
}

void T2DMap::setMapZoom(int zoom)
{
    xzoom = zoom;
    yzoom = zoom;
    if (yzoom < 3 || xzoom < 3) {
        xzoom = 3;
        yzoom = 3;
    }
}

void T2DMap::setRoomSize(double f)
{
    rSize = f;
    if (mpHost) {
        mpHost->mRoomSize = f;
    }
}

void T2DMap::setExitSize(double f)
{
    eSize = f;
    if (mpHost) {
        mpHost->mLineSize = f;
    }
}

void T2DMap::slot_setCustomLine()
{
    if (mMultiSelectionSet.isEmpty()) {
        return;
    }
    TRoom* pR = mpMap->mpRoomDB->getRoom(mMultiSelectionHighlightRoomId);
    if (!pR) {
        return;
    }

    if (mpCustomLinesDialog) {
        // Refuse to create another instance if one is already present!
        // Just show it...
        mpCustomLinesDialog->raise();
        return;
    }

    QUiLoader loader;

    QFile file(":/ui/custom_lines.ui");
    file.open(QFile::ReadOnly);
    QDialog* d = dynamic_cast<QDialog*>(loader.load(&file, this));
    file.close();
    if (!d) {
        return;
    }
    d->setWindowIcon(QIcon(QStringLiteral(":/icons/mudlet_custom_exit.png")));
    mCustomLinesRoomFrom = mMultiSelectionHighlightRoomId;
    mCustomLinesRoomTo = 0;
    mCustomLinesRoomExit = "";
    QPushButton* b_ = d->findChild<QPushButton*>("nw");
    QTreeWidget* specialExits = d->findChild<QTreeWidget*>("specialExits");
    mpCurrentLineStyle = d->findChild<QComboBox*>("lineStyle");
    mpCurrentLineColor = d->findChild<QPushButton*>("lineColor");
    mpCurrentLineArrow = d->findChild<QCheckBox*>("arrow");
    if (!b_ || !specialExits || !mpCurrentLineColor || !mpCurrentLineStyle || !mpCurrentLineArrow) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "nw" exit line button or another element of the dialog!)");
        return;
    } else if (pR->getNorthwest() <= 0) {
        b_->setCheckable(false);
        b_->setDisabled(true);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("NW") || pR->customLines.contains("nw"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("n");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "n" exit line button!)");
        return;
    } else if (pR->getNorth() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("N") || pR->customLines.contains("n"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("ne");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "ne" exit line button!)");
        return;
    } else if (pR->getNortheast() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("NE") || pR->customLines.contains("ne"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("up");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "up" exit line button!)");
        return;
    } else if (pR->getUp() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("UP") || pR->customLines.contains("up"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("w");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "w" exit line button!)");
        return;
    } else if (pR->getWest() <= 0) {
        b_->setCheckable(false);
        b_->setDisabled(true);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("W") || pR->customLines.contains("w"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("e");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "e" exit line button!)");
        return;
    } else if (pR->getEast() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("E") || pR->customLines.contains("e"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("down");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "down" exit line button!)");
        return;
    } else if (pR->getDown() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("DOWN") || pR->customLines.contains("down"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("sw");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "sw" exit line button!)");
        return;
    } else if (pR->getSouthwest() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("SW") || pR->customLines.contains("sw"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("s");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "s" exit line button!)");
        return;
    } else if (pR->getSouth() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("S") || pR->customLines.contains("s"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("se");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "se" exit line button!)");
        return;
    } else if (pR->getSoutheast() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("SE") || pR->customLines.contains("se"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("in");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "in" exit line button!)");
        return;
    } else if (pR->getIn() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("IN") || pR->customLines.contains("in"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    b_ = d->findChild<QPushButton*>("out");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "out" exit line button!)");
        return;
    } else if (pR->getOut() <= 0) {
        b_->setDisabled(true);
        b_->setCheckable(false);
    } else {
        b_->setCheckable(true);
        b_->setChecked(pR->customLines.contains("OUT") || pR->customLines.contains("out"));
        connect(b_, SIGNAL(clicked()), this, SLOT(slot_setCustomLine2()));
    }

    QMapIterator<int, QString> it(pR->getOtherMap());
    while (it.hasNext()) {
        it.next();
        int id_to = it.key();
        QString dir = it.value();
        if (dir.size() > 1) {
            if (dir.startsWith('0') || dir.startsWith('1')) {
                dir = dir.mid(1);
            }
        }
        auto pI = new QTreeWidgetItem(specialExits);
        if (pR->customLines.contains(dir)) {
            pI->setCheckState(0, Qt::Checked);
        } else {
            pI->setCheckState(0, Qt::Unchecked);
        }
        pI->setTextAlignment(0, Qt::AlignHCenter);
        pI->setText(1, QString::number(id_to));
        pI->setTextAlignment(1, Qt::AlignRight);
        pI->setText(2, dir);
        pI->setTextAlignment(2, Qt::AlignLeft);
    }

    b_ = d->findChild<QPushButton*>("button_cancel");
    if (!b_) {
        qWarning(R"(T2DMap::slot_setCustomLine() ERROR: failed to find "cancel" button!)");
        return;
    }
    connect(b_, SIGNAL(clicked()), d, SLOT(reject()));
    connect(d, SIGNAL(rejected()), this, SLOT(slot_cancelCustomLineDialog()));

    QStringList _lineStyles;
    _lineStyles << "solid line"
                << "dot line"
                << "dash line"
                << "dash dot line"
                << "dash dot dot line";
    mpCurrentLineStyle->addItems(_lineStyles);
    mpCurrentLineStyle->setCurrentText(mCurrentLineStyle);
    mpCurrentLineArrow->setChecked(mCurrentLineArrow);
    mpCurrentLineColor->setStyleSheet("background-color:" + mCurrentLineColor.name());
    connect(specialExits, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(slot_setCustomLine2B(QTreeWidgetItem*, int)));
    connect(mpCurrentLineColor, SIGNAL(clicked()), this, SLOT(slot_customLineColor()));
    mpCustomLinesDialog = d; // Don't assign the pointer value to the class memeber until ready to go
    mpCustomLinesDialog->show();
    mpCustomLinesDialog->raise();
    mDialogLock = true; // Prevent any line drawing until dialog has been used
}

void T2DMap::slot_customLineColor()
{
    QColor color;
    if (mCurrentLineColor.isValid()) {
        color = QColorDialog::getColor(mCurrentLineColor, this);
    } else {
        color = QColorDialog::getColor(mpHost->mFgColor_2, this);
    }

    if (color.isValid()) {
        mCurrentLineColor = color;
        QString styleSheet = QString("background-color:" + color.name());
        mpCurrentLineColor->setStyleSheet(styleSheet);
    }
}

// Called by dialog's reject event which is caused at least by "X" button on
// title bar and by ESC keypress...
void T2DMap::slot_cancelCustomLineDialog()
{
    mpCustomLinesDialog->deleteLater();
    mpCustomLinesDialog = nullptr;
    mCustomLinesRoomFrom = 0;
    mCustomLinesRoomTo = 0;
    mCustomLinesRoomExit.clear();
    mDialogLock = false;
}

void T2DMap::slot_setCustomLine2()
{
    QPushButton* pB = qobject_cast<QPushButton*>(sender());
    if (!pB) {
        if (mpCustomLinesDialog) {
            mpCustomLinesDialog->reject();
        } else {
            // This is needed to escape from custom line exit drawing mode if
            // the dialog has disappeared, not likely I think/hope
            mCustomLinesRoomFrom = 0;
            mCustomLinesRoomTo = 0;
            mCustomLinesRoomExit.clear();
            mDialogLock = false;
        }
        return;
    }
    QString exit = pB->text();
    mpCustomLinesDialog->hide(); // Hide but don't delete until done the custom line
    mCustomLinesRoomExit = exit;
    mDialogLock = false;
    TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLinesRoomFrom);
    if (!pR) {
        return;
    }
    if (exit == "NW") {
        mCustomLinesRoomTo = pR->getNorthwest(); // mCustomLinesRoomTo - wasn't being set!
    } else if (exit == "N") {
        mCustomLinesRoomTo = pR->getNorth();
    } else if (exit == "NE") {
        mCustomLinesRoomTo = pR->getNortheast();
    } else if (exit == "UP") {
        mCustomLinesRoomTo = pR->getUp();
    } else if (exit == "W") {
        mCustomLinesRoomTo = pR->getWest();
    } else if (exit == "E") {
        mCustomLinesRoomTo = pR->getEast();
    } else if (exit == "DOWN") {
        mCustomLinesRoomTo = pR->getDown();
    } else if (exit == "SW") {
        mCustomLinesRoomTo = pR->getSouthwest();
    } else if (exit == "S") {
        mCustomLinesRoomTo = pR->getSouth();
    } else if (exit == "SE") {
        mCustomLinesRoomTo = pR->getSoutheast();
    } else if (exit == "IN") {
        mCustomLinesRoomTo = pR->getIn();
    } else if (exit == "OUT") {
        mCustomLinesRoomTo = pR->getOut();
    } else {
        qWarning(R"(T2DMap::slot_setCustomLine2(): unable to identify exit "%s"to use!)", qPrintable(exit));
        return;
    }
    QList<QPointF> _list;
    pR->customLines[exit] = _list;
    QList<int> _colorList;
    //    qDebug("T2DMap::slot_setCustomLine2() NORMAL EXIT: %s", qPrintable(exit));
    _colorList << mCurrentLineColor.red() << mCurrentLineColor.green() << mCurrentLineColor.blue();
    pR->customLinesColor[exit] = _colorList;
    /*
	 *    qDebug("   COLOR(r,g,b): %i,%i,%i",
	 *            mCurrentLineColor.red(),
	 *            mCurrentLineColor.green(),
	 *            mCurrentLineColor.blue() );
	 */
    pR->customLinesStyle[exit] = mCurrentLineStyle;
    //    qDebug("   LINE STYLE: %s", qPrintable(mCurrentLineStyle) );
    pR->customLinesArrow[exit] = mCurrentLineArrow;
    //    qDebug("   ARROW: %s", mCurrentLineArrow ? "Yes" : "No");

    mHelpMsg = tr("Left-click to add point, right-click to undo/change/finish...");
    // This message was previously being put up AFTER first click to set first segment was made....
    update();
}

void T2DMap::slot_setCustomLine2B(QTreeWidgetItem* special_exit, int column)
{
    Q_UNUSED(column);
    if (!special_exit) {
        return;
    }
    QString exit = special_exit->text(2);
    mpCustomLinesDialog->hide(); // Hide but don't delete until done the custom line
    mCustomLinesRoomExit = exit;
    mCustomLinesRoomTo = special_exit->text(1).toInt(); // Wasn't being set !
    mDialogLock = false;
    TRoom* pR = mpMap->mpRoomDB->getRoom(mCustomLinesRoomFrom);
    if (!pR) {
        return;
    }
    QList<QPointF> _list;
    pR->customLines[exit] = _list;
    QList<int> _colorList;
    _colorList << mCurrentLineColor.red() << mCurrentLineColor.green() << mCurrentLineColor.blue();
    //    qDebug("T2DMap::slot_setCustomLine2B() SPECIAL EXIT: %s", qPrintable(exit));
    pR->customLinesColor[exit] = _colorList;
    /*
	 *     qDebug("   COLOR(r,g,b): %i,%i,%i",
	 *            mCurrentLineColor.red(),
	 *            mCurrentLineColor.green(),
	 *            mCurrentLineColor.blue() );
	 */
    pR->customLinesStyle[exit] = mCurrentLineStyle;
    //    qDebug("   LINE STYLE: %s", qPrintable(mCurrentLineStyle) );
    pR->customLinesArrow[exit] = mCurrentLineArrow;
    //    qDebug("   ARROW: %s", mCurrentLineArrow ? "Yes" : "No");
    mHelpMsg = tr("Left-click to add point, right-click to undo/change/finish...");
    // This message was previously being put up AFTER first click to set first segment was made....
    update();
}

void T2DMap::slot_createLabel()
{
    if (!mpMap->mpRoomDB->getArea(mAID)) {
        return;
    }

    mHelpMsg = tr("Left-click and drag a square for the size and position of your label");
    mSizeLabel = true;
    mMultiSelection = true;
    update();
}

void T2DMap::slot_roomSelectionChanged()
{
    QList<QTreeWidgetItem*> _sl = mMultiSelectionListWidget.selectedItems();
    mMultiSelectionSet.clear();
    for (auto treeWidgetItem : _sl) {
        int currentRoomId = treeWidgetItem->text(0).toInt();
        mMultiSelectionSet.insert(currentRoomId);
    }
    switch (mMultiSelectionSet.size()) {
    case 0:
        mMultiSelectionHighlightRoomId = -1;
        break;
    case 1:
        mMultiSelectionHighlightRoomId = *(mMultiSelectionSet.constBegin());
        break;
    default:
        getCenterSelection();
    }
    update();
}

void T2DMap::paintMap()
{
    //    if( !mpMap ) return;
    //    bool __Pick = mPick;
    //    QTime __time; __time.start();

    //    mAreaExitList.clear();
    //    int px,py;
    //    QList<int> exitList;
    //    QList<int> oneWayExits;

    //    int ox, oy, oz;

    //    if( !mpMap ) return;
    //    if( !mpMap->areas.contains(mAID)) return;

    //    mpMap->areas[mAID]->calcSpan();
    //    int x_min;
    //    int y_min;
    //    int x_max;
    //    int y_max;
    //    if( ! mpMap->areas[mAID]->xminEbene.contains(mOz) )
    //    {
    //        ox = 0;
    //        oy = 0;
    //        oz = 0;
    //    }
    //    else
    //    {
    //        x_min = mpMap->areas[mAID]->xminEbene[mOz];
    //        y_min = mpMap->areas[mAID]->yminEbene[mOz];
    //        x_max = mpMap->areas[mAID]->xmaxEbene[mOz];
    //        y_max = mpMap->areas[mAID]->ymaxEbene[mOz];
    //        ox = x_min + ( abs( x_max - x_min ) / 2 );
    //        oy = ( y_min + ( abs( y_max - y_min ) / 2 ) ) * -1;
    //        oz = 0;
    //    }

    //    int sizex, sizey;
    //    if( x_max < 0 ) sizex = abs(abs(x_min)-abs(x_max));
    //    if( x_max > 0 && x_min > 0 ) sizex = x_max-x_min;
    //    if( x_max >= 0 && x_min <= 0 ) sizex = abs(x_max+abs(x_min));

    //    if( y_max < 0 ) sizey = abs(abs(y_min)-abs(y_max));
    //    if( y_max > 0 && y_min > 0 ) sizey = y_max-y_min;
    //    if( y_max >= 0 && y_min <= 0 ) sizey = abs(y_max+abs(y_min));

    //    sizex += 10;
    //    sizey += 10;

    //    if( sizex > sizey )
    //        sizey = sizex;
    //    else
    //        sizex = sizey;

    //    xzoom = 30;
    //    yzoom = 30;

    //    // Qt png limits
    //    //if( sizex*xzoom >= 32768 )
    //        xzoom = 10000/sizex;
    //    //if( sizey*yzoom >= 32768 )
    //        yzoom = 10000/sizey;

    //    if(xzoom > yzoom)
    //        xzoom = yzoom;
    //    else
    //        yzoom = xzoom;

    //    xspan = xzoom;
    //    yspan = yzoom;

    //    float _w = sizex*xspan;
    //    float _h = sizey*yspan;
    //    float tx = xspan;
    //    float ty = yspan;

    //    mTX = tx;
    //    mTY = ty;

    //    oy *= -1;

    //    if( ox*tx > (sizex/2)*tx )
    //        _rx = -(tx*ox-(sizex/2)*tx);
    //    else
    //        _rx = (sizex/2)*tx-tx*ox;
    //    if( oy*ty > (sizey/2)*ty )
    //        _ry = -(ty*oy-(sizey/2)*ty);
    //    else
    //        _ry = (sizey/2)*ty-ty*oy;

    //    px = ox*tx+_rx;
    //    py = oy*ty+_ry;

    //    TArea * pArea = mpMap->areas[mAID];
    //    if( ! pArea ) return;

    //    int zEbene;
    //    zEbene = mOz;

    //    if( ! mpMap ) return;
    //    if( ! mpMap->rooms.contains( mRID ) ) return;

    //    float wegBreite = 1/eSize * tx * rSize;

    //    if( ! mpMap->areas.contains(mAID) ) return;

    //    TArea * pA = mpMap->areas[mAID];

    //    if( pA->rooms.size() < 1 ) return;

    //    QPixmap pix = QPixmap(sizex*xspan, sizey*yspan);
    //    QPainter p( &pix );
    //    if( !p.isActive() )
    //    {
    //        cout << "ERROR: no active painter"<<endl;
    //        return;
    //    }
    //    pix.fill( mpHost->mBgColor_2 );

    //    QPen pen;

    //    pen = p.pen();
    //    pen.setColor( mpHost->mFgColor_2 );
    //    pen.setWidthF(wegBreite);
    //    if(mMapperUseAntiAlias)
    //        p.setRenderHint(QPainter::Antialiasing);
    //    else
    //        p.setRenderHint(QPainter::NonCosmeticDefaultPen);
    //    p.setPen( pen );

    //    if( mpMap->mapLabels.contains( mAID ) )
    //    {
    //        QMapIterator<int, TMapLabel> it(mpMap->mapLabels[mAID]);
    //        while( it.hasNext() )
    //        {
    //            it.next();
    //            if( it.value().pos.z() != mOz ) continue;
    //            if( it.value().text.length() < 1 )
    //            {
    //                mpMap->mapLabels[mAID][it.key()].text = "no text";
    //            }
    //            QPointF lpos;
    //            int _lx = it.value().pos.x()*tx+_rx;
    //            int _ly = it.value().pos.y()*ty*-1+_ry;

    //            lpos.setX( _lx );
    //            lpos.setY( _ly );
    //            int _lw = abs(it.value().size.width())*tx;
    //            int _lh = abs(it.value().size.height())*ty;

    //            QRectF _drawRect = QRect(it.value().pos.x()*tx+_rx, it.value().pos.y()*ty*-1+_ry, _lw, _lh);
    //            if ( !it.value().pix.isNull() )
    //            {
    //                p.drawPixmap( lpos, it.value().pix.scaled(_drawRect.size().toSize()) );
    //            }
    //            else
    //            {
    //                if( it.value().text.length() < 1 )
    //                {
    //                    mpMap->mapLabels[mAID][it.key()].text = "no text";
    //                }
    //                QRectF lr = QRectF( 0, 0, 1000, 100 );
    //                QPixmap pix( lr.size().toSize() );
    //                pix.fill(Qt::transparent);
    //                QPainter lp( &pix );

    //                if( it.value().hilite )
    //                {
    //                    lp.fillRect( lr, QColor(255,155,55) );
    //                }
    //                else
    //                {
    //                    lp.fillRect( lr, it.value().bgColor );
    //                }
    //                QPen lpen;
    //                lpen.setColor( it.value().fgColor );
    //                lp.setPen( lpen );
    //                QRectF br;
    //                lp.drawText( lr, Qt::AlignLeft, it.value().text, &br );
    //                QPointF lpos;
    //                lpos.setX( it.value().pos.x()*tx+_rx );
    //                lpos.setY( it.value().pos.y()*ty*-1+_ry );
    //                p.drawPixmap( lpos, pix, br.toRect() );
    //            }
    //            if( it.value().hilite )
    //            {
    //                p.fillRect(_drawRect, QColor(255, 155, 55, 190));
    //            }

    //        }
    //    }

    //    if( ! pArea->gridMode )
    //    {
    //        for( int i=0; i<pArea->rooms.size(); i++ )
    //        {
    //            TRoom * pR = mpMap->rooms[pArea->rooms[i]];
    //            int trID = pArea->rooms[i];
    //            float rx = pR->x*tx+_rx;
    //            float ry = pR->y*-1*ty+_ry;
    //            int rz = pR->z;

    //            if( rz != zEbene && !mMultiSelectionList.contains(i)) continue;

    //            pR->rendered = true;

    //            exitList.clear();
    //            oneWayExits.clear();
    //            if( pR->customLines.size() > 0 )
    //            {
    //                if( ! pR->customLines.contains("N") )
    //                {
    //                    exitList.push_back( pR->north );
    //                    if( mpMap->rooms.contains(pR->north) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->north];
    //                        if( pER->south != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->north );
    //                        }
    //                    }
    //                }
    //                if( !pR->customLines.contains("NW") )
    //                {
    //                    exitList.push_back( pR->northwest );
    //                    if( mpMap->rooms.contains(pR->northwest) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->northwest];
    //                        if( pER->southeast != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->northwest );
    //                        }
    //                    }
    //                }
    //                if( !pR->customLines.contains("E") )
    //                {
    //                    exitList.push_back( pR->east );
    //                    if( mpMap->rooms.contains(pR->east) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->east];
    //                        if( pER->west != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->east );
    //                        }
    //                    }
    //                }
    //                if( !pR->customLines.contains("SE") )
    //                {
    //                    exitList.push_back( pR->southeast );
    //                    if( mpMap->rooms.contains(pR->southeast) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->southeast];
    //                        if( pER->northwest != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->southeast );
    //                        }
    //                    }
    //                }
    //                if( !pR->customLines.contains("S") )
    //                {
    //                    exitList.push_back( pR->south );
    //                    if( mpMap->rooms.contains(pR->south) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->south];
    //                        if( pER->north != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->south );
    //                        }
    //                    }
    //                }
    //                if( !pR->customLines.contains("SW") )
    //                {
    //                    exitList.push_back( pR->southwest );
    //                    if( mpMap->rooms.contains(pR->southwest) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->southwest];
    //                        if( pER->northeast != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->southwest );
    //                        }
    //                    }
    //                }
    //                if( !pR->customLines.contains("W") )
    //                {
    //                    exitList.push_back( pR->west );
    //                    if( mpMap->rooms.contains(pR->west) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->west];
    //                        if( pER->east != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->west );
    //                        }
    //                    }
    //                }
    //                if( !pR->customLines.contains("NE") )
    //                {
    //                    exitList.push_back( pR->northeast );
    //                    if( mpMap->rooms.contains(pR->northeast) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->northeast];
    //                        if( pER->southwest != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->northeast );
    //                        }
    //                    }
    //                }
    //            }
    //            else
    //            {
    //                if( pR->north > 0 )
    //                {
    //                    exitList.push_back( pR->north );
    //                    if( mpMap->rooms.contains(pR->north) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->north];
    //                        if( pER->south != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->north );
    //                        }
    //                    }
    //                }
    //                if( pR->northwest > 0 )
    //                {
    //                    exitList.push_back( pR->northwest );
    //                    if( mpMap->rooms.contains(pR->northwest) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->northwest];
    //                        if( pER->southeast != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->northwest );
    //                        }
    //                    }
    //                }
    //                if( pR->east > 0 )
    //                {
    //                    exitList.push_back( pR->east );
    //                    if( mpMap->rooms.contains(pR->east) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->east];
    //                        if( pER->west != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->east );
    //                        }
    //                    }
    //                }
    //                if( pR->southeast > 0 )
    //                {
    //                    exitList.push_back( pR->southeast );
    //                    if( mpMap->rooms.contains(pR->southeast) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->southeast];
    //                        if( pER->northwest != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->southeast );
    //                        }
    //                    }
    //                }
    //                if( pR->south > 0 )
    //                {
    //                    exitList.push_back( pR->south );
    //                    if( mpMap->rooms.contains(pR->south) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->south];
    //                        if( pER->north != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->south );
    //                        }
    //                    }
    //                }
    //                if( pR->southwest > 0 )
    //                {
    //                    exitList.push_back( pR->southwest );
    //                    if( mpMap->rooms.contains(pR->southwest) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->southwest];
    //                        if( pER->northeast != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->southwest );
    //                        }
    //                    }
    //                }
    //                if( pR->west > 0 )
    //                {
    //                    exitList.push_back( pR->west );
    //                    if( mpMap->rooms.contains(pR->west) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->west];
    //                        if( pER->east != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->west );
    //                        }
    //                    }
    //                }
    //                if( pR->northeast > 0 )
    //                {
    //                    exitList.push_back( pR->northeast );
    //                    if( mpMap->rooms.contains(pR->northeast) )
    //                    {
    //                        TRoom * pER = mpMap->rooms[pR->northeast];
    //                        if( pER->southwest != pR->id )
    //                        {
    //                            oneWayExits.push_back( pR->northeast );
    //                        }
    //                    }
    //                }
    //            }

    //            if( pR->customLines.size() > 0 )
    //            {
    //                QPen oldPen = p.pen();
    //                QMapIterator<QString, QList<QPointF> > itk(pR->customLines);
    //                while( itk.hasNext() )
    //                {
    //                    itk.next();
    //                    QColor _color;
    //                    if( pR->id == mCustomLineSelectedRoom && itk.key()== mCustomLineSelectedExit )
    //                    {
    //                        _color.setRed( 255 );
    //                        _color.setGreen( 155 );
    //                        _color.setBlue( 55 );
    //                    }
    //                    else if( pR->customLinesColor[itk.key()].size() == 3 )
    //                    {
    //                        _color.setRed( mpMap->rooms[pArea->rooms[i]]->customLinesColor[itk.key()][0] );
    //                        _color.setGreen( mpMap->rooms[pArea->rooms[i]]->customLinesColor[itk.key()][1] );
    //                        _color.setBlue( mpMap->rooms[pArea->rooms[i]]->customLinesColor[itk.key()][2] );
    //                    }
    //                    else
    //                        _color = QColor(Qt::red);
    //                    bool _arrow = pR->customLinesArrow[itk.key()];
    //                    QString _style = pR->customLinesStyle[itk.key()];
    //                    QPointF _cstartP;
    //                    float ex = pR->x*tx+_rx;
    //                    float ey = pR->y*ty*-1+_ry;
    //                    if( itk.key() == "N" )
    //                        _cstartP = QPoint(ex,ey-ty/2);
    //                    else if( itk.key() == "NW" )
    //                        _cstartP = QPoint(ex-tx/2,ey-ty/2);
    //                    else if( itk.key() == "NE" )
    //                        _cstartP = QPoint(ex+tx/2,ey-ty/2);
    //                    else if( itk.key() == "S" )
    //                        _cstartP = QPoint(ex,ey+ty/2);
    //                    else if( itk.key() == "SW" )
    //                        _cstartP = QPoint(ex-tx/2,ey+ty/2);
    //                    else if( itk.key() == "SE" )
    //                        _cstartP = QPoint(ex+tx/2,ey+ty/2);
    //                    else if( itk.key() == "W" )
    //                        _cstartP = QPoint(ex-tx/2, ey);
    //                    else if( itk.key() == "E" )
    //                        _cstartP = QPoint(ex+tx/2, ey);
    //                    else
    //                        _cstartP = QPointF(ex, ey);
    //                    QPointF ursprung = QPointF(ex,ey);
    //                    QPen customLinePen = p.pen();
    //                    customLinePen.setCosmetic(mMapperUseAntiAlias);
    //                    customLinePen.setColor( _color );
    //                    customLinePen.setCapStyle( Qt::RoundCap );
    //                    customLinePen.setJoinStyle( Qt::RoundJoin );

    //                    if( _style == "solid line" )
    //                        customLinePen.setStyle( Qt::SolidLine );
    //                    else if( _style == "dot line" )
    //                        customLinePen.setStyle( Qt::DotLine );
    //                    else if( _style == "dash line" )
    //                        customLinePen.setStyle( Qt::DashLine );
    //                    else
    //                        customLinePen.setStyle( Qt::DashDotDotLine );

    //                    QList<QPointF> _pL = itk.value();
    //                    if( _pL.size() > 0 )
    //                    {
    //                        p.setPen(customLinePen);
    //                        p.drawLine( ursprung, _cstartP );
    //                    }
    //                    for( int pk=0; pk<_pL.size(); pk++ )
    //                    {
    //                        QPointF _cendP;
    //                        _cendP.setX( _pL[pk].x()*tx+_rx );
    //                        _cendP.setY( _pL[pk].y()*ty*-1+_ry );
    //                        p.drawLine( _cstartP, _cendP );

    //                        if( pR->id == mCustomLineSelectedRoom && itk.key()== mCustomLineSelectedExit )
    //                        {
    //                            QBrush _brush = p.brush();
    //                            p.setBrush(QColor(255,155,55));
    //                            p.drawEllipse( _cendP, mTX/4, mTX/4 );
    //                            p.setBrush(_brush);
    //                        }

    //                        if( pk == _pL.size()-1 && _arrow )
    //                        {
    //                            QLineF l0 = QLineF( _cendP, _cstartP );
    //                            l0.setLength(wegBreite*5);
    //                            QPointF _p1 = l0.p1();
    //                            QPointF _p2 = l0.p2();
    //                            QLineF l1 = QLineF( l0 );
    //                            qreal w1 = l1.angle()-90.0;
    //                            QLineF l2;
    //                            l2.setP1(_p2);
    //                            l2.setAngle(w1);
    //                            l2.setLength(wegBreite*2);
    //                            QPointF _p3 = l2.p2();
    //                            l2.setAngle( l2.angle()+180.0 );
    //                            QPointF _p4 = l2.p2();
    //                            QPolygonF _poly;
    //                            _poly.append( _p1 );
    //                            _poly.append( _p3 );
    //                            _poly.append( _p4 );
    //                            QBrush brush = p.brush();
    //                            brush.setColor( _color );
    //                            brush.setStyle( Qt::SolidPattern );
    //                            QPen arrowPen = p.pen();
    //                            arrowPen.setCosmetic( mMapperUseAntiAlias );
    //                            arrowPen.setStyle(Qt::SolidLine);
    //                            p.setPen( arrowPen );
    //                            p.setBrush( brush );
    //                            p.drawPolygon(_poly);
    //                        }
    //                        _cstartP = _cendP;
    //                    }
    //                }
    //                p.setPen(oldPen);
    //            }

    //            int e = pR->z;

    //            // draw exit stubs
    //            QMap<int, QVector3D> unitVectors = mpMap->unitVectors;
    //            for( int k=0; k<pR->exitStubs.size(); k++ )
    //            {
    //                int direction = pR->exitStubs[k];
    //                QVector3D uDirection = unitVectors[direction];
    //                p.drawLine(rx+rSize*(int)uDirection.x()/2, ry+rSize*(int)uDirection.y(),rx+(int)uDirection.x()*(rSize*3/4*tx), ry+uDirection.y()*(rSize*3/4*ty));
    //            }

    //            QPen __pen;
    //            for( int k=0; k<exitList.size(); k++ )
    //            {
    //                int rID = exitList[k];
    //                if( rID <= 0 ) continue;

    //                bool areaExit;

    //                TRoom * pE = mpMap->rooms[rID];

    //                if( pE->area != mAID )
    //                {
    //                    areaExit = true;
    //                }
    //                else
    //                    areaExit = false;
    //                float ex = pE->x*tx+_rx;
    //                float ey = pE->y*ty*-1+_ry;
    //                int ez = pE->z;

    //                QVector3D p1( ex, ey, ez );
    //                QVector3D p2( rx, ry, rz );
    //                QLine _line;
    //                if( ! areaExit )
    //                {
    //                    // one way exit or 2 way exit?
    //                    if( ! oneWayExits.contains( rID ) )
    //                    {
    //                        p.drawLine( (int)p1.x(), (int)p1.y(), (int)p2.x(), (int)p2.y() );
    //                    }
    //                    else
    //                    {
    //                        // one way exit draw arrow

    //                        QLineF l0 = QLineF( p2.x(), p2.y(), p1.x(), p1.y() );
    //                        QLineF k0 = l0;
    //                        k0.setLength( (l0.length()-wegBreite*5)*0.5 );
    //                        qreal dx = k0.dx(); qreal dy = k0.dy();
    //                        QPen _tp = p.pen();
    //                        QPen _tp2 = _tp;
    //                        _tp2.setStyle(Qt::DotLine);
    //                        p.setPen(_tp2);
    //                        p.drawLine( l0 );
    //                        p.setPen(_tp);
    //                        l0.setLength(wegBreite*5);
    //                        QPointF _p1 = l0.p2();
    //                        QPointF _p2 = l0.p1();
    //                        QLineF l1 = QLineF( l0 );
    //                        qreal w1 = l1.angle()-90.0;
    //                        QLineF l2;
    //                        l2.setP1(_p2);
    //                        l2.setAngle(w1);
    //                        l2.setLength(wegBreite*2);
    //                        QPointF _p3 = l2.p2();
    //                        l2.setAngle( l2.angle()+180.0 );
    //                        QPointF _p4 = l2.p2();
    //                        QPolygonF _poly;
    //                        _poly.append( _p1 );
    //                        _poly.append( _p3 );
    //                        _poly.append( _p4 );

    //                        QBrush brush = p.brush();
    //                        brush.setColor( QColor(255,100,100) );
    //                        brush.setStyle( Qt::SolidPattern );
    //                        QPen arrowPen = p.pen();
    //                        arrowPen.setCosmetic( mMapperUseAntiAlias );
    //                        arrowPen.setStyle(Qt::SolidLine);
    //                        p.setPen( arrowPen );
    //                        p.setBrush( brush );
    //                        p.drawPolygon(_poly.translated(dx,dy));
    //                    }
    //                }
    //                else
    //                {
    //                    __pen = p.pen();
    //                    QLine _line;
    //                    if( pR->south == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine( p2.x(), p2.y()+ty,p2.x(), p2.y() );
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x(), p2.y()+ty/2);
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    else if( pR->north == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine( p2.x(), p2.y()-ty, p2.x(), p2.y() );
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x(), p2.y()-ty/2);
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    else if( pR->west == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine(p2.x()-tx, p2.y(),p2.x(), p2.y() );
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x()-tx/2, p2.y());
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    else if( pR->east == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine(p2.x()+tx, p2.y(),p2.x(), p2.y() );
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x()+tx/2, p2.y());
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    else if( pR->northwest == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine(p2.x()-tx, p2.y()-ty,p2.x(), p2.y() );
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x()-tx/2, p2.y()-ty/2);
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    else if( pR->northeast == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine(p2.x()+tx, p2.y()-ty,p2.x(), p2.y());
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x()+tx/2, p2.y()-ty/2);
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    else if( pR->southeast == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine(p2.x()+tx, p2.y()+ty, p2.x(), p2.y());
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x()+tx/2, p2.y()+ty/2);
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    else if( pR->southwest == rID )
    //                    {
    //                        pen = p.pen();
    //                        pen.setWidthF(wegBreite);
    //                        pen.setCosmetic( mMapperUseAntiAlias );
    //                        pen.setColor(getColor(exitList[k]));
    //                        p.setPen( pen );
    //                        _line = QLine(p2.x()-tx, p2.y()+ty, p2.x(), p2.y());
    //                        p.drawLine( _line );
    //                        QPoint _p = QPoint(p2.x()-tx/2, p2.y()+ty/2);
    //                        mAreaExitList[exitList[k]] = _p;
    //                    }
    //                    QLineF l0 = QLineF( _line );
    //                    l0.setLength(wegBreite*5);
    //                    QPointF _p1 = l0.p1();
    //                    QPointF _p2 = l0.p2();
    //                    QLineF l1 = QLineF( l0 );
    //                    qreal w1 = l1.angle()-90.0;
    //                    QLineF l2;
    //                    l2.setP1(_p2);
    //                    l2.setAngle(w1);
    //                    l2.setLength(wegBreite*2);
    //                    QPointF _p3 = l2.p2();
    //                    l2.setAngle( l2.angle()+180.0 );
    //                    QPointF _p4 = l2.p2();
    //                    QPolygonF _poly;
    //                    _poly.append( _p1 );
    //                    _poly.append( _p3 );
    //                    _poly.append( _p4 );
    //                    QBrush brush = p.brush();
    //                    brush.setColor( getColor(exitList[k]) );
    //                    brush.setStyle( Qt::SolidPattern );
    //                    QPen arrowPen = p.pen();
    //                    arrowPen.setCosmetic( mMapperUseAntiAlias );
    //                    p.setPen( arrowPen );
    //                    p.setBrush( brush );
    //                    p.drawPolygon(_poly);
    //                    p.setPen( __pen );
    //                }
    //                if( pR->doors.size() > 0 )
    //                {
    //                    int doorStatus = 0;
    //                    if( pR->south == rID && pR->doors.contains("s") )
    //                    {
    //                        doorStatus = pR->doors["s"];
    //                    }
    //                    else if( pR->north == rID && pR->doors.contains("n") )
    //                    {
    //                        doorStatus = pR->doors["n"];
    //                    }
    //                    else if( pR->southwest == rID && pR->doors.contains("sw") )
    //                    {
    //                        doorStatus = pR->doors["sw"];
    //                    }
    //                    else if( pR->southeast == rID && pR->doors.contains("se") )
    //                    {
    //                        doorStatus = pR->doors["se"];
    //                    }
    //                    else if( pR->northeast == rID && pR->doors.contains("ne") )
    //                    {
    //                        doorStatus = pR->doors["ne"];
    //                    }
    //                    else if( pR->northwest == rID && pR->doors.contains("nw") )
    //                    {
    //                        doorStatus = pR->doors["nw"];
    //                    }
    //                    else if( pR->west == rID && pR->doors.contains("w") )
    //                    {
    //                        doorStatus = pR->doors["w"];
    //                    }
    //                    else if( pR->east == rID && pR->doors.contains("e") )
    //                    {
    //                        doorStatus = pR->doors["e"];
    //                    }
    //                    if( doorStatus > 0 )
    //                    {
    //                        QLineF k0;
    //                        QRectF rect;
    //                        rect.setWidth(0.25*mTX);
    //                        rect.setHeight(0.25*mTY);
    //                        if ( areaExit )
    //                            k0 = QLineF(_line);
    //                        else
    //                            k0 = QLineF( p2.x(), p2.y(), p1.x(), p1.y() );
    //                        k0.setLength( (k0.length())*0.5 );
    //                        rect.moveCenter(k0.p2());
    //                        QPen arrowPen = p.pen();
    //                        QPen _tp = p.pen();
    //                        arrowPen.setCosmetic( mMapperUseAntiAlias );
    //                        arrowPen.setStyle(Qt::SolidLine);
    //                        if( doorStatus == 1 ) //open door
    //                            arrowPen.setColor(QColor(10,155,10));
    //                        else if( doorStatus == 2 ) //closed door
    //                            arrowPen.setColor(QColor(155,155,10));
    //                        else //locked door
    //                            arrowPen.setColor(QColor(155,10,10));
    //                        QBrush brush;
    //                        QBrush oldBrush;
    //                        p.setPen( arrowPen );
    //                        p.setBrush(brush);
    //                        p.drawRect(rect);
    //                        p.setBrush(oldBrush);
    //                        p.setPen(_tp);
    //                    }
    //                }
    //            }
    //        }
    //    }
    //    // draw group selection box
    //    if( mSizeLabel )
    //        p.fillRect(mMultiRect,QColor(250,190,0,190));
    //    else
    //        p.fillRect(mMultiRect,QColor(190,190,190,60));
    //    for( int i=0; i<pArea->rooms.size(); i++ )
    //    {
    //        TRoom * pR = mpMap->rooms[pArea->rooms[i]];
    //        float rx = pR->x*tx+_rx;
    //        float ry = pR->y*-1*ty+_ry;
    //        int rz = pR->z;

    //        if( rz != zEbene ) continue;
    //        //if( rx < 0 || ry < 0 || rx > _w || ry > _h ) continue;

    //        pR->rendered = false;
    //        QRectF dr;
    //        if( pArea->gridMode )
    //        {
    //            dr = QRectF(rx-tx/2, ry-ty/2,tx,ty);
    //        }
    //        else
    //        {
    //            dr = QRectF(rx-(tx*rSize)/2,ry-(ty*rSize)/2,tx*rSize,ty*rSize);
    //        }

    //        QColor c;
    //        int env = pR->environment;
    //        if( mpMap->envColors.contains(env) )
    //            env = mpMap->envColors[env];
    //        else
    //        {
    //            if( ! mpMap->customEnvColors.contains(env))
    //            {
    //                env = 1;
    //            }
    //        }
    //        switch( env )
    //        {
    //        case 1:
    //            c = mpHost->mRed_2;
    //            break;

    //        case 2:
    //            c = mpHost->mGreen_2;
    //            break;
    //        case 3:
    //            c = mpHost->mYellow_2;
    //            break;

    //        case 4:
    //            c = mpHost->mBlue_2;
    //            break;

    //        case 5:
    //            c = mpHost->mMagenta_2;
    //            break;
    //        case 6:
    //            c = mpHost->mCyan_2;
    //            break;
    //        case 7:
    //            c = mpHost->mWhite_2;
    //            break;
    //        case 8:
    //            c = mpHost->mBlack_2;
    //            break;

    //        case 9:
    //            c = mpHost->mLightRed_2;
    //            break;

    //        case 10:
    //            c = mpHost->mLightGreen_2;
    //            break;
    //        case 11:
    //            c = mpHost->mLightYellow_2;
    //            break;

    //        case 12:
    //            c = mpHost->mLightBlue_2;
    //            break;

    //        case 13:
    //            c = mpHost->mLightMagenta_2;
    //            break;
    //        case 14:
    //            c = mpHost->mLightCyan_2;
    //            break;
    //        case 15:
    //            c = mpHost->mLightWhite_2;
    //            break;
    //        case 16:
    //            c = mpHost->mLightBlack_2;
    //        default: //user defined room color
    //            if( ! mpMap->customEnvColors.contains(env) ) break;
    //            c = mpMap->customEnvColors[env];
    //        }
    //        if( ( mPick || __Pick ) && mPHighlight.x() >= dr.x()-(tx*rSize) && mPHighlight.x() <= dr.x()+(tx*rSize) && mPHighlight.y() >= dr.y()-(ty*rSize) && mPHighlight.y() <= dr.y()+(ty*rSize)
    //            || mMultiSelectionList.contains(pArea->rooms[i]) )
    //        {
    //            p.fillRect(dr,QColor(255,155,55));
    //            mPick = false;
    //            if( mStartSpeedWalk )
    //            {
    //                mStartSpeedWalk = false;
    //                float _radius = (0.8*tx)/2;
    //                QPointF _center = QPointF(rx,ry);
    //                QRadialGradient _gradient(_center,_radius);
    //                _gradient.setColorAt(0.95, QColor(255,0,0,150));
    //                _gradient.setColorAt(0.80, QColor(150,100,100,150));
    //                _gradient.setColorAt(0.799,QColor(150,100,100,100));
    //                _gradient.setColorAt(0.7, QColor(255,0,0,200));
    //                _gradient.setColorAt(0, QColor(255,255,255,255));
    //                QPen myPen(Qt::transparent);
    //                QPainterPath myPath;
    //                p.setBrush(_gradient);
    //                p.setPen(myPen);
    //                myPath.addEllipse(_center,_radius,_radius);
    //                p.drawPath(myPath);


    //                mTarget = pArea->rooms[i];
    //                if( mpMap->rooms.contains(mTarget) )
    //                {
    //                    mpMap->mTargetID = mTarget;
    //                    if( mpMap->findPath( mpMap->mRoomId, mpMap->mTargetID) )
    //                    {
    //                       mpMap->mpHost->startSpeedWalk();
    //                    }
    //                    else
    //                    {
    //                        QString msg = "Mapper: Cannot find a path to this room using known exits.\n";
    //                        mpHost->mpConsole->printSystemMessage(msg);
    //                    }
    //                }
    //            }
    //        }
    //        else
    //        {
    //            char _ch = pR->c;
    //            if( _ch >= 33 && _ch < 255 )
    //            {
    //                int _color = ( 265 - 257 ) * 254 + _ch;//(mpMap->rooms[pArea->rooms[i]]->environment - 257 ) * 254 + _ch;

    //                if( c.red()+c.green()+c.blue() > 260 )
    //                    _color = ( 7 ) * 254 + _ch;
    //                else
    //                    _color = ( 6 ) * 254 + _ch;

    //                p.fillRect( dr, c );
    //                if( mPixMap.contains( _color ) )
    //                {
    //                    QPixmap pix = mPixMap[_color].scaled(dr.width(), dr.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    //                    p.drawPixmap(dr.topLeft(), pix);
    //                }
    //            }
    //            else
    //            {
    //                if( mBubbleMode )
    //                {
    //                    float _radius = (rSize*tx)/2;
    //                    QPointF _center = QPointF(rx,ry);
    //                    QRadialGradient _gradient(_center,_radius);
    //                    _gradient.setColorAt(0.85, c);
    //                    _gradient.setColorAt(0, QColor(255,255,255,255));
    //                    QPen myPen(Qt::transparent);
    //                    QPainterPath myPath;
    //                    p.setBrush(_gradient);
    //                    p.setPen(myPen);
    //                    myPath.addEllipse(_center,_radius,_radius);
    //                    p.drawPath(myPath);
    //                }
    //                else
    //                    p.fillRect(dr,c);
    //            }
    //            if( pR->highlight )
    //            {
    //                float _radius = (mpMap->rooms[pArea->rooms[i]]->highlightRadius*tx)/2;
    //                QPointF _center = QPointF(rx,ry);
    //                QRadialGradient _gradient(_center,_radius);
    //                _gradient.setColorAt(0.85, mpMap->rooms[pArea->rooms[i]]->highlightColor);
    //                _gradient.setColorAt(0, mpMap->rooms[pArea->rooms[i]]->highlightColor2 );
    //                QPen myPen(Qt::transparent);
    //                QPainterPath myPath;
    //                p.setBrush(_gradient);
    //                p.setPen(myPen);
    //                myPath.addEllipse(_center,_radius,_radius);
    //                p.drawPath(myPath);
    //            }
    //            if( mShowRoomID )
    //            {
    //                QPen __pen = p.pen();
    //                QColor lc;
    //                if( c.red()+c.green()+c.blue() > 200 )
    //                    lc=QColor(Qt::black);
    //                else
    //                    lc=QColor(Qt::white);
    //                p.setPen(QPen(lc));
    //                p.drawText(dr, Qt::AlignHCenter|Qt::AlignVCenter,QString::number(pArea->rooms[i]));
    //                p.setPen(__pen);
    //            }
    //            if( mShiftMode && pArea->rooms[i] == mpMap->mRoomId )
    //            {
    //                float _radius = (1.2*tx)/2;
    //                QPointF _center = QPointF(rx,ry);
    //                QRadialGradient _gradient(_center,_radius);
    //                _gradient.setColorAt(0.95, QColor(255,0,0,150));
    //                _gradient.setColorAt(0.80, QColor(150,100,100,150));
    //                _gradient.setColorAt(0.799,QColor(150,100,100,100));
    //                _gradient.setColorAt(0.7, QColor(255,0,0,200));
    //                _gradient.setColorAt(0, QColor(255,255,255,255));
    //                QPen myPen(Qt::transparent);
    //                QPainterPath myPath;
    //                p.setBrush(_gradient);
    //                p.setPen(myPen);
    //                myPath.addEllipse(_center,_radius,_radius);
    //                p.drawPath(myPath);

    //            }
    //        }

    //        QColor lc;
    //        if( c.red()+c.green()+c.blue() > 200 )
    //            lc=QColor(Qt::black);
    //        else
    //            lc=QColor(Qt::white);
    //        pen = p.pen();
    //        pen.setColor( lc );
    //        pen.setWidthF(0);//wegBreite?);
    //        pen.setCosmetic( mMapperUseAntiAlias );
    //        pen.setCapStyle( Qt::RoundCap );
    //        pen.setJoinStyle( Qt::RoundJoin );
    //        p.setPen( pen );

    //        //FIXME: redo exit stubs here since the room will draw over up/down stubs -- its repetitive though
    //        QMap<int, QVector3D> unitVectors = mpMap->unitVectors;
    //        for( int k=0; k<pR->exitStubs.size(); k++ )
    //        {
    //            int direction = pR->exitStubs[k];
    //            QVector3D uDirection = unitVectors[direction];
    //            if (direction > 8)
    //            {
    //                float rx = pR->x*tx+_rx;
    //                float ry = pR->y*-1*ty+_ry;
    //                QPolygonF _poly;
    //                QPointF _pt;
    //                _pt = QPointF( rx, ry+(ty*rSize)*uDirection.z()/20 );
    //                _poly.append( _pt );
    //                _pt = QPointF( rx+(tx*rSize)/3.1, ry+(ty*rSize)*uDirection.z()/3.1);
    //                _poly.append(_pt);
    //                _pt = QPointF( rx-(tx*rSize)/3.1, ry+(ty*rSize)*uDirection.z()/3.1 );
    //                _poly.append( _pt );
    //                QBrush brush = p.brush();
    //                brush.setColor( QColor(Qt::black) );
    //                brush.setStyle( Qt::NoBrush );
    //                p.setBrush( brush );
    //                p.drawPolygon(_poly);
    //           }
    //        }

    //        if( pR->up > 0 )
    //        {
    //            QPolygonF _poly;
    //            QPointF _pt;
    //            _pt = QPointF( rx, ry+(ty*rSize)/20 );
    //            _poly.append( _pt );
    //            _pt = QPointF( rx-(tx*rSize)/3.1, ry+(ty*rSize)/3.1 );
    //            _poly.append( _pt );
    //            _pt = QPointF( rx+(tx*rSize)/3.1, ry+(ty*rSize)/3.1);
    //            _poly.append(_pt);
    //            QBrush brush = p.brush();
    //            brush.setColor( QColor(Qt::black) );
    //            brush.setStyle( Qt::SolidPattern );
    //            p.setBrush( brush );
    //            p.drawPolygon(_poly);
    //        }
    //        if( pR->down > 0 )
    //        {
    //            QPolygonF _poly;
    //            QPointF _pt;
    //            _pt = QPointF( rx, ry-(ty*rSize)/20 );
    //            _poly.append( _pt );
    //            _pt = QPointF( rx-(tx*rSize)/3.1, ry-(ty*rSize)/3.1 );
    //            _poly.append( _pt );
    //            _pt = QPointF( rx+(tx*rSize)/3.1, ry-(ty*rSize)/3.1);
    //            _poly.append(_pt);
    //            QBrush brush = p.brush();
    //            brush.setColor( QColor(Qt::black) );
    //            brush.setStyle( Qt::SolidPattern );
    //            p.setBrush( brush );
    //            p.drawPolygon(_poly);
    //        }
    //        if( pR->in > 0 )
    //        {
    //            QPolygonF _poly;
    //            QPointF _pt;
    //            _pt = QPointF( rx+(tx*rSize)/20, ry );
    //            _poly.append( _pt );
    //            _pt = QPointF( rx-(tx*rSize)/3.1, ry-(ty*rSize)/3.1 );
    //            _poly.append( _pt );
    //            _pt = QPointF( rx-(tx*rSize)/3.1, ry+(ty*rSize)/3.1);
    //            _poly.append(_pt);
    //            QBrush brush = p.brush();
    //            brush.setColor( QColor(Qt::black) );
    //            brush.setStyle( Qt::SolidPattern );
    //            p.setBrush( brush );
    //            p.drawPolygon(_poly);
    //        }
    //        if( pR->out > 0 )
    //        {
    //            QPolygonF _poly;
    //            QPointF _pt;
    //            _pt = QPointF( rx-(tx*rSize)/20, ry);
    //            _poly.append( _pt );
    //            _pt = QPointF( rx+(tx*rSize)/3.1, ry-(ty*rSize)/3.1 );
    //            _poly.append( _pt );
    //            _pt = QPointF( rx+(tx*rSize)/3.1, ry+(ty*rSize)/3.1);
    //            _poly.append(_pt);
    //            QBrush brush = p.brush();
    //            brush.setColor( QColor(Qt::black) );
    //            brush.setStyle( Qt::SolidPattern );
    //            p.setBrush( brush );
    //            p.drawPolygon(_poly);
    //        }
    //    }
    //    QString _zeit = QTime().currentTime().toString("hh-mm-ss");
    //    QString home = QDir::homePath();
    //    home.append( "/.config/mudlet/profiles/" );
    //    QString name = mpHost->getName();
    //    home.append( name );

    //    QString name2 = "/mapOfAreaID_"+QString::number(mAID)+"_"+_zeit+".PNG";
    //    home.append( name2 );
    //    QString erg = QDir::toNativeSeparators( home );
    //    pix.save(erg);
}

void T2DMap::resizeMultiSelectionWidget()
{
    int _newWidth;
    if (mIsSelectionUsingNames) {
        if (width() <= 300) { // 0 - 300 => 0 - 200
            _newWidth = 2 * width() / 3;
        } else if (width() <= 600) { // 300 - 600 => 200 - 300
            _newWidth = 100 + width() / 3;
        } else { // 600+ => 300
            _newWidth = 300;
        }
    } else {
        if (width() <= 300) { // 0 - 300 => 0 - 120
            _newWidth = 2 * width() / 3;
        } else { // 300+ => 120
            _newWidth = 120;
        }
    }
    int _newHeight = 300;
    if (mMultiSelectionListWidget.topLevelItemCount() > 0) {
        QTreeWidgetItem* rowItem = mMultiSelectionListWidget.topLevelItem(1);
        // The following factors are tweaks to ensure that the widget shows all
        // the rows, as the header seems bigger than the value returned, statics
        // used to enable values to be change by debugger at runtime!
        static float headerFactor = 1.2;
        static float rowFactor = 1.0;
        _newHeight = headerFactor * mMultiSelectionListWidget.header()->height();
        if (rowItem) { // Have some data rows - and we have forced them to be the same height:
            _newHeight += rowFactor * mMultiSelectionListWidget.topLevelItemCount() * mMultiSelectionListWidget.visualItemRect(rowItem).height();
        }
    }
    if (_newHeight < height()) {
        mMultiSelectionListWidget.resize(_newWidth, _newHeight);
    } else {
        mMultiSelectionListWidget.resize(_newWidth, height());
    }
}
