3 // (C) 1997-2007 Christopher J. Madsen
4 // (C) 2019 James Hammons
6 // GUEmap is licensed under either version 2 of the GPL, or (at your option)
7 // any later version. See LICENSE file for details.
9 // Implementation of the MapDoc class
17 const RoomCorner oppositeCorner[rcNumCorners] = {
19 rcSW, rcSE, rcNW, rcNE,
20 rcSSW, rcSSE, rcNNW, rcNNE
25 /////////////////////////////////////////////////////////////////////////////
28 IMPLEMENT_DYNCREATE(MapDoc, CDocument)
30 BEGIN_MESSAGE_MAP(MapDoc, CDocument)
31 //{{AFX_MSG_MAP(MapDoc)
32 ON_COMMAND(ID_EDIT_NAVIGATE, OnNavigationMode)
33 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
34 ON_COMMAND(ID_FILE_EXPORT, OnFileExport)
35 ON_UPDATE_COMMAND_UI(ID_EDIT_NAVIGATE, OnUpdateNavigationMode)
36 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
39 ON_COMMAND(ID_FILE_SEND_MAIL, OnFileSendMail)
40 ON_UPDATE_COMMAND_UI(ID_FILE_SEND_MAIL, OnUpdateFileSendMail)
42 ON_COMMAND_RANGE(ID_EDIT_MOVE_BOTTOM, ID_EDIT_MOVE_TOP, OnEditMoveMap)
43 ON_UPDATE_COMMAND_UI_RANGE(ID_EDIT_MOVE_BOTTOM, ID_EDIT_MOVE_TOP,
49 /////////////////////////////////////////////////////////////////////////////
50 // MapDoc construction/destruction
52 MapDoc::MapDoc(): locked(false), pageSize(42 * gridX, 53 * gridY), undoData(NULL)
54 // Initialize the document... :-P
67 bool MapDoc::OnNewDocument()
69 if (!CDocument::OnNewDocument())
72 // TODO: add reinitialization code here
73 // (SDI documents will reuse this document)
80 /////////////////////////////////////////////////////////////////////////////
81 // MapDoc serialization
82 //--------------------------------------------------------------------
83 // Make a backup file when we Save As:
85 bool MapDoc::DoSave(const char * pathName, bool replace)
87 const bool neededBackup = needBackup;
90 needBackup = true; // Saving to new name
92 const bool result = CDocument::DoSave(pathName, replace);
94 if (!pathName && (!result || !replace))
95 needBackup = neededBackup; // Did not save (or saved a copy)
102 // Allocate a CFile object for loading/saving a file:
104 // Rename the existing file if this is the first save after a load.
106 CFile * MapDoc::GetFile(const char * fileName, UINT openFlags, CFileException * error)
108 if (needBackup && (openFlags & (CFile::modeReadWrite | CFile::modeWrite)))
110 String backupFileName(fileName);
112 // Change the extension to .GM~:
113 // Look for a period after the last backslash (a period before
114 // the last backslash would be part of a directory name).
115 StrIdx dot = backupFileName.find_last_of(_T('.'));
117 if ((dot == String::npos) || (dot < backupFileName.find_last_of(_T('\\'))))
118 dot = backupFileName.size(); // No extension
120 backupFileName.replace(dot, String::npos, _T(".gm~"));
122 if (_tcsicmp(fileName, backupFileName.c_str()) == 0)
123 needBackup = false; // This file already has a .GM~ extension
126 ::DeleteFile(backupFileName.c_str());
128 if (::MoveFile(fileName, backupFileName.c_str()))
129 needBackup = false; // We made a backup file
133 // The rest is copied from CDocument::GetFile:
134 CMirrorFile * file = new CMirrorFile;
135 ASSERT(file != NULL);
137 if (!file->Open(fileName, openFlags, error))
150 void MapDoc::ReportSaveLoadException(const char * lpszPathName, CException * e, bool bSaving, UINT nIDPDefault)
152 if (e && e->IsKindOf(RUNTIME_CLASS(CArchiveException)))
156 switch (static_cast<CArchiveException *>(e)->m_cause)
158 case CArchiveException::badClass: msg = IDS_AR_NOT_GUEMAP; break;
159 case CArchiveException::badSchema: msg = IDS_AR_BAD_VERSION; break;
161 case CArchiveException::badIndex:
162 case CArchiveException::endOfFile:
163 msg = IDS_AR_CORRUPT;
169 AfxMessageBox(msg, MB_ICONEXCLAMATION);
174 CDocument::ReportSaveLoadException(lpszPathName, e, bSaving, nIDPDefault);
181 //--------------------------------------------------------------------
182 // Add a corner to an edge
185 // rNum: The room to add a corner by
186 // eNum: The edge to add a corner in
189 // The number of the newly created room
190 // -1 if we couldn't add a corner
192 int MapDoc::addCorner(RoomNum rNum, int eNum)
194 const QPoint cornerOffset[] = {
195 { gridX, -gridY }, { gridX, roomHeight }, // N, S
196 { roomWidth, gridY }, { -gridX, gridY }, // E, W
197 { roomWidth, -gridY }, { -gridX, -gridY }, // NE, NW
198 { roomWidth, roomHeight }, { -gridX, roomHeight }, // SE, SW
199 { 2 * gridX, -gridY }, { 0, -gridY }, // NNE, NNW
200 { 2 * gridX, roomHeight }, { 0, roomHeight } // SSE, SSW
203 if (room.size() == maxRooms)
206 MapEdge e1(edge[eNum]);
209 ASSERT(!(e1.type1 & etNoRoom2));
211 RoomCorner corner = (e1.room1 == rNum ? e1.end1 : e1.end2);
213 e1.type1 &= ~etOneWay; // Clear one-way
215 e2.type1 &= etObstructed | etOneWay; // Clear all but obstructed & one-way
216 e1.end2 = e2.end1 = rcCorner;
217 e1.room2 = e2.room1 = room.size();
219 QPoint pos = room[rNum].pos + cornerOffset[corner];
221 if (pos.x() < 0 || pos.x() + gridX > docSize.width() || pos.y() < 0 || pos.y() + gridY > docSize.height())
224 MapRoom * newRoom = new MapRoom();
225 newRoom->flags = rfCorner;
227 // newRoom->computeNameLength();
229 setUndoData(new UndoChanges(isDirty, e1.room2, 2, edge[eNum]));
231 addRoom(e1.room2, newRoom);
240 // Remove a corner from a connection:
243 // r: The room number of the corner to be removed
245 void MapDoc::deleteCorner(RoomNum r)
247 ASSERT(r < room.size() && room[r].isCorner());
253 memset(edges, 0, sizeof(edges));
257 for(EdgeConstItr e=edge.begin(); (count<2) && (e!=edge.end()); ++e)
261 edges[count].room1 = e->room2;
262 edges[count].end1 = e->end2;
263 edges[count].type1 |= e->type2;
264 edges[!count].type1 |= e->type1 & etSpecial;
265 delEdges.push_back(*e);
268 else if (e->room2 == r)
270 edges[count].room1 = e->room1;
271 edges[count].end1 = e->end1;
272 edges[count].type1|= e->type1;
273 delEdges.push_back(*e);
280 // MessageBeep(MB_ICONASTERISK); // Corner without 2 edges
289 if (edges[1].type1 & etSpecial)
295 edges[dest].end2 = edges[source].end1;
296 edges[dest].room2 = edges[source].room1;
297 edges[dest].type2 = edges[source].type1 & ~etSpecial;
299 if (edges[dest].room1 > r)
302 if (edges[dest].room2 > r)
305 const bool isMod = isDirty;
306 setUndoData(new UndoChanges(isMod, r, extractRoom(r), delEdges));
307 addEdge(edges[dest]);
311 void MapDoc::addEdge(const MapEdge & newEdge)
313 edge.push_back(newEdge);
316 // getEdgeRect(newEdge, rect);
317 // UpdateAllViews(NULL, 0, reinterpret_cast<CObject *>(&rect));
321 void MapDoc::addEdges(int n)
323 edge.reserve(edge.size() + n);
327 void MapDoc::deleteEdge(int n)
329 ASSERT(n < edge.size());
333 getEdgeRect(edge[n], rect);
335 edge.erase(edge.begin() + n);
337 // UpdateAllViews(NULL, 0, reinterpret_cast<CObject *>(&rect));
342 // Find the edge (if any) connected to a given corner:
345 // n: The room number to look for
346 // corner: The corner of room N we want
349 // newEdge.room1: The room at the other end of this edge
350 // newEdge.end1: The corner of the other room
351 // newEdge.type1: The type of connection at the other end
352 // newEdge.type2: The type of connection at this end etObstructed is moved
353 // from type2 to type1 if necessary
357 // -1 if no edge is attached to the specified corner
359 int MapDoc::findEdge(RoomNum n, RoomCorner corner, MapEdge & newEdge) const
361 EdgeConstItr e = edge.begin();
363 if (corner < rcNumCorners)
365 for(int i=edge.size()-1; i>=0; i--)
367 if ((e[i].room1 == n) && (e[i].end1 == corner))
369 newEdge = e[i].Swapped();
372 else if ((e[i].room2 == n) && (e[i].end2 == corner)
373 && !(e[i].type1 & etNoRoom2))
382 // Direction (up/down/in/out)
383 const EdgeType corner2et[] = { etUp, etDown, etIn, etOut };
384 const EdgeType dir = corner2et[corner - rcNumCorners];
386 for(int i=edge.size()-1; i>=0; i--)
388 if ((e[i].room1 == n) && ((e[i].type1 & etDirection) == dir))
390 newEdge = e[i].Swapped();
393 else if ((e[i].room2 == n) && ((e[i].type2 & etDirection) == dir)
394 && !(e[i].type1 & etNoRoom2))
407 // Find the other end of a connection with corners:
411 // The edge we want to follow
414 // cornerRooms: (if not NULL)
415 // Contains the room numbers of all corners in this connection.
416 // If the vector was not empty, the new entries are appended.
419 // The number of the room at the other end
420 // -1 if the connection doesn't lead anywhere
422 int MapDoc::findOtherEnd(EdgeConstItr start, RoomNumVec * cornerRooms) const
424 EdgeConstItr oldEdge = start;
425 RoomNum room = (start->end1 == rcCorner ? start->room1 : start->room2);
428 cornerRooms->push_back(room);
432 EdgeConstItr lastTime = oldEdge;
434 for(EdgeConstItr e=edge.begin(); e<edge.end(); e++)
439 if (e->room1 == room)
443 if (e->end2 != rcCorner)
447 cornerRooms->push_back(room);
451 else if (e->room2 == room)
455 if (e->end1 != rcCorner)
459 cornerRooms->push_back(room);
465 if (lastTime == oldEdge)
466 return -1; // We didn't go anywhere
471 int MapDoc::findOtherEnd(int n) const
473 return findOtherEnd(edge.begin() + n);
477 void MapDoc::getEdgePoints(const MapEdge & e, QPoint & p1, QPoint & p2) const
479 const QPoint cornerOffset[] = {
480 { roomWidth / 2, 0 }, { roomWidth / 2, roomHeight },
481 { roomWidth, roomHeight / 2 }, { 0, roomHeight / 2 },
482 { roomWidth, 0 }, { 0, 0 },
483 { roomWidth, roomHeight }, { 0, roomHeight },
484 { 3 * roomWidth / 4, 0 }, { roomWidth / 4, 0 },
485 { 3 * roomWidth / 4, roomHeight }, { roomWidth / 4, roomHeight },
486 { gridX / 2, gridY / 2 }
489 /* const QPoint nowhereOffset[] = {
490 { 0, -gridY / 2 }, { 0, gridY / 2 }, { gridX / 2, 0 }, { -gridX / 2, 0 }, // N, S, E, W
491 { gridX / 2, -gridY / 2 }, { -gridX / 2, -gridY / 2 }, { gridX / 2, gridY / 2 }, { -gridX / 2, gridY / 2 }, // NE, NW, SE, SW
492 { 0, -gridY / 2 }, { 0, -gridY / 2 }, { 0, gridY / 2 }, { 0, gridY / 2 } // NNE, NNW, SSE, SSW
494 const int stub = (int)((gridY / 2.0) - 15.0);
495 const int stubd = (int)(((gridY / 2.0) - 15.0) * 0.7071);
496 const QPoint nowhereOffset[] = {
497 { 0, -stub }, { 0, stub }, { stub, 0 }, { -stub, 0 }, // N, S, E, W
498 { stubd, -stubd }, { -stubd, -stubd }, { stubd, stubd }, { -stubd, stubd }, // NE, NW, SE, SW
499 { 0, -stub }, { 0, -stub }, { 0, stub }, { 0, stub } // NNE, NNW, SSE, SSW
502 p1 = room[e.room1].pos + cornerOffset[e.end1];
504 if (e.type1 & etNoRoom2)
506 if (e.type1 & etNoExit)
507 p2 = p1 + nowhereOffset[e.end1];
509 p2 = p1 + (nowhereOffset[e.end1] * 3);
512 p2 = room[e.room2].pos + cornerOffset[e.end2];
516 void MapDoc::getEdgePoints(int n, QPoint & p1, QPoint & p2) const
518 getEdgePoints(edge[n], p1, p2);
522 void MapDoc::getEdgeRect(const MapEdge & e, QRect & r) const
524 // getEdgePoints(e, r.left, r.top, r.right, r.bottom);
525 // r.NormalizeRect();
526 // r.InflateRect(gridX, gridY);
528 getEdgePoints(e, p1, p2);
529 r = QRect(p1, p2).normalized();
530 r += QMargins(gridX, gridY, gridX, gridY);
535 // Compute the actual size of the map:
538 // r: The rectangle enclosing the map in logical coordinates
540 void MapDoc::getGridRect(QRect & rect)
542 // rect.SetRectEmpty();
545 for(RoomConstItr r=room.getVector().begin(); r!=room.getVector().end(); r++)
546 rect |= (*r)->getRect();
548 for(PageConstItr p=page.begin(); p!=page.end(); p++)
549 rect |= getPageRect(*p);
554 // Compute the actual size of the map:
557 // size: The size of the map in grid units
558 // Does not consider stubs or labels that may extend off the edge
560 void MapDoc::getGridSize(QSize & size)
565 size.setWidth(rect.right() / gridX);
566 size.setHeight(rect.top() / -gridY);
571 // Add a page to the map:
574 // n: The new page number (0 based)
575 // newPage: The page information
577 void MapDoc::addPage(int n, const MapPage & newPage)
579 ASSERT(n >= 0 && n <= page.size());
581 if (page.size() == maxRooms)
584 page.insert(page.begin() + n, newPage);
586 // UpdateAllViews(NULL, dupPageCount, NULL);
591 // Append pages to the map:
594 // newPages: The page information
596 void MapDoc::addPages(const PageVec & newPages)
598 if (page.size() + newPages.size() > maxRooms)
601 page.insert(page.end(), newPages.begin(), newPages.end());
603 // UpdateAllViews(NULL, dupPageCount, NULL);
607 void MapDoc::addPages(int n)
609 page.reserve(page.size() + n);
614 // Delete a page from the map:
617 // n: The (0 based) page number to delete
619 void MapDoc::deletePage(int n)
621 ASSERT(n >= 0 && n < page.size());
622 ASSERT(page.size() > 1);
625 QRect rect = getPageRect(n);
627 page.erase(page.begin() + n);
629 // UpdateAllViews(NULL, dupPageCount, NULL);
633 QRect MapDoc::getPageRect(int p)
635 return QRect(page[p].pos, pageSize);
639 QRect MapDoc::getPageRect(const MapPage & p)
641 return QRect(p.pos, pageSize);
646 // Place pages to cover the entire map:
648 void MapDoc::layoutPages()
650 // Adds nothing for now, so nuke it
652 UndoPaginate * undoRec = new UndoPaginate(*this);
655 getGridRect(r); // Just counts rooms, since UndoPaginate cleared the pages
660 numX = (width + pageSize.width() - 1) / pageSize.width(),
661 numY = (height + pageSize.height() - 1) / pageSize.height();
664 page.insert(page.begin()); // One page at 0,0
670 page.resize(numX * numY);
674 i = r.left - (pageSize.width() - width) / 2;
680 for(p=page.begin(); p!=page.end(); ++p)
685 step = float(width - pageSize.width()) / float(numX);
688 for(p=page.begin(), p2=p+numX; p!=p2; ++p, delta+=step)
690 p->pos.x = r.left + delta;
691 p->pos.x -= p->pos.x % gridX;
694 p[-1].pos.x = r.right - pageSize.width();
696 for(p2=page.begin(); p!=page.end(); ++p, ++p2)
697 p->pos.x = p2->pos.x;
702 i = r.bottom + (pageSize.height() - height)/2;
708 for(p=page.begin(); p!=page.end(); ++p)
713 step = float(pageSize.height() - height) / float(numY);
715 p = p2 = page.begin();
717 while (p2 != page.end())
721 if (p2 == page.end())
722 i = r.top + pageSize.height();
725 i = r.bottom + delta;
736 if (undoRec->getPages() == page)
737 delete undoRec; // No changes
740 setUndoData(undoRec);
742 // UpdateAllViews(NULL, dupPageCount, NULL);
752 // n: The (0 based) page number to move
753 // offset: How much to move it
756 // Must not change undo data (may be used during undo)
758 void MapDoc::movePage(int n, const QSize & offset)
762 // page[n].pos += offset;
763 page[n].pos += QPoint(offset.width(), offset.height());
764 // UpdateAllViews(NULL);
769 // Decide if we need to repaginate:
772 // true: Some rooms are not (completely) on any page
773 // false: All rooms are on a page
775 bool MapDoc::needRepaginate()// const
777 const PageConstItr pBegin = page.begin(), pEnd = page.end();
780 for(RoomConstItr r=room.getVector().begin(); r!=room.getVector().end(); r++)
782 QRect rr = (**r).getRect();
784 for(PageConstItr p=pBegin; p!=pEnd; p++)
786 QRect pr = getPageRect(*p);
789 if (!i.isNull() && (i == rr))
790 // if (i.IntersectRect(&pr, &rr) && (i == rr))
794 return true; // This room not on any page
805 // Determine which page (if any) contains a given point:
807 // The point must be in the page border (one of the grid cells which
808 // displays the page number).
811 // pos: The position to test
814 // The (0 based) page number of the page containing the point
815 // -1 if no page contains the point
817 int MapDoc::pageBorderHit(const QPoint & pos)
819 for(int i=page.size()-1; i>=0; i--)
821 QRect r = getPageRect(i);
823 if (r.contains(pos) &&
824 // if (r.PtInRect(pos) &&
825 (((pos.x() - r.left() < gridX) || (r.right() - pos.x() < gridX)) !=
826 ((pos.y() - r.top() < gridY) || (r.bottom() - pos.y() < gridY))))
827 // Point is in the X border or the Y border, but not both
831 // Not in any page border
840 // The room number of the new room (-1 if error)
842 int MapDoc::addRoom(const QPoint & pos)
844 if (room.size() == maxRooms)
847 MapRoom * newRoom = new MapRoom();
848 newRoom->flags = rfBorder;
850 // newRoom->computeNameLength();
851 addRoom(room.size(), newRoom);
853 return room.size() - 1;
861 // n: The position for the new room [0..size]
862 // newRoom: A MapRoom object allocated by new
864 void MapDoc::addRoom(int n, MapRoom * newRoom)
866 ASSERT(n >= 0 && n <= room.size());
869 room.insert(n, newRoom);
871 if (n != room.size()-1)
873 for(int i=edge.size()-1; i>=0; i--)
875 if (edge[i].room1 >= n)
878 if (edge[i].room2 >= n)
884 QRect rect = newRoom->getRect();
885 // UpdateAllViews(NULL, 0, reinterpret_cast<CObject *>(&rect));
889 void MapDoc::addRooms(int n)
891 room.reserve(room.size() + n);
896 // Extract a room from the map:
899 // n: The room number to remove
902 // A pointer to the room extracted from the map
903 // You can use this in the undo record
905 MapRoom * MapDoc::extractRoom(int n)
907 ASSERT(n >= 0 && n < room.size());
911 for(int i=edge.size()-1; i>=0; i--)
913 if ((edge[i].room1 == n) ||
914 (!(edge[i].type1 & etNoRoom2) && (edge[i].room2 == n)))
918 if (edge[i].room1 > n)
921 if (edge[i].room2 > n)
926 MapRoom * oldRoom = room.extract(n);
927 QRect rect = oldRoom->getRect();
929 // UpdateAllViews(NULL, dupDeletedRoom, reinterpret_cast<CObject *>(&rect));
935 void MapDoc::deleteRoom(int n)
937 delete extractRoom(n);
942 // Find out if a new room would intersect an old one:
944 // There must be at least one grid of space around the room. Ignores corners.
947 // pos: The proposed location for a new room
950 // The room number of an intersecting room
951 // -1 if the space is unoccupied
952 // -2 if the space is off the grid
954 int MapDoc::findRoom(const QPoint & pos) const
956 if (pos.x() < 0 || pos.x() + roomWidth > docSize.width()
957 || pos.y() < 0 || pos.y() + roomHeight > docSize.height())
960 RoomConstItr rm = room.getVector().begin();
961 QRect newRoom(pos, defaultRoomSize);
962 // newRoom.InflateRect(gridX - 1, gridY - 1);
963 newRoom += QMargins(gridX - 1, gridY - 1, gridX - 1, gridY - 1);
965 for(int i=room.size()-1; i>=0; i--)
967 if (!rm[i]->isCorner()
968 && newRoom.intersects(rm[i]->getRect()))
980 // n: The room number to move
981 // offset: How much to move it
984 // Must not change undo data (may be used during undo)
986 void MapDoc::moveRoom(int n, const QSize & offset)
990 // room[n].pos += offset;
991 room[n].pos += QPoint(offset.width(), offset.height());
992 // UpdateAllViews(NULL);
997 // Determine which room (if any) contains a given point:
1000 // pos: The position to test
1004 // Which corner of the room was hit (rcNone if in center)
1007 // The room number of the room containing the point
1008 // -1 if no room contains the point
1010 int MapDoc::roomHit(const QPoint & pos, RoomCorner * corner/*= NULL*/)
1015 for(int i=room.size()-1; i>=0; i--)
1017 QRect r = room[i].getRect();
1019 if (r.contains(pos) == false)
1022 if (corner && (room[i].isCorner() == false))
1023 *corner = CornerHit(pos - r.topLeft());
1033 RoomCorner MapDoc::CornerHit(const QPoint & pos)
1035 long x = pos.x(), y = pos.y();
1037 // FIXME this assumes all rooms are the same size
1038 if (y < roomHeight / 4)
1040 if (x < roomWidth * 3 / 16)
1042 else if (x < roomWidth * 5 / 16)
1044 else if (x > roomWidth * 13 / 16)
1046 else if (x > roomWidth * 11 / 16)
1051 else if (y > roomHeight * 3 / 4)
1053 if (x < roomWidth * 3 / 16)
1055 else if (x < roomWidth * 5 / 16)
1057 else if (x > roomWidth * 13 / 16)
1059 else if (x > roomWidth * 11 / 16)
1066 if (x < roomWidth / 4)
1068 else if (x > roomWidth * 3 / 4)
1077 // Change the map's title:
1080 // newName: The new map title
1082 void MapDoc::setName(const char * newName)
1084 if (name == newName)
1085 return; // Nothing to do
1093 // Change the map's comments:
1096 // newNote: The new map comments
1098 void MapDoc::setNote(const char * newNote)
1100 if (note == newNote)
1101 return; // Nothing to do
1110 // Change a room's flags:
1113 // n: The room number to change
1114 // set: The flags to set
1115 // clear: The flags to clear (default 0)
1118 // flags in both SET and CLEAR will be set.
1120 void MapDoc::setRoomFlags(int n, RoomFlags set, RoomFlags clear)
1122 MapRoom & rm = room[n];
1123 RoomFlags old = rm.flags;
1128 if (rm.flags != old)
1132 // rm.getRect(rect).InflateRect(5, 5);
1133 QRect rect = rm.getRect() += QMargins(5, 5, 5, 5);
1134 // UpdateAllViews(NULL, 0, reinterpret_cast<CObject *>(&rect));
1140 // Change a room's name
1143 // n: The room number to change
1144 // newName: The new room name
1146 void MapDoc::setRoomName(int n, const char * newName)
1148 MapRoom & rm = room[n];
1150 if (rm.name == newName)
1151 return; // Nothing to do
1156 // rm.getRect(rect).DeflateRect(5, 5);
1157 QRect rect = rm.getRect() -= QMargins(5, 5, 5, 5);
1158 // UpdateAllViews(NULL, 0, reinterpret_cast<CObject *>(&rect));
1163 // Change a room's comments
1166 // n: The room number to change
1167 // newNote: The new room note
1169 void MapDoc::setRoomNote(int n, const char * newNote)
1171 MapRoom & rm = room[n];
1173 if (rm.note == newNote)
1174 return; // Nothing to do
1176 const bool redraw = (rm.note.empty() || !*newNote);
1183 // rm.getRect(rect).DeflateRect(5, 5); // PORT
1184 QRect rect = rm.getRect() -= QMargins(5, 5, 5, 5);
1185 // UpdateAllViews(NULL, dupRoomComment, reinterpret_cast<CObject *>(&rect));
1188 // UpdateAllViews(NULL, dupRoomComment, NULL);
1194 //--------------------------------------------------------------------
1197 void MapDoc::setUndoData(UndoRec * newUndo)
1199 if (undoData != newUndo)
1209 // Undo the last operation:
1211 void MapDoc::OnEditUndo()
1214 setUndoData(undoData->undo(*this));
1219 // Update the Undo menu item:
1221 void MapDoc::OnUpdateEditUndo(CCmdUI * pCmdUI)
1223 pCmdUI->Enable(undoData && !locked);
1230 text += undoData->getName();
1233 text = "Can't &Undo";
1236 pCmdUI->SetText(text);
1244 // Erase contents of document:
1246 void MapDoc::DeleteContents(void)
1249 edge.erase(edge.begin(), edge.end());
1251 page.front().pos.setX(0);
1252 page.front().pos.setY(0);
1256 docSize = QSize(271 * gridX, 451 * gridY);
1264 // Move all the rooms around:
1266 // This moves only rooms that have connections. Rooms that stand
1267 // alone (used as a title box, for example) are not moved.
1269 void MapDoc::OnEditMoveMap(UINT cmd)
1271 const int numEdges = edge.size();
1272 const int numRooms = room.size();
1275 selected.resize(numRooms, false);
1279 // Select all rooms that have a connection to somewhere:
1280 for(i=0; i<numEdges; i++)
1282 selected[edge[i].room1] = true;
1284 if ((edge[i].type1 & etNoRoom2) == 0)
1285 selected[edge[i].room2] = true;
1288 // Compute the bounding rectangle of the selected rooms:
1289 int numSelected = 0;
1294 for(i=0; i<numRooms; i++)
1299 r |= room[i].getRect(temp);
1303 // Calculate the distance to move them:
1308 case ID_EDIT_MOVE_CENTER:
1309 offset.width() = (docSize.width() - r.right - r.left) / 2;
1310 offset.height() = (docSize.height() + r.top + r.bottom) / -2;
1312 case ID_EDIT_MOVE_TOP: offset.height() = -r.bottom; break;
1313 case ID_EDIT_MOVE_BOTTOM: offset.height() = -(docSize.height() + r.top); break;
1314 case ID_EDIT_MOVE_LEFT: offset.width() = -r.left; break;
1315 case ID_EDIT_MOVE_RIGHT: offset.width() = docSize.width() - r.right; break;
1318 offset.width() -= offset.width() % gridX; // Make sure the rooms stay on the grid
1319 offset.height() -= offset.height() % gridY;
1322 if (!offset.width() && !offset.height())
1323 return; // Do nothing
1325 setUndoData(new UndoMove(isDirty, offset, numSelected, selected, 0, selected)); // No selected pages
1327 for(i=0; i<numRooms; i++)
1330 moveRoom(i, offset);
1335 void MapDoc::OnNavigationMode()
1341 if (getRoomCount() > 0)
1342 UpdateAllViews(NULL, dupNavigationMode);
1344 locked = false; // Can't enter navigation mode
1349 void MapDoc::OnUpdateEditMoveMap(CCmdUI * pCmdUI)
1351 // You can't use Move Map unless you have edges:
1352 pCmdUI->Enable(!locked && edge.size());
1356 void MapDoc::OnUpdateNavigationMode(CCmdUI * pCmdUI)
1358 pCmdUI->SetCheck(locked);
1361 pCmdUI->Enable(getRoomCount() > 0);