File indexing completed on 2024-12-08 06:46:39

0001 /********************************************f*******************************
0002                           mapmanager.cpp  -  description
0003                              -------------------
0004     begin                : Wed Mar 7 2001
0005     copyright            : (C) 2001 by KMud Development Team
0006                            (C) 2007 Tomas Mecir <kmuddy@kmuddy.com>
0007     email                : kmud-devel@kmud.de
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *   This program is free software; you can redistribute it and/or modify  *
0013  *   it under the terms of the GNU General Public License as published by  *
0014  *   the Free Software Foundation; either version 2 of the License, or     *
0015  *   (at your option) any later version.                                   *
0016  *                                                                         *
0017  ***************************************************************************/
0018 
0019 #include "cmapmanager.h"
0020 
0021 #include <KLocalizedString>
0022 #include <kservicetypetrader.h>
0023 #include <kmessagebox.h>
0024 #include <kstandardaction.h>
0025 #include <kpagedialog.h>
0026 
0027 #include <QDebug>
0028 #include <QIcon>
0029 #include <QQueue>
0030 #include <QStandardPaths>
0031 #include <QStatusBar>
0032 #include <QTimer>
0033 #include <QUndoStack>
0034 
0035 #include "cmapzone.h"
0036 #include "cmappath.h"
0037 #include "cmaptext.h"
0038 #include "cmaproom.h"
0039 #include "cmapview.h"
0040 #include "cmaplevel.h"
0041 #include "cmapview.h"
0042 #include "cmaptoolbase.h"
0043 #include "cmappluginbase.h"
0044 #include "cmapzonemanager.h"
0045 
0046 #include "cmapcmdelementcreate.h"
0047 #include "cmapcmdelementdelete.h"
0048 #include "cmapcmdelementproperties.h"
0049 #include "cmapcmdgroup.h"
0050 #include "cmapcmdmovemap.h"
0051 #include "cmapcmdmoveplayer.h"
0052 #include "cmapcmdleveldelete.h"
0053 #include "cmapcmdlevelcreate.h"
0054 #include "cmapcmdtogglepathtwoway.h"
0055 #include "cmapfilefilterbase.h"
0056 
0057 #include "kmuddy_mapper.h"
0058 
0059 #include "filefilters/cmapfilefilterxml.h"
0060 
0061 #include "dialogs/dlgmaproomproperties.h"
0062 #include "dialogs/dlgmappathproperties.h"
0063 #include "dialogs/dlgmaptextproperties.h"
0064 #include "dialogs/dlgspeedwalkprogress.h"
0065 #include "dialogs/dlgmapspeedwalk.h"
0066 #include "dialogs/dlgmapcolor.h"
0067 #include "dialogs/dlgmapdirections.h"
0068 #include "dialogs/dlgmapmovement.h"
0069 
0070 #include "cactionmanager.h"
0071 #include "cdialoglist.h"
0072 #include "cglobalsettings.h"
0073 #include "cstatus.h"
0074 
0075 CMapManager::CMapManager (QWidget *parent, KMuddyMapper *mapper, int sessId) :
0076   cActionBase ("map-manager", 0),
0077   m_sessId (sessId),
0078   mapperPlugin (mapper)
0079 {
0080   qDebug() << "constructor begins";
0081 
0082   // register action handlers
0083   addEventHandler ("dialog-create", 50, PT_STRING);
0084   addEventHandler ("dialog-save", 50, PT_STRING);
0085 
0086   mapData = new CMapData();
0087 
0088   // Setup vars
0089   loginRoom = nullptr;
0090   currentRoom = nullptr;
0091   elementEdit = nullptr;
0092 
0093   /** Create undo/redo history */
0094   commandHistory = new QUndoStack();
0095   //FIXME_jp: Needs to be configurable
0096   commandHistory->setUndoLimit(30);
0097   commandHistory->clear();
0098   historyGroup = nullptr;
0099   m_commandsActive = true;
0100 
0101   initFileFilters();
0102 
0103   activeView = nullptr;
0104 
0105   setDefaultOptions();
0106 
0107   speedwalkActive = false;
0108 
0109   speedwalkProgressDlg = new DlgSpeedwalkProgress();
0110   speedwalkProgressDlg->hide();
0111   connect(speedwalkProgressDlg,SIGNAL(abortSpeedwalk()),this,SLOT(slotAbortSpeedwalk()));
0112 
0113   m_zoneCount = 0;
0114   m_levelCount = 0;
0115   m_zoneManager = nullptr;
0116 
0117   setUndoActive (false);
0118   createNewMap();  // because some things break if a map doesn't exist
0119   activeView = new CMapView(this, parent);
0120 
0121   m_zoneManager = new CMapZoneManager(sessId, this);
0122   if (!m_zoneManager->zonesModel()->rowCount())
0123     m_zoneManager->createZone (i18n ("Map #1"));
0124   m_zoneManager->loadZone(0);
0125 
0126   initPlugins();
0127   activeView->initGUI();
0128 
0129   readOptions();
0130 
0131   openMapView ();
0132   setUndoActive (true);
0133 
0134   qDebug() << "constructor ends";
0135 }
0136 
0137 CMapManager::~CMapManager()
0138 {
0139   qDebug() << "CMapManager::~CMapManager() start";
0140   removeEventHandler ("dialog-create");
0141   removeEventHandler ("dialog-save");
0142 
0143   if (mapData)
0144     delete mapData;
0145   mapData = nullptr;
0146   delete m_zoneManager;
0147   delete activeView;
0148 
0149   if (commandHistory)
0150     delete commandHistory;
0151 
0152   for (CMapFileFilterBase *filter : m_fileFilter)
0153     delete filter;
0154   m_fileFilter.clear();
0155 
0156   qDebug() << "CMapManager::~CMapManager() end";
0157 }
0158 
0159 void CMapManager::eventStringHandler (QString event, int, QString &par1, const QString &)
0160 {
0161   if (event == "dialog-create") {
0162     if (par1 == "profile-prefs")
0163       createProfileConfigPanes ();
0164     else if (par1 == "app-prefs")
0165       createGlobalConfigPanes ();
0166   }
0167   if (event == "dialog-save") {
0168     if (par1 == "profile-prefs") {
0169     } else if (par1 == "app-prefs") {
0170       // TODO: the first two should be profile prefs
0171       mapDirection->slotOkPressed();
0172       mapMovement->slotOkPressed();
0173 
0174       mapColor->slotOkPressed();
0175       mapSpeedwalk->slotOkPressed();
0176       saveGlobalConfig ();
0177     }
0178   }
0179 }
0180 
0181 void CMapManager::createProfileConfigPanes ()
0182 {
0183   /*KPageDialog *dlg = */(KPageDialog *) cDialogList::self()->getDialog ("profile-prefs");
0184 
0185   for (CMapPluginBase *plugin : pluginList)
0186     plugin->createProfileConfigPanes();
0187 }
0188 
0189 void CMapManager::createGlobalConfigPanes ()
0190 {
0191   KPageDialog *dlg = (KPageDialog *) cDialogList::self()->getDialog ("app-prefs");
0192 
0193   KPageWidgetItem *item;
0194   QFrame *frmdir = new QFrame (dlg);
0195   item = dlg->addPage (frmdir, i18n ("Mapper: Directions"));
0196   item->setIcon (QIcon::fromTheme ("gear"));
0197   QFrame *frmmovement = new QFrame (dlg);
0198   item = dlg->addPage (frmmovement, i18n ("Mapper: Movement"));
0199   item->setIcon (QIcon::fromTheme ("run"));
0200   QFrame *frmcolor = new QFrame (dlg);
0201   item = dlg->addPage (frmcolor, i18n ("Mapper: Colors"));
0202   item->setIcon (QIcon::fromTheme ("colorize"));
0203   QFrame *frmspeedwalk = new QFrame (dlg);
0204   item = dlg->addPage (frmspeedwalk, i18n ("Mapper: Speedwalk"));
0205   item->setIcon (QIcon::fromTheme ("launch"));
0206 
0207   QVBoxLayout *dirlayout = new QVBoxLayout (frmdir);
0208   QVBoxLayout *movementlayout = new QVBoxLayout (frmmovement);
0209   QVBoxLayout *colorlayout = new QVBoxLayout (frmcolor);
0210   QVBoxLayout *speedwalklayout = new QVBoxLayout (frmspeedwalk);
0211   mapDirection = new DlgMapDirections (this, frmdir);
0212   mapMovement = new DlgMapMovement (this, frmmovement);
0213   mapColor = new DlgMapColor(this, frmcolor);
0214   mapSpeedwalk = new DlgMapSpeedwalk(this, frmspeedwalk);
0215   dirlayout->addWidget (mapDirection);
0216   movementlayout->addWidget (mapMovement);
0217   colorlayout->addWidget (mapColor);
0218   speedwalklayout->addWidget (mapSpeedwalk);
0219 
0220   for (CMapPluginBase *plugin : pluginList)
0221     plugin->createGlobalConfigPanes();
0222 }
0223 
0224 QList<CMapPropertiesPaneBase *> CMapManager::createPropertyPanes(elementTyp type,CMapElement *element,QWidget *parent)
0225 {
0226   QList<CMapPropertiesPaneBase *> res;
0227   for (CMapPluginBase *plugin : pluginList)
0228     res.append(plugin->createPropertyPanes(type, element, parent));
0229   return res;
0230 }
0231 
0232 
0233 /** This will setup the import/export file filters */
0234 void CMapManager::initFileFilters()
0235 {
0236   m_fileFilter.push_back(new CMapFileFilterXML(this));
0237 }
0238 
0239 #include "plugins/standard/cmappluginstandard.h"
0240 
0241 /** Used to create the plugins */
0242 void CMapManager::initPlugins()
0243 {
0244   int pluginCount = 0;
0245   qDebug() << "Loading Static Plugins...\n";
0246   // These used to be plug-ins, but now I'm linking them in statically, and just pretend that they are plug-ins.
0247   // This is because linking to the mapper part is causing issues.
0248   CMapPluginBase *plugin;
0249   plugin = new CMapPluginStandard (activeView);
0250   pluginList.push_back (plugin);
0251 
0252   for (CMapPluginBase *plugin : pluginList)
0253   {
0254     qDebug() << "Tools in plugin : " << plugin->getToolList()->count();
0255     foreach (CMapToolBase *tool, *plugin->getToolList())
0256     {
0257       toolList.push_back(tool);
0258     }
0259 
0260 //      getActiveView()->insertChildClient(plugin);
0261     
0262     pluginCount++;
0263   }
0264 
0265   qDebug() << "Finished loading " << pluginCount << " plugins\n";
0266   qDebug() << "Finished loading " << toolList.size() << " tools\n";
0267   
0268   if (!toolList.empty())
0269   {
0270     currentTool = toolList.front();
0271     currentTool->setChecked(true);
0272   }
0273   else
0274   {
0275     qWarning() << "No tools loaded!\n";
0276     currentTool = nullptr;
0277   }
0278 
0279     if (pluginCount==0)
0280     {
0281     qWarning() << "No plugins loaded!\n";
0282     }
0283 
0284      qDebug() << "XML File : " << activeView->xmlFile(); 
0285 }
0286 
0287 /** Used to get a list of the plugins */
0288 std::vector<CMapPluginBase *> CMapManager::getPluginList()
0289 {
0290   return pluginList;
0291 }
0292 
0293 /** Used to get a pointer to the map data */
0294 CMapData *CMapManager::getMapData() const
0295 {
0296   return mapData;
0297 }
0298 
0299 CMapZone *CMapManager::getZone(bool noCreate)
0300 {
0301   CMapZone *zone = mapData->rootZone;
0302   if ((!zone) && (!noCreate))
0303     zone = new CMapZone(this);
0304 
0305   return zone;
0306 }
0307 
0308 /** Used to create a new view of the map */
0309 void CMapManager::openMapView()
0310 {
0311   if (loginRoom)
0312     activeView->showPosition(QPoint(loginRoom->getX(),loginRoom->getY()),loginRoom->getLevel());
0313   else
0314   {
0315     CMapRoom *firstRoom = findFirstRoom(nullptr);
0316     if (firstRoom)
0317       displayLevel(firstRoom->getLevel(), true);
0318   }
0319 }
0320 
0321 void CMapManager::displayLevel(CMapLevel *level, bool centerView)
0322 {
0323   CMapView *mapView = getActiveView();
0324   mapView->showPosition(level, centerView);
0325 }
0326 
0327 /** This method is used to covert cords so that they snap to the grid */
0328 QPoint CMapManager::cordsSnapGrid(QPoint oldPos)
0329 {
0330   QPoint newPos;
0331 
0332   int oldx = (int)(oldPos.x() / mapData->gridSize.width());
0333   int oldy =  (int)(oldPos.y() / mapData->gridSize.height());
0334   newPos.setX( oldx * mapData->gridSize.width());
0335   newPos.setY( oldy * mapData->gridSize.height());
0336 
0337   return newPos;
0338 }
0339 
0340 /**
0341  * Used to create a new view of the a given level and
0342  * center the view on the given position.
0343  */
0344 void CMapManager::openNewMapView(QPoint pos,CMapLevel *level)
0345 {
0346   CMapView *mapView = getActiveView();
0347   mapView->showPosition(pos,level);
0348 }
0349 
0350 /** Used to set properties of the view widget */
0351 void CMapManager::setPropertiesAllViews(QCursor *cursor,bool mouseTracking)
0352 {  
0353   activeView->setCursor(*cursor);
0354   activeView->setMouseTracking(mouseTracking);
0355 }
0356 
0357 /** Used to unselect all the elements in a level */
0358 void CMapManager::unselectElements(CMapLevel *level)
0359 {
0360   QList<CMapElement *> lst = level->getAllElements();
0361   foreach (CMapElement *element, lst)
0362   {
0363     element->setSelected(false);
0364     element->setEditMode(false);
0365   }  
0366 }
0367 
0368 /** Used to convert a text direction to a direction type */
0369 QString CMapManager::directionToText(directionTyp dir, QString specialCmd, bool shortName)
0370 {
0371   if ((uint)dir < NUM_DIRECTIONS)
0372   {
0373     if (shortName && ((uint)dir < NUM_DIRECTIONS / 2)) dir = (directionTyp) ((int)dir + NUM_DIRECTIONS / 2);
0374     return mapData->directions[dir];
0375   }
0376   else
0377   {
0378     return specialCmd;
0379   }
0380 }
0381 
0382 /** Used to convert a direction type to a text direction */
0383 directionTyp CMapManager::textToDirection(QString text)
0384 {
0385   directionTyp dir = SPECIAL;  
0386 
0387   for (uint i = 0; i < NUM_DIRECTIONS; ++i)
0388   {
0389     if (text == mapData->directions[i])
0390     {
0391       if (i>9)
0392       {
0393         dir = (directionTyp)(i-10);
0394       }
0395       else
0396       {
0397         dir =  (directionTyp)i;
0398       }
0399       break;
0400     }
0401   }
0402   
0403   return dir;
0404 }
0405 
0406 /** Used to set the login room */
0407 void CMapManager::setLoginRoom(CMapRoom *room)
0408 {
0409   openCommandGroup(i18n("Change Login Room"));
0410   if (loginRoom)
0411   {
0412     CMapCmdElementProperties *cmdRemove = new CMapCmdElementProperties(this,i18n("Remove Login Room Status"),loginRoom);
0413     cmdRemove->getOrgProperties().writeEntry("Login",true);
0414     cmdRemove->getNewProperties().writeEntry("Login",false);
0415 
0416     addCommand(cmdRemove);
0417   }
0418 
0419   CMapCmdElementProperties *cmdSet = new CMapCmdElementProperties(this,i18n("Set Login Room Status"),room);
0420 
0421   cmdSet->getOrgProperties().writeEntry("Login",false);
0422   cmdSet->getNewProperties().writeEntry("Login",true);
0423 
0424   addCommand(cmdSet);
0425 
0426   closeCommandGroup();
0427 }
0428 
0429 /** Uesd to return the login room */
0430 CMapRoom *CMapManager::getLoginRoom()
0431 {
0432   return loginRoom;
0433 }
0434 
0435 /** Should only be called by CMapRoom.setCurrentRoom() */
0436 void CMapManager::setCurrentRoomWithoutUndo(CMapRoom *room)
0437 {
0438   currentRoom = room;
0439 }
0440 
0441 /** Should only be called by CMapRoom.setLoginRoom() */
0442 void CMapManager::setLoginRoomWithoutUndo(CMapRoom *room)
0443 {
0444   loginRoom = room;
0445 }
0446 
0447 
0448 /** Used to set the current room */
0449 void CMapManager::setCurrentRoom(CMapRoom *room)
0450 {
0451   openCommandGroup(i18n("Change Current Room"));
0452 
0453   CMapCmdElementProperties *cmdRemove = new CMapCmdElementProperties(this,i18n("Remove Current Room Status"),currentRoom);
0454   cmdRemove->getOrgProperties().writeEntry("Current",true);
0455   cmdRemove->getNewProperties().writeEntry("Current",false);
0456   addCommand(cmdRemove);
0457 
0458   CMapCmdElementProperties *cmdSet = new CMapCmdElementProperties(this,i18n("Set Current Room Status"),room);
0459   cmdSet->getOrgProperties().writeEntry("Current",false);
0460   cmdSet->getNewProperties().writeEntry("Current",true);
0461   addCommand(cmdSet);
0462 
0463   closeCommandGroup();
0464 }
0465 
0466 /** Uesd to return the current room */
0467 CMapRoom *CMapManager::getCurrentRoom()
0468 {
0469   return currentRoom;
0470 }
0471 
0472 /** This method is used to find a element from a list of properties
0473   * @param properties The list of proerties
0474   * @return The element if it's found otherwise NULL */
0475 CMapElement *CMapManager::findElement(KConfigGroup properties)
0476 {
0477   elementTyp type = (elementTyp)properties.readEntry("Type",(uint)OTHER);
0478 
0479   if (type == OTHER) return nullptr;
0480 
0481   if (type==PATH)
0482   {
0483     CMapLevel *srcLevel = findLevel(properties.readEntry("SrcLevel",-1));
0484     CMapRoom *srcRoom = srcLevel->findRoom(properties.readEntry("SrcRoom",-1));
0485     if (!srcRoom) return nullptr;
0486     directionTyp srcDir = (directionTyp)properties.readEntry("SrcDir",0);
0487 
0488     QString specialCommand = properties.readEntry("SpecialCmdSrc","");
0489     return srcRoom->getPathDirection(srcDir,specialCommand);
0490   }
0491 
0492   CMapLevel *level = findLevel(properties.readEntry("Level",-5));
0493   if (!level) return nullptr;
0494 
0495   if (type == ROOM)
0496   {
0497     CMapRoom *room = level->findRoom(properties.readEntry("RoomID",-5));
0498     return room;
0499   }
0500 
0501   int x = properties.readEntry("X",-5);
0502   int y = properties.readEntry("Y",-5);
0503 
0504   foreach (CMapText *text, *level->getTextList())
0505     if ((text->getX()==x) && (text->getY()==y))
0506       return text;
0507 
0508   return nullptr;
0509 }
0510 
0511 /** Used to erase the map. This will erase all elements and can't be undone */
0512 void CMapManager::eraseMap(void)
0513 {
0514   if (!mapData->rootZone) return;
0515 
0516   eraseZone(mapData->rootZone);
0517   delete mapData->rootZone;
0518   mapData->rootZone = nullptr;
0519 
0520   m_zoneCount = 0;
0521   m_levelCount = 0;
0522 
0523   if (activeView) activeView->setLevel(nullptr);
0524 
0525   for (CMapPluginBase *plugin : pluginList)
0526     plugin->mapErased();
0527 
0528   loginRoom = nullptr;
0529   currentRoom = nullptr;
0530   elementEdit = nullptr;
0531 }
0532 
0533 void CMapManager::eraseZone(CMapZone *zone)
0534 {
0535   if (zone == nullptr)
0536     return;
0537 
0538   QList<CMapLevel *> levels;
0539   for (unsigned int idx = 0; idx < zone->levelCount(); ++idx)
0540     levels.append(zone->getLevel(idx));
0541   foreach (CMapLevel *level, levels)
0542   {
0543     // TODO: this seems to not delete things correctly
0544     foreach (CMapRoom *room, *level->getRoomList())
0545     {        
0546       room->getPathList()->clear();
0547       room->getConnectingPathList()->clear();
0548     }
0549     level->getRoomList()->clear();
0550     level->getTextList()->clear();
0551 
0552     delete level;
0553   }
0554 }
0555 
0556 /** Create new bottom or top level depending on the given direction */
0557 CMapLevel *CMapManager::createLevel(directionTyp dir)
0558 {
0559   CMapLevel *result = nullptr;
0560 
0561   CMapCmdLevelCreate *cmd = nullptr;
0562 
0563   int pos = (dir == UP) ? getZone()->levelCount() : 0;
0564   if (getUndoActive())
0565   {
0566     cmd = new CMapCmdLevelCreate(this,i18n("Create Level"),pos);
0567     addCommand(cmd);
0568     
0569     result = cmd->getLevel();
0570   }
0571   else
0572   {
0573     result = new CMapLevel(this, pos);
0574   }
0575   
0576   return result;
0577 }
0578 
0579 /** This is used to find a level with a given id
0580   * @param id The id of the level to find
0581   * @return Null if no level is found otherwise a pointer to the level */
0582 CMapLevel *CMapManager::findLevel(unsigned int id)
0583 {
0584   CMapZone *zone = getZone();
0585   for (unsigned int idx = 0; idx < zone->levelCount(); ++idx)
0586   {
0587     CMapLevel *level = zone->getLevel(idx);
0588     if (level->getLevelID() == id)
0589       return level;
0590   }
0591 
0592   return nullptr;
0593 }
0594 
0595 /** Create new map */
0596 void CMapManager::createNewMap()
0597 {
0598   // Create the root zone
0599   getMapData()->rootZone = nullptr;  
0600 
0601   CMapZone *zone = getZone();
0602 
0603   // Create a empty room in the first level of the new zone
0604   CMapRoom *room = CMapElementUtil::createRoom(this, QPoint(2 * mapData->gridSize.width(),2 * mapData->gridSize.height()),zone->firstLevel());
0605   setCurrentRoomWithoutUndo(room);
0606   setLoginRoomWithoutUndo(room);
0607 
0608   if (!activeView) return;
0609 
0610   if (currentRoom) activeView->showPosition(currentRoom->getLowPos(),zone->firstLevel());
0611 
0612   if (activeView->getCurrentlyViewedLevel()==nullptr)  
0613     activeView->showPosition(loginRoom,true);
0614 
0615   for (CMapPluginBase *plugin : pluginList)
0616     plugin->newMapCreated();
0617 
0618   activeView->changed();
0619 }
0620 
0621 /** Used to create a new room that can be undone/redone
0622    * @param pos The position to create the room
0623    * @param level The level to create the room in
0624    */
0625 void CMapManager::createRoom(QPoint pos,CMapLevel *level)
0626 {
0627   if (getUndoActive())
0628   {
0629     KMemConfig properties;
0630     KConfigGroup props = properties.group("Properties");
0631     props.writeEntry("Type",(int)ROOM);
0632     props.writeEntry("X",pos.x());
0633     props.writeEntry("Y",pos.y());
0634     props.writeEntry("Level",level->getLevelID());
0635     CMapCmdElementCreate *command = new CMapCmdElementCreate(this,i18n("Create Room"));
0636     command->addElement(&properties);
0637     addCommand(command);
0638   }
0639   else
0640   {
0641     CMapElementUtil::createRoom(this, pos, level);
0642   }
0643 }
0644 
0645 /** Used to create a new text label */
0646 void CMapManager::createText(QPoint pos,CMapLevel *level,QString str)
0647 {
0648   if (getUndoActive())
0649   {
0650     KMemConfig properties;
0651     KConfigGroup props = properties.group("Properties");
0652     props.writeEntry("Type",(int)TEXT);
0653     props.writeEntry("X",pos.x());
0654     props.writeEntry("Y",pos.y());
0655 
0656     if (level)
0657     {
0658       props.writeEntry("Level",level->getLevelID());
0659     }
0660     
0661     props.writeEntry("Text",str);
0662 
0663     CMapCmdElementCreate *command = new CMapCmdElementCreate(this,i18n("Create Text"));
0664     command->addElement(&properties);
0665     addCommand(command);
0666   }
0667   else
0668   {
0669     CMapElementUtil::createText(this, pos, level, str);
0670   }
0671 }
0672 
0673 /** Used to create a new text label */
0674 void CMapManager::createText(QPoint pos,CMapLevel *level,QString str,QFont font,QColor col)
0675 {
0676   if (getUndoActive())
0677   {
0678     KMemConfig properties;
0679     KConfigGroup props = properties.group("Properties");
0680     props.writeEntry("Type",(int)TEXT);
0681     props.writeEntry("X",pos.x());
0682     props.writeEntry("Y",pos.y());
0683     if (level)
0684     {
0685       props.writeEntry("Level",level->getLevelID());
0686     }
0687 
0688     props.writeEntry("Text",str);
0689     props.writeEntry("Font",font);
0690     props.writeEntry("Color",col);
0691 
0692     CMapCmdElementCreate *command = new CMapCmdElementCreate(this,i18n("Create Text"));
0693     command->addElement(&properties);
0694     addCommand(command);
0695   }
0696   else
0697   {
0698     CMapElementUtil::createText(this, pos, level, str, font, col);
0699   }
0700 }
0701 
0702 
0703 /** Used to create a new path*/
0704 CMapPath *CMapManager::createPath(QPoint srcPos,CMapLevel *srcLevel,directionTyp srcDir,
0705                                   QPoint destPos,CMapLevel *destLevel,directionTyp destDir)
0706 {
0707   CMapRoom *room=nullptr;
0708   CMapRoom *srcRoom=nullptr;
0709   CMapRoom *destRoom=nullptr;
0710 
0711   if (!srcLevel || !destLevel)
0712     return nullptr;
0713 
0714   foreach (room, *srcLevel->getRoomList())
0715   {
0716     if (room->getLowPos() == srcPos)
0717     {
0718       srcRoom = room;
0719       break;
0720     }
0721   }
0722 
0723   foreach (room, *destLevel->getRoomList())
0724   {
0725     if (room->getLowPos()  == destPos)
0726     {
0727       destRoom = room;
0728        break;
0729     }
0730   }
0731 
0732   return createPath(srcRoom,srcDir,destRoom,destDir);
0733 }
0734 
0735 CMapPath *CMapManager::createPath(CMapRoom *srcRoom,CMapRoom *destRoom)
0736 {
0737   CMapPath *result = nullptr;
0738 
0739   KMemConfig properties;
0740   KConfigGroup props = properties.group("Properties");
0741   // Auto-set the directions if possible
0742   directionTyp srcDir = srcRoom->bestDirectionToRoom(destRoom);
0743   directionTyp destDir = getOpsiteDirection(srcDir);
0744   if (srcDir != SPECIAL) {
0745     // nothing if there is an exit already
0746     if (srcRoom->getPathDirection(srcDir, QString())) srcDir = SPECIAL;
0747     if (destRoom->getPathDirection(destDir, QString())) destDir = SPECIAL;
0748   }
0749   props.writeEntry("SrcDir", (int) srcDir);
0750   props.writeEntry("DestDir", (int) destDir);
0751 
0752   DlgMapPathProperties d(this,props,false);
0753 
0754   if (!d.exec()) return nullptr;
0755 
0756   srcDir = (directionTyp)props.readEntry("SrcDir",0);
0757   destDir = (directionTyp)props.readEntry("DestDir",0);
0758   QString specialCmdSrc = props.readEntry("SpecialCmdSrc");
0759   QString specialCmdDest = props.readEntry("SpecialCmdDest");
0760 
0761   if (srcRoom->getPathDirection(srcDir,specialCmdSrc) || destRoom->getPathDirection(destDir,specialCmdDest))
0762   {
0763     KMessageBox::information (nullptr,i18n("A path already exists at this location"),i18n("KMuddy Mapper"));
0764     return nullptr;
0765   }
0766 
0767   // create
0768   props.writeEntry("Type",(int)PATH);
0769   props.writeEntry("SrcRoom",srcRoom->getRoomID());
0770   props.writeEntry("SrcDir",(int)srcDir);
0771   props.writeEntry("SrcLevel",srcRoom->getLevel()->getLevelID());
0772   props.writeEntry("DestRoom",destRoom->getRoomID());
0773   props.writeEntry("DestDir",(int)destDir);        
0774   props.writeEntry("DestLevel",destRoom->getLevel()->getLevelID());
0775 
0776   CMapCmdElementCreate *command = new CMapCmdElementCreate(this,i18n("Create Path"));
0777   command->addElement(&properties);
0778 
0779   addCommand(command);
0780 
0781   QList<CMapElement *> *elements=command->getElements();
0782 
0783   foreach (CMapElement *el, *elements)
0784     if (el->getElementType()==PATH)
0785       result = (CMapPath *)el;
0786 
0787   return result;
0788 }
0789 
0790 
0791 /** Used to create a new path*/
0792 CMapPath *CMapManager::createPath (CMapRoom *srcRoom,directionTyp srcDir,CMapRoom *destRoom,directionTyp destDir,bool undoable, bool twoWay)
0793 {
0794   // FIXME_jp : Allow this to call lowlevel mapper methods when undo is not active
0795   //            but becarefull of second stage stuff
0796 
0797   CMapPath *result = nullptr;
0798   
0799   KMemConfig properties;
0800   KConfigGroup props = properties.group("Properties");
0801   props.writeEntry("Type",(int)PATH);
0802   props.writeEntry("SrcRoom",srcRoom->getRoomID());
0803   props.writeEntry("SrcLevel",srcRoom->getLevel()->getLevelID());
0804   props.writeEntry("SrcDir",(int)srcDir);
0805   props.writeEntry("DestRoom",destRoom->getRoomID());
0806   props.writeEntry("DestLevel",destRoom->getLevel()->getLevelID());
0807   props.writeEntry("DestDir",(int)destDir);
0808   props.writeEntry("PathTwoWay",twoWay);
0809 
0810   CMapCmdElementCreate *command = new CMapCmdElementCreate(this,i18n("Create Path"));
0811   command->addElement(&properties);
0812   bool active = getUndoActive();
0813   if (!undoable)
0814   {
0815     setUndoActive(false);
0816   }
0817     
0818   addCommand(command);
0819 
0820   if (!undoable)
0821   {
0822     setUndoActive(active);
0823   }
0824 
0825   QList<CMapElement *> *elements=command->getElements();
0826   foreach (CMapElement *el, *elements)
0827     if (el->getElementType()==PATH)
0828       result = (CMapPath *)el;
0829 
0830   return result;
0831 }
0832 
0833 
0834 /** Find the first room in the map, if one can't be found then create one */
0835 CMapRoom *CMapManager::findFirstRoom(CMapRoom *existingRoom)
0836 {
0837   CMapZone *zone = getZone();
0838   for (unsigned int idx = 0; idx < zone->levelCount(); ++idx)
0839   {
0840     CMapLevel *level = zone->getLevel(idx);
0841     foreach (CMapRoom *room, *level->getRoomList())
0842       if (room!=existingRoom)
0843         return room;
0844   }
0845 
0846   // If we get to this point then no room was found so create one
0847   return CMapElementUtil::createRoom(this, QPoint(2 * mapData->gridSize.width(),2 * mapData->gridSize.height()), getZone()->firstLevel());
0848 }
0849 
0850 void CMapManager::deleteLevel(CMapLevel *level)
0851 {
0852   //FIXME_jp: Check to see what happens when all levels are deleted.
0853     //          It may be nessecary to create a new one.
0854 
0855   // Delete the level
0856   CMapCmdLevelDelete *cmd = new CMapCmdLevelDelete(this,i18n("Delete Level"),level);
0857   addCommand(cmd);
0858 }
0859 
0860 
0861 /** Check to see if a string is a valid move command
0862   * @param dirCmd The command that was typed
0863   * @return True, if a valid command otherwise false */
0864 bool CMapManager::validMoveCmd(QString dirCmd)
0865 {
0866   if (dirCmd.isEmpty()) return false;
0867 
0868   // check for directions
0869   for (uint i = 0; i < NUM_DIRECTIONS; ++i)
0870     if (mapData->directions[i] == dirCmd)
0871       return true;
0872   
0873   if (currentRoom)
0874     foreach (CMapPath *path, *currentRoom->getPathList())
0875       if (path->getSpecialExit() && (path->getSpecialCmd()==dirCmd))
0876         return true;
0877 
0878   return false;
0879 }
0880 
0881 /** Used to delete a element from the map, should not be used by deleteElement as
0882   * it does not use groups                                                */
0883 // FIXME_jp : This method can be removed if the deleteElement method is recursive
0884 void CMapManager::deleteElementWithoutGroup(CMapElement *element,bool delOpsite)
0885 {
0886   KMemConfig properties;
0887 
0888   element->saveProperties(properties.group("Properties"));
0889 
0890   CMapCmdElementDelete *command = new CMapCmdElementDelete(this,i18n("Delete Element"),delOpsite);
0891 
0892   command->addElement(&properties);
0893   addCommand(command);
0894 }
0895 
0896 /** Used to delete a element from the map */
0897 void CMapManager::deleteElement(CMapElement *element,bool delOpsite)
0898 {
0899   openCommandGroup(i18n("Delete Element"));
0900 
0901   // If the element is a room, then we also need to delete all its paths
0902   if (element->getElementType()==ROOM)
0903   {
0904     CMapRoom *room = (CMapRoom *)element;
0905     if (room->getLinkedElement())  
0906     {
0907       deleteElementWithoutGroup(room->getLinkedElement(),true);
0908     }
0909 
0910     QList<CMapPath *> wipePaths;
0911     // Delete the paths for the room
0912     foreach (CMapPath *path, *room->getPathList())
0913       if (!wipePaths.contains(path))
0914         wipePaths.push_back(path);
0915     // Delete any paths connecting with this room
0916     foreach (CMapPath *path, *room->getConnectingPathList())
0917       if (!wipePaths.contains(path))
0918         wipePaths.push_back(path);
0919     foreach (CMapPath *path, wipePaths)
0920       deleteElementWithoutGroup(path,false);
0921   }
0922 
0923   if (element->getElementType() == ZONE)
0924   {
0925     // Delete the levels in the zone
0926     CMapZone *zone = (CMapZone *)element;
0927     QList<CMapLevel *> levels;
0928     for (unsigned int idx = 0; idx < zone->levelCount(); ++idx)
0929       levels.append(zone->getLevel(idx));
0930     foreach (CMapLevel *level, levels)
0931       deleteLevel(level);
0932   }
0933 
0934   deleteElementWithoutGroup(element,delOpsite);
0935 
0936   closeCommandGroup();
0937 }
0938 
0939 bool CMapManager::isClean() const
0940 {
0941   return commandHistory->isClean();
0942 }
0943 
0944 /** Used to load a map */
0945 void CMapManager::importMap(const QString& file,CMapFileFilterBase *filter)
0946 {
0947   QFile f(file);
0948 
0949   setUndoActive(false);
0950   commandHistory->clear();
0951   historyGroup = nullptr;
0952 
0953   eraseMap();
0954 
0955   if (!f.exists())
0956     createNewMap();
0957   else {
0958     // Load the map using the correct filter
0959     filter->loadData(file);
0960   }
0961 
0962   if (!getLoginRoom())
0963   {
0964     CMapRoom *firstRoom = findFirstRoom(nullptr);
0965     setLoginRoom(firstRoom);
0966   }
0967 
0968   setCurrentRoomWithoutUndo(loginRoom);
0969 
0970   if (loginRoom && activeView)
0971   {
0972     if (activeView->getCurrentlyViewedLevel()==nullptr)  
0973       activeView->showPosition(loginRoom,true);
0974     setCurrentRoom(loginRoom);
0975   }
0976 
0977   setUndoActive(true);
0978 }
0979 
0980 /** Used to save a map */
0981 void CMapManager::exportMap(const QString& url,CMapFileFilterBase *filter)
0982 {
0983   filter->saveData(url);
0984   commandHistory->setClean();
0985 }
0986 
0987 /** Used to inform to change the state of the navigation tools */
0988 void CMapManager::activeViewChanged(void)
0989 {
0990   if (!activeView) return;
0991   CMapLevel *level = activeView->getCurrentlyViewedLevel();
0992   if (!level) return;
0993 
0994   // nothing here at this time
0995 }
0996 
0997 void CMapManager::levelChanged(CMapLevel *level)
0998 {
0999   if (level==nullptr)
1000     return;
1001 
1002   activeView->changedLevel(level);
1003 }
1004 
1005 /** Used to inform the various parts of the mapper that a element has changed */
1006 void CMapManager::changedElement(CMapElement *element)
1007 {
1008   if (element==nullptr)
1009     return;
1010   if (!activeView) return;
1011 
1012   for (CMapPluginBase *plugin : pluginList)
1013     plugin->elementChanged(element);
1014 
1015   activeView->changedElement(element);
1016 }
1017 
1018 /** Used to inform the various parts of the mapper that a element has added */
1019 void CMapManager::addedElement(CMapElement *element)
1020 {
1021   if (!activeView) return;
1022   if (activeView->getCurrentlyViewedLevel())
1023     activeView->addedElement(element);
1024 }
1025 
1026 
1027 /** Used to alter the path properties */
1028 bool CMapManager::propertiesPath(CMapPath *path)
1029 {
1030   DlgMapPathProperties d(this,path);
1031 
1032   return d.exec();
1033 }
1034 
1035 /** Used to alter the room properties */
1036 bool CMapManager::propertiesRoom(CMapRoom *room)
1037 {
1038   openCommandGroup("Change room properties");
1039   DlgMapRoomProperties d(this,room);
1040 
1041   bool b = d.exec();
1042   
1043   closeCommandGroup();
1044   
1045   return b;
1046 }
1047 
1048 /** Used to alter the text properties */
1049 bool CMapManager::propertiesText(CMapText *text)
1050 {
1051   DlgMapTextProperties d(this,text);
1052 
1053   if (d.exec())
1054   {
1055     text->updateLinkElements();
1056     return true;
1057   }
1058 
1059   return false;
1060 }
1061 
1062 void CMapManager::setDefaultOptions()
1063 {
1064   cGlobalSettings *gs = cGlobalSettings::self();
1065   // directions
1066   gs->setDefaultString ("mapper-direction-north", "north");
1067   gs->setDefaultString ("mapper-direction-northeast", "northeast");
1068   gs->setDefaultString ("mapper-direction-east", "east");
1069   gs->setDefaultString ("mapper-direction-southeast", "southeast");
1070   gs->setDefaultString ("mapper-direction-south", "south");
1071   gs->setDefaultString ("mapper-direction-southwest", "southwest");
1072   gs->setDefaultString ("mapper-direction-west", "west");
1073   gs->setDefaultString ("mapper-direction-northwest", "northwest");
1074   gs->setDefaultString ("mapper-direction-up", "up");
1075   gs->setDefaultString ("mapper-direction-down", "down");
1076   gs->setDefaultString ("mapper-direction-n", "n");
1077   gs->setDefaultString ("mapper-direction-ne", "ne");
1078   gs->setDefaultString ("mapper-direction-e", "e");
1079   gs->setDefaultString ("mapper-direction-se", "se");
1080   gs->setDefaultString ("mapper-direction-s", "s");
1081   gs->setDefaultString ("mapper-direction-sw", "sw");
1082   gs->setDefaultString ("mapper-direction-w", "w");
1083   gs->setDefaultString ("mapper-direction-nw", "nw");
1084   gs->setDefaultString ("mapper-direction-u", "u");
1085   gs->setDefaultString ("mapper-direction-d", "d");
1086 
1087   // move check
1088   gs->setDefaultBool ("mapper-movement-validcheck", false);
1089 
1090   gs->setDefaultColor ("mapper-color-Background", QColor(12*16,12*16,12*16));
1091   gs->setDefaultColor ("mapper-color-Grid",QColor(160,160,160));
1092   gs->setDefaultColor ("mapper-color-LowerRoom", Qt::darkGray);
1093   gs->setDefaultColor ("mapper-color-LowerZone", Qt::darkGray);
1094   gs->setDefaultColor ("mapper-color-LowerText", Qt::darkGray);
1095   gs->setDefaultColor ("mapper-color-DefaultRoom", QColor(32,255,0));
1096   gs->setDefaultColor ("mapper-color-DefaultZone", QColor(32,255,0));
1097   gs->setDefaultColor ("mapper-color-HigherRoom", Qt::white);
1098   gs->setDefaultColor ("mapper-color-HigherZone", Qt::white);
1099   gs->setDefaultColor ("mapper-color-HigherText", Qt::white);
1100   gs->setDefaultColor ("mapper-color-LowerPath", Qt::darkGray);
1101   gs->setDefaultColor ("mapper-color-DefaultPath", Qt::black);
1102   gs->setDefaultColor ("mapper-color-HigherPath", Qt::white);
1103   gs->setDefaultColor ("mapper-color-DefaultText", Qt::black);
1104   gs->setDefaultColor ("mapper-color-Selected", Qt::blue);
1105   gs->setDefaultColor ("mapper-color-Special", Qt::yellow);
1106   gs->setDefaultColor ("mapper-color-Login", Qt::blue);
1107   gs->setDefaultColor ("mapper-color-Edit", Qt::red);
1108   gs->setDefaultColor ("mapper-color-Current", Qt::red);
1109 
1110   // read speedwalk options
1111   gs->setDefaultBool ("mapper-speedwalk-abort-active", true);
1112   gs->setDefaultInt ("mapper-speedwalk-abort-limit", 100);
1113   gs->setDefaultInt ("mapper-speedwalk-delay", 0);
1114 
1115 
1116 }
1117 
1118 /** Used to read the map options */
1119 void CMapManager::readOptions()
1120 {  
1121   cGlobalSettings *gs = cGlobalSettings::self();
1122 
1123   // Read directions
1124   getMapData()->directions[NORTH] = gs->getString ("mapper-direction-north");
1125   getMapData()->directions[NORTHEAST] = gs->getString ("mapper-direction-northeast");
1126   getMapData()->directions[EAST] = gs->getString ("mapper-direction-east");
1127   getMapData()->directions[SOUTHEAST] = gs->getString ("mapper-direction-southeast");
1128   getMapData()->directions[SOUTH] = gs->getString ("mapper-direction-south");
1129   getMapData()->directions[SOUTHWEST] = gs->getString ("mapper-direction-southwest");
1130   getMapData()->directions[WEST] = gs->getString ("mapper-direction-west");
1131   getMapData()->directions[NORTHWEST] = gs->getString ("mapper-direction-northwest");
1132   getMapData()->directions[UP] = gs->getString ("mapper-direction-up");
1133   getMapData()->directions[DOWN] = gs->getString ("mapper-direction-down");
1134 
1135   int half = NUM_DIRECTIONS/2;
1136   getMapData()->directions[NORTH+half] = gs->getString ("mapper-direction-n");
1137   getMapData()->directions[NORTHEAST+half] = gs->getString ("mapper-direction-ne");
1138   getMapData()->directions[EAST+half] = gs->getString ("mapper-direction-e");
1139   getMapData()->directions[SOUTHEAST+half] = gs->getString ("mapper-direction-se");
1140   getMapData()->directions[SOUTH+half] = gs->getString ("mapper-direction-s");
1141   getMapData()->directions[SOUTHWEST+half] = gs->getString ("mapper-direction-sw");
1142   getMapData()->directions[WEST+half] = gs->getString ("mapper-direction-w");
1143   getMapData()->directions[NORTHWEST+half] = gs->getString ("mapper-direction-nw");
1144   getMapData()->directions[UP+half] = gs->getString ("mapper-direction-u");
1145   getMapData()->directions[DOWN+half] = gs->getString ("mapper-direction-d");
1146 
1147   // Read movecheck config
1148   getMapData()->validRoomCheck = gs->getBool ("mapper-movement-validcheck");
1149   int count = gs->getInt ("mapper-movement-check-count");
1150   getMapData()->failedMoveMsg.clear();
1151   for (int i = 1; i <= count; ++i)
1152     getMapData()->failedMoveMsg << gs->getString ("mapper-movement-check-" + QString::number (i));
1153 
1154   // Read map colors
1155   mapData->backgroundColor=gs->getColor ("mapper-color-Background");
1156   mapData->gridColor=gs->getColor ("mapper-color-Grid");
1157   mapData->lowerRoomColor=gs->getColor ("mapper-color-LowerRoom");
1158   mapData->lowerZoneColor=gs->getColor ("mapper-color-LowerZone");
1159   mapData->lowerTextColor=gs->getColor ("mapper-color-LowerText");
1160   mapData->defaultRoomColor=gs->getColor ("mapper-color-DefaultRoom");
1161   mapData->defaultZoneColor=gs->getColor ("mapper-color-DefaultZone");
1162   mapData->higherRoomColor=gs->getColor ("mapper-color-HigherRoom");
1163   mapData->higherZoneColor=gs->getColor ("mapper-color-HigherZone");
1164   mapData->higherTextColor=gs->getColor ("mapper-color-HigherText");
1165   mapData->lowerPathColor=gs->getColor ("mapper-color-LowerPath");
1166   mapData->defaultPathColor=gs->getColor ("mapper-color-DefaultPath");
1167   mapData->higherPathColor=gs->getColor ("mapper-color-HigherPath");
1168   mapData->defaultTextColor=gs->getColor ("mapper-color-DefaultText");
1169   mapData->selectedColor=gs->getColor ("mapper-color-Selected");
1170   mapData->specialColor = gs->getColor ("mapper-color-Special");
1171   mapData->loginColor=gs->getColor ("mapper-color-Login");
1172   mapData->editColor=gs->getColor ("mapper-color-Edit");
1173   mapData->currentColor=gs->getColor ("mapper-color-Current");
1174 
1175   // read speedwalk options
1176   mapData->speedwalkAbortActive = gs->getBool ("mapper-speedwalk-abort-active");
1177   mapData->speedwalkAbortLimit = gs->getInt ("mapper-speedwalk-abort-limit");
1178   mapData->speedwalkDelay = gs->getInt ("mapper-speedwalk-delay");
1179 
1180   activeView->readOptions();
1181 
1182   for (CMapPluginBase *plugin : pluginList)
1183     plugin->loadConfigOptions();
1184 }
1185 
1186 /** Used to write the map options */
1187 void CMapManager::saveGlobalConfig()
1188 {
1189   cGlobalSettings *gs = cGlobalSettings::self();
1190 
1191   // directions
1192   gs->setString ("mapper-direction-north", getMapData()->directions[NORTH]);
1193   gs->setString ("mapper-direction-northeast", getMapData()->directions[NORTHEAST]);
1194   gs->setString ("mapper-direction-east", getMapData()->directions[EAST]);
1195   gs->setString ("mapper-direction-southeast", getMapData()->directions[SOUTHEAST]);
1196   gs->setString ("mapper-direction-south", getMapData()->directions[SOUTH]);
1197   gs->setString ("mapper-direction-southeast", getMapData()->directions[SOUTHWEST]);
1198   gs->setString ("mapper-direction-west", getMapData()->directions[WEST]);
1199   gs->setString ("mapper-direction-northwest", getMapData()->directions[NORTHWEST]);
1200   gs->setString ("mapper-direction-up", getMapData()->directions[UP]);
1201   gs->setString ("mapper-direction-down", getMapData()->directions[DOWN]);
1202 
1203   int half = NUM_DIRECTIONS/2;
1204   gs->setString ("mapper-direction-n", getMapData()->directions[NORTH+half]);
1205   gs->setString ("mapper-direction-ne", getMapData()->directions[NORTHEAST+half]);
1206   gs->setString ("mapper-direction-e", getMapData()->directions[EAST+half]);
1207   gs->setString ("mapper-direction-se", getMapData()->directions[SOUTHEAST+half]);
1208   gs->setString ("mapper-direction-s", getMapData()->directions[SOUTH+half]);
1209   gs->setString ("mapper-direction-sw", getMapData()->directions[SOUTHWEST+half]);
1210   gs->setString ("mapper-direction-w", getMapData()->directions[WEST+half]);
1211   gs->setString ("mapper-direction-nw", getMapData()->directions[NORTHWEST+half]);
1212   gs->setString ("mapper-direction-u", getMapData()->directions[UP+half]);
1213   gs->setString ("mapper-direction-d", getMapData()->directions[DOWN+half]);
1214 
1215   // movement
1216   gs->setBool ("mapper-movement-validcheck", getMapData()->validRoomCheck);
1217   gs->setInt ("mapper-movement-check-count", getMapData()->failedMoveMsg.size());
1218   int idx = 0;
1219   QStringList::iterator it;
1220   for (it = getMapData()->failedMoveMsg.begin(); it != getMapData()->failedMoveMsg.end(); ++it)
1221     gs->setString ("mapper-movement-check-" + QString::number (++idx), *it);
1222 
1223   gs->setColor ("mapper-color-Background", mapData->backgroundColor);
1224   gs->setColor ("mapper-color-Grid", mapData->gridColor);
1225   gs->setColor ("mapper-color-LowerRoom", mapData->lowerRoomColor);
1226   gs->setColor ("mapper-color-LowerZone", mapData->lowerZoneColor);
1227   gs->setColor ("mapper-color-LowerText", mapData->lowerTextColor);
1228   gs->setColor ("mapper-color-HigherZone", mapData->higherZoneColor);
1229   gs->setColor ("mapper-color-DefaultRoom", mapData->defaultRoomColor);
1230   gs->setColor ("mapper-color-DefaultZone", mapData->defaultZoneColor);
1231   gs->setColor ("mapper-color-DefaultText", mapData->defaultTextColor);
1232   gs->setColor ("mapper-color-HigherRoom", mapData->higherRoomColor);
1233   gs->setColor ("mapper-color-HigherText", mapData->higherTextColor);
1234   gs->setColor ("mapper-color-LowerPath", mapData->lowerPathColor);
1235   gs->setColor ("mapper-color-DefaultPath",mapData->defaultPathColor);
1236   gs->setColor ("mapper-color-HigherPath", mapData->higherPathColor);
1237   gs->setColor ("mapper-color-Selected", mapData->selectedColor);
1238   gs->setColor ("mapper-color-Special", mapData->specialColor);
1239   gs->setColor ("mapper-color-Login", mapData->loginColor);
1240   gs->setColor ("mapper-color-Edit", mapData->editColor);
1241   gs->setColor ("mapper-color-Current", mapData->currentColor);
1242 
1243   for (CMapPluginBase *plugin : pluginList)
1244     plugin->saveConfigOptions();
1245 
1246   gs->setBool ("mapper-speedwalk-abort-active", mapData->speedwalkAbortActive);
1247   gs->setInt ("mapper-speedwalk-abort-limit", mapData->speedwalkAbortLimit);
1248   gs->setInt ("mapper-speedwalk-delay", mapData->speedwalkDelay);
1249 
1250   redrawAllViews();
1251 }
1252 
1253 void CMapManager::getCounts(int *levels,int *rooms,int *paths,int *labels)
1254 {
1255   *rooms = 0;
1256   *labels = 0;
1257   *paths = 0;
1258   CMapZone *zone = getZone();
1259   *levels = zone->levelCount();
1260 
1261   for (unsigned int idx = 0; idx < zone->levelCount(); ++idx)
1262   {
1263     CMapLevel *level = zone->getLevel(idx);
1264 
1265     foreach (CMapRoom *room, *level->getRoomList())
1266       *paths += room->getPathList()->count();
1267 
1268     *rooms += level->getRoomList()->count();
1269     *labels += level->getTextList()->count();
1270   }
1271 }
1272 
1273 directionTyp CMapManager::getOpsiteDirection(directionTyp dir)
1274 {
1275   directionTyp result = SOUTH;
1276   switch (dir)
1277   {
1278     case NORTH     : result = SOUTH; break;
1279     case SOUTH     : result = NORTH; break;
1280     case EAST      : result = WEST; break;
1281     case WEST      : result = EAST; break;
1282     case NORTHWEST : result = SOUTHEAST; break;
1283     case NORTHEAST : result = SOUTHWEST; break;
1284     case SOUTHWEST : result = NORTHEAST; break;
1285     case SOUTHEAST : result = NORTHWEST; break;
1286     case UP        : result = DOWN; break;
1287     case DOWN      : result = UP; break;
1288     case SPECIAL   : result = SPECIAL; break;
1289   }
1290 
1291   return result;
1292 }
1293 
1294 /** Used to set the current tool */
1295 void CMapManager::setCurrentTool(CMapToolBase *tool)
1296 {
1297   if (currentTool)
1298     currentTool->toolUnselected();
1299 
1300   currentTool=tool;
1301 
1302   if (currentTool)
1303     currentTool->toolSelected();
1304 
1305   activeView->changed();
1306 }
1307 
1308 /** Usd to get the current tool */
1309 CMapToolBase *CMapManager::getCurrentTool(void)
1310 {
1311   return currentTool;
1312 }
1313 
1314 /** This method puts a element into edit state */
1315 void CMapManager::setEditElement(CMapElement *element)
1316 {
1317   if (elementEdit)
1318   {
1319     elementEdit->setEditMode(false);
1320     changedElement(elementEdit);
1321   }
1322 
1323   element->setEditMode(true);
1324   elementEdit = element;
1325   changedElement(elementEdit);
1326 }
1327 
1328 /** This is used to remove edit state from element being edited */
1329 void CMapManager::unsetEditElement(void)
1330 {
1331   if (elementEdit)
1332   {
1333     elementEdit->setEditMode(false);
1334     changedElement(elementEdit);
1335   }
1336 }
1337 
1338 /** This gets the element that is being edited */
1339 CMapElement *CMapManager::getEditElement(void)
1340 {
1341   return elementEdit;
1342 }
1343 
1344 /** This cancels any editing */
1345 void CMapManager::stopEditing(void)
1346 {
1347   if (elementEdit)
1348   {
1349     elementEdit->setEditMode(false);
1350   }
1351 
1352   elementEdit = nullptr;
1353 }
1354 
1355 int CMapManager::getUndoActive(void)
1356 {
1357   return m_commandsActive;
1358 }
1359 
1360 /** This is used to tell the mapper if commands should be added to the history list */
1361 void CMapManager::setUndoActive(bool active)
1362 {
1363   m_commandsActive = active;
1364 }
1365 
1366 /** Used to add a command to the command history */
1367 void CMapManager::addCommand(CMapCommand *command)
1368 {
1369   if (getUndoActive())
1370   {
1371     if (historyGroup)
1372     {
1373       // If there is a history group, it will -not- execute anything. That will happen once the group gets closed.
1374       historyGroup->addCommand(command);
1375     }
1376     else
1377     {
1378       commandHistory->push(command);
1379     }
1380   }
1381   else
1382   {
1383     command->redo();
1384   }
1385 }
1386 
1387 void CMapManager::openCommandGroup(QString name)
1388 {
1389   CMapCmdGroup *group = new CMapCmdGroup(this,name);
1390   group->setPreviousGroup(historyGroup);
1391   historyGroup = group;
1392 }
1393 
1394 void CMapManager::closeCommandGroup()
1395 {
1396   CMapCmdGroup *currentGroup = historyGroup;
1397   CMapCmdGroup *oldGroup =historyGroup->getPreviousGroup();
1398   historyGroup = oldGroup;
1399   addCommand(currentGroup);
1400 }
1401 
1402 /** move the player relative to the current position of the player
1403   * @param cmd The move command used to move the player */
1404 void CMapManager::movePlayerBy(QString cmd)
1405 {
1406   QString specialCmd = "";
1407   directionTyp dir = textToDirection(cmd);
1408 
1409   if (dir == SPECIAL)
1410     specialCmd = cmd;
1411 
1412   movePlayerBy(dir, getActiveView()->getCreateMode(), specialCmd);
1413 }
1414 
1415 /** move the player relative to the current position of the player
1416   * This command has support for special paths, but can only move
1417   * anlong exsiting special paths. In other words, it is unable to
1418   * create special paths.
1419   */
1420 void CMapManager::movePlayerBy(directionTyp dir,bool create,QString specialCmd)
1421 {
1422   // Make sure that the room we are currently in is visible
1423   if (!currentRoom) return;
1424 
1425   CMapCmdMovePlayer *cmd = new CMapCmdMovePlayer(this, dir, specialCmd, create);
1426   addCommand(cmd);
1427 }
1428 
1429 /** Used to walk the player in the mud to a given room */
1430 void CMapManager::walkPlayerTo(CMapRoom *toRoom)
1431 {
1432   QQueue<CMapRoom *> roomsToVisit;
1433   CMapRoom *destRoom;
1434   CMapRoom *srcRoom;
1435   CMapPath *path;
1436   CMapRoom *foundRoom;
1437   signed int time = 0;
1438   bool bFound = false;
1439   CMapPath *foundPath = nullptr;
1440 
1441   int speedWalkAbortCount = 0;
1442 
1443   if ((!currentRoom) || (currentRoom == toRoom)) return;
1444 
1445   if (speedwalkActive)
1446   {
1447     KMessageBox::information (nullptr,i18n("Speedwalking is already in progress"),i18n("KMuddy Mapper"));
1448     return;
1449   }
1450 
1451   speedwalkActive = true;
1452 
1453   pathToWalk.clear();
1454 
1455   // Reset the seach count for all the rooms
1456   for (unsigned int idx = 0; idx < getZone()->levelCount(); ++idx)
1457   {
1458     CMapLevel *level = getZone()->getLevel(idx);
1459     foreach (CMapRoom *room, *level->getRoomList())
1460       room->setMoveTime(-1);
1461   }
1462 
1463   // Init things for the start room
1464   srcRoom = currentRoom;
1465   destRoom = (CMapRoom *)toRoom;
1466   srcRoom->setMoveTime(time++);
1467 
1468   // enqueue starting room
1469   roomsToVisit.enqueue(srcRoom);
1470   CMapRoom* room;
1471 
1472   // this is the Breadth First Search Algorithm
1473   while (!(roomsToVisit.isEmpty() || bFound))
1474   {
1475     foundRoom = roomsToVisit.dequeue();
1476 
1477     // for all neighbours of foundRoom
1478     foreach (path, *foundRoom->getPathList())
1479     {
1480       room = path->getDestRoom();
1481 
1482       if (room == destRoom)
1483       {
1484         bFound = true;
1485         break;
1486       }
1487 
1488       // neighbour has not been visited yet, enqueue it
1489       if (room->getMoveTime() == -1)
1490       {
1491         room->setMoveTime(time++);
1492         roomsToVisit.enqueue(room);
1493       }
1494     }
1495   }
1496 
1497   // Check to see if we were unable to find any paths
1498   if (!bFound)
1499   {
1500     speedwalkActive = false;
1501     KMessageBox::information (nullptr,i18n("The automapper was unable to find a path to requested room"),i18n("KMuddy Mapper"));
1502     return;
1503   }
1504 
1505   speedWalkAbortCount=0;
1506   
1507   // Trace steps that need to be taken backwards from the dest to the src room
1508   while(destRoom != srcRoom)
1509   {
1510     time = destRoom->getConnectingPathList()->first()->getSrcRoom()->getMoveTime();
1511     foundRoom = destRoom->getConnectingPathList()->first()->getSrcRoom();
1512 
1513     // Find the room with the shortest time as this is the room we
1514     // should be moving to.
1515     foreach (path, *destRoom->getConnectingPathList())
1516     {
1517       if (time == -1 || (path->getSrcRoom()->getMoveTime()<=time && path->getSrcRoom()->getMoveTime()!=-1))
1518       {
1519         time = path->getSrcRoom()->getMoveTime();
1520         foundRoom = path->getSrcRoom();
1521         foundPath = path;
1522       }
1523     }
1524 
1525     pathToWalk.append(directionToText(foundPath->getSrcDir(),foundPath->getSpecialCmd()));
1526 
1527     destRoom=foundRoom;
1528     // Check to make sure that tings are not stuck in a loop and abort
1529     // if the move limit is reached
1530 
1531     speedWalkAbortCount++;
1532     if (mapData->speedwalkAbortActive && (speedWalkAbortCount == mapData->speedwalkAbortLimit))
1533     {
1534       speedwalkActive = false;
1535       KMessageBox::information (nullptr,i18n("Speedwalk abort because move limit was reached"),i18n("KMuddy Mapper"));
1536 
1537       return;
1538     }
1539   }
1540 
1541   speedwalkProgressDlg->setTotalSteps(pathToWalk.count());
1542   speedwalkProgressDlg->setProgress(0);
1543 
1544   cActionManager *am = cActionManager::self();
1545   cStatus *status = dynamic_cast<cStatus *>(am->object ("status", am->activeSession()));
1546   if (status)
1547     status->statusBar()->addPermanentWidget(speedwalkProgressDlg,0);
1548   speedwalkProgressDlg->show();
1549   speedwalkProgress = 0;
1550 
1551   // Start walking path
1552   slotWalkPlayerAlongPath();
1553 }
1554 
1555 void CMapManager::slotAbortSpeedwalk(void)
1556 {
1557   pathToWalk.clear();
1558   speedwalkActive = false;
1559   speedwalkProgressDlg->setProgress(speedwalkProgressDlg->getTotalSteps());
1560   speedwalkProgressDlg->hide();
1561   cActionManager *am = cActionManager::self();
1562   cStatus *status = dynamic_cast<cStatus *>(am->object ("status", am->activeSession()));
1563   if (status)
1564     status->statusBar()->removeWidget(speedwalkProgressDlg);
1565 }
1566 
1567 /** Used to recersivly move the play along a speedwalk path */
1568 void CMapManager::slotWalkPlayerAlongPath(void)
1569 {
1570   if (speedwalkActive)
1571   {
1572     QString dir = pathToWalk.takeFirst();
1573 
1574     // TODO: using active session isn't a very good idea; progress bar should be shown on the mapper window, not in KMuddy's status bar; furthermore, the mapper should distinguish sessions and switch maps when session changes or something - until all this gets done, we cannot implement this any better
1575     mapperPlugin->sendCommand (mapperPlugin->activeSession(), dir);
1576     speedwalkProgressDlg->setProgress(++speedwalkProgress);
1577 
1578     // Walk the path
1579     if (!pathToWalk.isEmpty())
1580     {
1581       QTimer::singleShot( mapData->speedwalkDelay * 100, this, SLOT(slotWalkPlayerAlongPath()) );
1582     }
1583     else
1584     {
1585       slotAbortSpeedwalk();
1586     }
1587   }
1588 }
1589 
1590 /** This method is used to convert a direction into a offset */
1591 void CMapManager::directionToCord(directionTyp dir, QSize distance,QPoint *pos)
1592 {
1593   int x = pos->x();
1594   int y = pos->y();
1595 
1596   switch (dir)
1597   {
1598     case NORTH     : x = 0;
1599                      y = -distance.height();
1600                      break;
1601     case EAST      : x = distance.width();
1602                      y = 0;
1603                      break;
1604     case SOUTH     : x = 0;
1605                      y = distance.height();
1606                      break;
1607     case WEST      : x = -distance.width();
1608                      y = 0;
1609                          break;
1610     case NORTHEAST : x = distance.width();
1611                      y = -distance.height();
1612                      break;
1613     case NORTHWEST : x = -distance.width();
1614                      y = -distance.height();
1615                      break;
1616     case SOUTHEAST : x = distance.width();
1617                      y = distance.height();
1618                      break;
1619     case SOUTHWEST : x = -distance.width();
1620                      y = distance.height();
1621                      break;
1622     case UP        : break;
1623     case DOWN      : break;
1624     case SPECIAL   : break;
1625   }
1626 
1627   pos->setX(x);
1628   pos->setY(y);
1629 }
1630 
1631 /** This method is used to move the elements in a zone by the given vector */
1632 void CMapManager::moveMap(QPoint inc,CMapZone *)
1633 {
1634   CMapCmdMoveMap *cmd = new CMapCmdMoveMap(this,inc,i18n("Move Elements in map"));
1635   addCommand(cmd);
1636 }
1637 
1638 /** This method is used to make a path two way */
1639 void CMapManager::makePathTwoWay(CMapPath *path)
1640 {
1641   if (!path->getOpsitePath())
1642   {
1643     CMapCmdTogglePathTwoWay *cmd =  new CMapCmdTogglePathTwoWay(this,i18n("Make Path Two-Way"),path);
1644     addCommand(cmd);
1645   }
1646 }
1647 
1648 /** This method is used to make a path one way */
1649 void CMapManager::makePathOneWay(CMapPath *path)
1650 {
1651   if (path->getOpsitePath())
1652   {
1653     CMapCmdTogglePathTwoWay *cmd =  new CMapCmdTogglePathTwoWay(this,i18n("Make Path One-Way"),path);
1654     addCommand(cmd);
1655   }
1656 }
1657 
1658 /** Get the main window */
1659 CMapView *CMapManager::getActiveView()
1660 {  
1661   return activeView;
1662 }                                                                    
1663 
1664 /** Used to repaint all the views */
1665 void CMapManager::redrawAllViews(void)
1666 {
1667   activeView->changed();
1668 }
1669 
1670 CMapFileFilterBase *CMapManager::nativeFilter(bool isLoad)
1671 {
1672   for (CMapFileFilterBase *filter : m_fileFilter)
1673   {
1674     if (isLoad && (!filter->supportLoad())) continue;
1675     if ((!isLoad) && (!filter->supportSave())) continue;
1676     if (filter->isNative()) return filter;
1677   }
1678   return nullptr;
1679 }
1680 
1681 QString CMapManager::defaultSavePath () const
1682 {
1683   return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/maps/";
1684 }
1685 
1686 /** This is a debug function and not for genreal use */
1687 void CMapManager::changeProperties(CMapElement *element,QString key,QString oldData,QString newData)
1688 {
1689   CMapCmdElementProperties *cmd = new CMapCmdElementProperties(this,i18n("Change Element Property"),element);
1690   cmd->getOrgProperties().writeEntry(key,oldData);
1691   cmd->getNewProperties().writeEntry(key,newData);
1692   addCommand(cmd);
1693 }
1694 
1695 /** This is a debug function and not for genreal use */
1696 void CMapManager::changeProperties(CMapElement *element,QString key,int oldData,int newData)
1697 {
1698   CMapCmdElementProperties *cmd = new CMapCmdElementProperties(this,i18n("Change Element Property"),element);
1699   cmd->getOrgProperties().writeEntry(key,oldData);
1700   cmd->getNewProperties().writeEntry(key,newData);
1701   addCommand(cmd);
1702 }
1703 
1704 /** This is a debug function and not for genreal use */
1705 void CMapManager::generateTestMap()
1706 {
1707   qDebug() << "creating test map";
1708   bool smallMap = false;
1709 
1710   /////////////////////////////////////////////////////////////////////
1711   // Create a new empty map
1712   setUndoActive(false);
1713   commandHistory->clear();
1714   historyGroup = nullptr;
1715   eraseMap();
1716   createNewMap();
1717   setUndoActive(true);
1718 
1719   /////////////////////////////////////////////////////////////////////
1720   // Create a test map
1721   // Test rooms and paths
1722 
1723   openCommandGroup("Create Test Map");
1724 
1725   if (!smallMap)
1726   {
1727     movePlayerBy(SOUTH,true,"");
1728     movePlayerBy(SOUTH,true,"");
1729     movePlayerBy(SOUTH,true,"");
1730     movePlayerBy(WEST,true,"");
1731     movePlayerBy(NORTH,true,"");
1732     movePlayerBy(EAST,true,"");
1733     movePlayerBy(EAST,true,"");
1734     movePlayerBy(SOUTH,true,"");
1735     movePlayerBy(SOUTH,true,"");
1736     movePlayerBy(SOUTH,true,"");
1737     movePlayerBy(EAST,true,"");
1738     movePlayerBy(EAST,true,"");
1739     movePlayerBy(EAST,true,"");
1740     movePlayerBy(EAST,true,"");
1741 
1742     movePlayerBy(UP,true,"");
1743     movePlayerBy(EAST,true,"");
1744     movePlayerBy(EAST,true,"");
1745     movePlayerBy(EAST,true,"");
1746     movePlayerBy(EAST,true,"");
1747     movePlayerBy(SOUTH,true,"");
1748     movePlayerBy(SOUTH,true,"");
1749     movePlayerBy(SOUTH,true,"");
1750     movePlayerBy(SOUTH,true,"");
1751     movePlayerBy(SOUTH,true,"");
1752     movePlayerBy(SOUTH,true,"");
1753     movePlayerBy(EAST,true,"");
1754     movePlayerBy(EAST,true,"");
1755     movePlayerBy(DOWN,true,"");
1756 
1757     movePlayerBy(DOWN,true,"");
1758     movePlayerBy(SOUTH,true,"");
1759     movePlayerBy(SOUTH,true,"");
1760     movePlayerBy(SOUTH,true,"");
1761     movePlayerBy(SOUTH,true,"");
1762     movePlayerBy(SOUTH,true,"");
1763     movePlayerBy(EAST,true,"");
1764     movePlayerBy(EAST,true,"");
1765     movePlayerBy(UP,true,"");
1766 
1767     movePlayerBy(SOUTH,true,"");
1768     movePlayerBy(WEST,true,"");
1769 
1770     // Test text
1771     changeProperties(getZone(),"Label","",i18n("Root Zone"));
1772 
1773   }
1774 
1775   CMapLevel *level = getZone()->firstLevel();
1776   if (level->getNextLevel())
1777     level = level->getNextLevel();
1778 
1779   QFont font = QFont("times");
1780   font.setPointSize(25);
1781   createText(QPoint(160,20),level,"Test Map",font,Qt::black);
1782 
1783 
1784   if (!smallMap)
1785   {
1786     CMapRoom *test1=CMapElementUtil::createRoom(this, QPoint(14*20,14*20),level);
1787     changeProperties(test1,"Label","",i18n("Test room 1"));
1788     changeProperties(test1,"LabelPos",(int)CMapRoom::HIDE,(int)CMapRoom::WEST);
1789     CMapRoom *test2=CMapElementUtil::createRoom(this, QPoint(20*20,20*20),level);
1790     changeProperties(test2,"Label","",i18n("Test room 2"));
1791     changeProperties(test2,"LabelPos",(int)CMapRoom::HIDE,(int)CMapRoom::EAST);
1792     CMapRoom *test3=CMapElementUtil::createRoom(this, QPoint(14*20,20*20),level);
1793     changeProperties(test3,"Label","",i18n("Test room 3"));
1794     changeProperties(test3,"LabelPos",(int)CMapRoom::HIDE,(int)CMapRoom::WEST);
1795     CMapRoom *test4=CMapElementUtil::createRoom(this, QPoint(20*20,14*20),level);
1796     changeProperties(test4,"Label","",i18n("Test room 4"));
1797     changeProperties(test4,"LabelPos",(int)CMapRoom::HIDE,(int)CMapRoom::NORTH);
1798 
1799     createPath(QPoint(14*20,14*20),level,SOUTHEAST,
1800                 QPoint(20*20,20*20),level,NORTHWEST);
1801     createPath(QPoint(20*20,20*20),level,NORTHWEST,
1802          QPoint(14*20,14*20),level,SOUTHEAST);
1803     createPath(QPoint(14*20,20*20),level,NORTHEAST,
1804                            QPoint(20*20,14*20),level,SOUTHWEST);
1805 
1806     createPath(QPoint(20*20,14*20),level,SOUTHWEST,
1807                            QPoint(14*20,20*20),level,NORTHEAST);
1808 
1809     CMapPath *specialPath = createPath(test1,SPECIAL,test4,SPECIAL);
1810     makePathTwoWay(specialPath);
1811 
1812     CMapCmdElementProperties *cmd = new CMapCmdElementProperties(this,i18n("Set Special Exit"),specialPath);
1813     cmd->getOrgProperties().writeEntry("SpecialCmdSrc","");
1814     cmd->getOrgProperties().writeEntry("SpecialCmdDest","");
1815     cmd->getOrgProperties().writeEntry("SpecialExit",false);
1816     cmd->getNewProperties().writeEntry("SpecialCmdSrc","enter");
1817     cmd->getNewProperties().writeEntry("SpecialCmdDest","out");
1818     cmd->getNewProperties().writeEntry("SpecialExit",true);
1819     addCommand(cmd);
1820   }
1821   closeCommandGroup();
1822   qDebug() << "test map created";
1823 }
1824 
1825 #include "moc_cmapmanager.cpp"