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 // mapview.cpp: implementation of the CMapView class
16 #include "mapdialog.h"
18 #include "mathconstants.h"
19 //#include "properties.h"
20 #include "roomdialog.h"
42 static const DirectionName directions[] = {
43 { rcN, "n" }, { rcS, "s" }, { rcE, "e" }, { rcW, "w" },
44 { rcNE, "ne" }, { rcNW, "nw" }, { rcSE, "se" }, { rcSW, "sw" },
45 { rcNNE, "nne" }, { rcNNW, "nnw" }, { rcSSE, "sse" }, { rcSSW, "ssw" },
46 { rcUp, "u" }, { rcDown, "d" }, { rcIn, "i" }, { rcOut, "o" },
47 { rcN, "north" }, { rcS, "south" }, { rcE, "east" }, { rcW, "west" },
48 { rcNE, "northeast" }, { rcNW, "northwest" },
49 { rcSE, "southeast" }, { rcSW, "southwest" },
50 { rcUp, "up" }, { rcDown, "down" }, { rcIn, "in" }, { rcOut, "out" },
54 static const char a2z[] = "abcdefghijklmnopqrstuvwxyz";
58 // Parse a direction word:
61 // text: The string to be tested (must be in lowercase, may be empty)
64 // text: Has the direction word and whitespace stripped from the beginning
67 // The direction indicated by the text
68 // rcNone if not a direction
70 RoomCorner parseDirection(string & text)
73 const CString word(text.SpanIncluding(a2z));
77 for(int i=0; directions[i].dir != rcNone; i++)
79 if (word == directions[i].name)
81 text = text.Mid(word.GetLength());
83 return directions[i].dir;
94 /////////////////////////////////////////////////////////////////////////////
95 // CRepaginateDlg dialog
97 class CRepaginateDlg : public CDialog
101 CRepaginateDlg(); // standard constructor
104 //{{AFX_DATA(CRepaginateDlg)
105 enum { IDD = IDD_REPAGINATE };
109 // ClassWizard generated virtual function overrides
110 //{{AFX_VIRTUAL(CRepaginateDlg)
115 // Generated message map functions
116 //{{AFX_MSG(CRepaginateDlg)
117 afx_msg void OnYes();
118 virtual bool OnInitDialog();
121 DECLARE_MESSAGE_MAP();
127 /////////////////////////////////////////////////////////////////////////////
128 /////////////////////////////////////////////////////////////////////////////
132 IMPLEMENT_DYNCREATE(CMapView, CScrollZoomView)
134 BEGIN_MESSAGE_MAP(CMapView, CScrollZoomView)
135 //{{AFX_MSG_MAP(CMapView)
136 ON_COMMAND(ID_EDIT_ADD_CORNER, OnEditAddCorner)
137 ON_COMMAND(ID_EDIT_ADD_PAGE, OnEditAddPage)
138 ON_COMMAND(ID_EDIT_ADD_ROOM, OnEditAddRoom)
139 ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
140 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
141 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
142 ON_COMMAND(ID_EDIT_PAGINATE, OnEditPaginate)
143 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
144 ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
145 ON_COMMAND(ID_EDIT_SELECT_CONNECTED, OnEditSelectConnected)
146 ON_COMMAND(ID_VIEW_GRID, OnViewGrid)
147 ON_COMMAND(ID_WINDOW_REFRESH, OnWindowRefresh)
148 ON_UPDATE_COMMAND_UI(ID_EDIT_ADD_CORNER, OnUpdateEditAddCorner)
149 ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateSelectedUnlocked)
150 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateSelectedUnlocked)
151 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateSelectedUnlocked)
152 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
153 ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateUnlocked)
154 ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_CONNECTED, OnUpdateSelectedUnlocked)
155 ON_UPDATE_COMMAND_UI(ID_VIEW_GRID, OnUpdateViewGrid)
158 ON_WM_LBUTTONDBLCLK()
168 ON_WM_MOUSEACTIVATE()
169 ON_BN_CLICKED(IDC_NAV_GO, OnNavGo)
170 ON_COMMAND_RANGE(ID_EDIT_PROPERTIES, ID_EDIT_MAP_PROPERTIES, OnEditProperties)
171 ON_COMMAND_RANGE(ID_NAV_GO_NORTH, ID_NAV_STUB2_OUT, OnNavGoDir)
172 ON_COMMAND_RANGE(ID_VIEW_ZOOM_IN, ID_VIEW_ZOOM_OUT, OnViewZoom)
173 ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_ZOOM_IN, ID_VIEW_ZOOM_OUT, OnUpdateViewZoom)
174 // Standard printing commands
175 ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
176 ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
177 ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
181 //===========================================================================
185 // opInProgress: Indicates what's happening while the mouse button is down
187 // iTmp: The edge that needs a corner
189 // bTmp: The modification state of the document
190 // edgeTmp: The edge being created
191 // e2Tmp: The original edge data (if iTmp >= 0)
192 // iTmp: -1 if this is a new edge, >=0 if changing an old edge
193 // p1Tmp: The start point (fixed) of the rubber band cursor
194 // p2Tmp: The end point (moving) of the rubber band cursor
196 // bTmp: The modification state of the document
197 // edgeTmp: Info about the other end of this edge
198 // iTmp: The edge number about to be changed
199 // p1Tmp, p2Tmp: The start point (fixed) of the rubber band cursor
201 // Used in OnEditClear to tell OnUpdate not to clear the selection
203 // p1Tmp: The fixed corner of the selection rectangle
204 // p2Tmp: The moving corner of the selection rectangle
205 // rTmp: The current selection rectangle
207 // bTmp: TRUE if the room or page was already selected
208 // b2Tmp: True if this is a page instead of a room
209 // iTmp: The room number where the button was pressed (if bTmp==TRUE)
210 // p1Tmp: The grid cell where the mouse button was pressed
211 // p2Tmp: The offset to the current grid cell (in logical coordinates)
213 // iTmp: The number of the selected page (-1 if a room)
214 // p1Tmp: The grid cell where the mouse button was pressed
215 // p2Tmp: The position of the selected room
216 // gmoDrag: (all dragging operations)
218 // The offset to the current grid cell (in logical coordinates)
220 // A rectangle enclosing the original position of all rooms being
221 // dragged. Used to ensure we don't push a room off the edge.
223 // The font used for room names
225 // The number of rooms currently selected
227 // The number of pages currently selected
229 // The number of the page being printed or previewed
230 // 0 means normal screen display
232 // True if we are in the middle of a scroll-by-drag operation
234 // The point under the cursor when a scrollDrag started
236 // The selected room's number, if exactly 1 room is selected
239 // selected[N] is non-zero iff room N is selected
240 // 2 means the room should stay selected during gmoSelectBox
242 // selectedPage[N] is non-zero iff page N is selected
243 // 2 means the page should stay selected during gmoSelectBox
245 // Zoom factor in percent
249 // CMapView construction/destruction
251 MapView::MapView(QWidget * parent/*= NULL*/): QWidget(parent),
252 opInProgress(gmoNone), hoveredEdge(-1), printingPage(0), scrollDrag(false),
253 scrollDragTimer(0), font("Arial", 24), showCorners(false), showGrid(true),
254 // showPages(false), zoom(100), shiftDown(false), ctrlDown(false),
255 showPages(false), zoom(20.0), shiftDown(false), ctrlDown(false),
256 altDown(false), mouseDown(false), offset(0, 0)
260 selectedPage.resize(doc->page.size(), 0);
261 clearSelection(false);
263 setFocusPolicy(Qt::StrongFocus);
264 // Make sure we get *ALL* mouse move events!
265 setMouseTracking(true);
267 // Set the document size:
268 //scrollzoom.cpp -> init(4 * gridX, doc->getDocSize(), QSize(gridX, gridY));
269 // CClientDC dc(this);
272 // memset(&lf, 0, sizeof(lf));
274 // strcpy(lf.lfFaceName, "Arial");
275 // font.CreateFontIndirect(&lf);
278 // OnUpdate(NULL, dupNavigationMode, NULL);
280 // CScrollZoomView::OnInitialUpdate();
281 // setScrollBars(); // Now fix the scroll bars
284 deleteRoomAct = CreateAction("Delete", "Delete room", "Deletes selected rooms", QIcon(), QKeySequence(), false, this);
285 connect(deleteRoomAct, SIGNAL(triggered()), this, SLOT(HandleDelete()));
287 roomPropertiesAct = CreateAction("Properties...", "Properties", "Opens the properties dialog for this room", QIcon(), QKeySequence(), false, this);
288 connect(roomPropertiesAct, SIGNAL(triggered()), this, SLOT(HandleRoomProperties()));
290 mapPropertiesAct = CreateAction("Properties...", "Properties", "Opens the properties dialog for this map", QIcon(), QKeySequence(), false, this);
291 connect(mapPropertiesAct, SIGNAL(triggered()), this, SLOT(HandleMapProperties()));
293 selectConnectedAct = CreateAction("Select connected rooms", "Select connected", "Selects connected rooms", QIcon(), QKeySequence(), false, this);
295 addCornerAct = CreateAction("Add corner", "Add corner", "Adds a corner to the selected room", QIcon(), QKeySequence(), false, this);
296 connect(addCornerAct, SIGNAL(triggered()), this, SLOT(HandleAddCorner()));
298 addRoomAct = CreateAction("Add room", "Add room", "Adds a rom to the map", QIcon(), QKeySequence(), false, this);
299 connect(addRoomAct, SIGNAL(triggered()), this, SLOT(HandleAddRoom()));
301 addPageAct = CreateAction("Add Page", "Add Page", "Adds a page to the map", QIcon(), QKeySequence(), false, this);
303 addUnexploredAct = CreateAction("Unexplored", "Unexplored", "Adds an unexplored notation to the selected corner", QIcon(), QKeySequence(), false, this);
304 connect(addUnexploredAct, SIGNAL(triggered()), this, SLOT(HandleAddUnexplored()));
306 addLoopBackAct = CreateAction("Loop back", "Loop back", "Adds a loop back notation to the selected corner", QIcon(), QKeySequence(), false, this);
307 connect(addLoopBackAct, SIGNAL(triggered()), this, SLOT(HandleAddLoopBack()));
309 addNoExitAct = CreateAction("No Exit", "No Exit", "Adds a no exit notation to the selected corner", QIcon(), QKeySequence(), false, this);
310 connect(addNoExitAct, SIGNAL(triggered()), this, SLOT(HandleAddNoExit()));
312 addUpAct = CreateAction("Up", "Up", "Marks the selected corner as Up", QIcon(), QKeySequence(), false, this);
313 connect(addUpAct, SIGNAL(triggered()), this, SLOT(HandleAddUp()));
315 addDownAct = CreateAction("Down", "Down", "Marks the selected corner as Down", QIcon(), QKeySequence(), false, this);
316 connect(addDownAct, SIGNAL(triggered()), this, SLOT(HandleAddDown()));
318 addInAct = CreateAction("In", "In", "Marks the selected corner as In", QIcon(), QKeySequence(), false, this);
319 connect(addInAct, SIGNAL(triggered()), this, SLOT(HandleAddIn()));
321 addOutAct = CreateAction("Out", "Out", "Marks the selected corner as Out", QIcon(), QKeySequence(), false, this);
322 connect(addOutAct, SIGNAL(triggered()), this, SLOT(HandleAddOut()));
324 addOneWayAct = CreateAction("One Way", "One Way", "Marks the selected corner as one way exit", QIcon(), QKeySequence(), false, this);
325 connect(addOneWayAct, SIGNAL(triggered()), this, SLOT(HandleAddOneWay()));
327 clearOneWayAct = CreateAction("Two Way", "Two Way", "Marks the selected corner as traversable both ways", QIcon(), QKeySequence(), false, this);
328 connect(clearOneWayAct, SIGNAL(triggered()), this, SLOT(HandleClearOneWay()));
330 addRestrictedAct = CreateAction("Restricted", "Restricted", "Marks the selected corner as a restricted exit", QIcon(), QKeySequence(), false, this);
331 connect(addRestrictedAct, SIGNAL(triggered()), this, SLOT(HandleAddRestricted()));
333 clearRestrictedAct = CreateAction("Unrestricted", "Unrestricted", "Marks the selected corner as an unrestricted exit", QIcon(), QKeySequence(), false, this);
334 connect(clearRestrictedAct, SIGNAL(triggered()), this, SLOT(HandleClearRestricted()));
337 roomContextMenu = new QMenu(this);
338 mapContextMenu = new QMenu(this);
340 roomContextMenu->addAction(deleteRoomAct);
341 roomContextMenu->addAction(roomPropertiesAct);
342 roomContextMenu->addAction(selectConnectedAct);
343 roomContextMenu->addAction(addCornerAct);
344 QMenu * sub = roomContextMenu->addMenu(tr("Edge"));
345 sub->addAction(addLoopBackAct);
346 sub->addAction(addUnexploredAct);
347 sub->addAction(addNoExitAct);
348 sub->addAction(addUpAct);
349 sub->addAction(addDownAct);
350 sub->addAction(addInAct);
351 sub->addAction(addOutAct);
352 sub->addAction(addOneWayAct);
353 sub->addAction(clearOneWayAct);
354 sub->addAction(addRestrictedAct);
355 sub->addAction(clearRestrictedAct);
357 mapContextMenu->addAction(mapPropertiesAct);
358 mapContextMenu->addAction(addRoomAct);
359 mapContextMenu->addAction(addPageAct);
360 mapContextMenu->addAction(deleteRoomAct);
367 // gueApp()->closingView(this); // Remove any comment for this view
371 void MapView::DrawArrowhead(QPainter * painter, QPointF head, QPointF tail)
375 const double angle = Angle(tail - head);
376 const double orthoAngle = angle + QTR_TAU;
377 const double size = 5.0;
379 QPointF ortho(cos(orthoAngle), sin(orthoAngle));
380 QPointF unit = UnitVector(head - tail);
382 QPointF p1 = head - (unit * 9.0 * size);
383 QPointF p2 = p1 + (ortho * 3.0 * size);
384 QPointF p3 = p1 - (ortho * 3.0 * size);
386 arrow << head << p2 << p3;
387 painter->drawPolygon(arrow);
391 void MapView::DrawNoExit(QPainter * painter, QPointF head, QPointF tail)
393 const double angle = Angle(tail - head);
394 const double orthoAngle = angle + QTR_TAU;
395 const double size = 5.0;
397 QPointF ortho(cos(orthoAngle), sin(orthoAngle));
398 QPointF unit = UnitVector(head - tail);
400 QPointF p2 = head + (ortho * 3.0 * size);
401 QPointF p3 = head - (ortho * 3.0 * size);
403 painter->drawLine(p2, p3);
410 void MapView::paintEvent(QPaintEvent * /*event*/)
412 const char edgeLabel[] = "UDIO";
414 const QPoint edgLblOffset[] = {
416 { roomWidth / 2 + 24, -13 }, { roomWidth / 2 - 48, roomHeight + 52 },
417 { roomWidth + 24, roomHeight / 2 + 57 }, { -48, roomHeight / 2 - 19 },
419 { roomWidth - 48, -13 }, { 24, -13 },
420 { roomWidth - 48, roomHeight + 52 }, { 24, roomHeight + 52 },
421 // NNE, NNW, SSE, SSW
422 { 3 * roomWidth / 4 + 24, -13 }, { roomWidth / 4 + 24, -13 },
423 { 3 * roomWidth / 4 - 48, roomHeight + 52 },
424 { roomWidth / 4 - 48, roomHeight + 52 }
427 // Offsets used when the label collides with the line...
428 const QPoint edgLblOffsetEx[] = {
430 { -48, 0 }, { 48, 0 }, { 0, -76 }, { 0, 76 },
432 { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
433 // NNE, NNW, SSE, SSW
434 { -48, 0 }, { -48, 0 }, { 48, 0 }, { 48, 0 }
439 cantGoAngleX = gridY / 6,
440 cantGoAngleY = gridX / 6;
443 rcNE, rcNW, rcSE, rcSW,
444 rcNNE, rcNNW, rcSSE, rcSSW,
446 QRect cornerRect[] = {
447 { roomWidth * 5 / 16, 0, roomWidth * 6 / 16, roomHeight / 4 },
448 { roomWidth * 5 / 16, roomHeight * 3 / 4, roomWidth * 6 / 16, roomHeight / 4 },
449 { roomWidth * 3 / 4, roomHeight / 4, roomWidth / 4, roomHeight / 2 },
450 { 0, roomHeight / 4, roomWidth / 4, roomHeight / 2 },
451 { roomWidth * 13 / 16, 0, roomWidth * 3 / 16, roomHeight / 4 },
452 { 0, 0, roomWidth * 3 / 16, roomHeight / 4 },
453 { roomWidth * 13 / 16, roomHeight * 3 / 4, roomWidth * 3 / 16, roomHeight / 4 },
454 { 0, roomHeight * 3 / 4, roomWidth * 3 / 16, roomHeight / 4 },
455 { roomWidth * 11 / 16, 0, roomWidth * 2 / 16, roomHeight / 4 },
456 { roomWidth * 3 / 16, 0, roomWidth * 2 / 16, roomHeight / 4 },
457 { roomWidth * 11 / 16, roomHeight * 3 / 4, roomWidth * 2 / 16, roomHeight / 4 },
458 { roomWidth * 3 / 16, roomHeight * 3 / 4, roomWidth * 2 / 16, roomHeight / 4 },
461 QPainter painter(this);
462 QPainter * dc = &painter;
464 painter.setRenderHint(QPainter::Antialiasing);
469 dc->setPen(QColor(0xFF, 0x7F, 0x00));
470 // dc->drawText(20, 50, QString("<%1, %2>").arg(mouse.x()).arg(mouse.y()));
471 dc->drawText(20, 50, QString("<%1, %2>, %3").arg(mouse.x()).arg(mouse.y()).arg(hoveredEdge));
476 t1.scale(zoom / 100.0, zoom / 100.0);
477 // t1.scale(+0.20, +0.20);
478 // t1.scale(+0.40, +0.40);
479 t1.translate(offset.x(), offset.y());
480 painter.setTransform(t1);
482 // QPainter painter(this);
484 painter.setPen(QPen(Qt::blue, 1, Qt::DashLine));
485 painter.drawRect(0, 0, 100, 100);
487 QTransform transform;
488 transform.translate(50, 50);
489 transform.rotate(45);
490 transform.scale(0.5, -1.0);
491 painter.setTransform(transform);
493 painter.setFont(QFont("Helvetica", 24));
494 painter.setPen(QPen(Qt::black, 1));
495 painter.drawText(20, 10, "QTransform");
505 lb.lbStyle = BS_SOLID;
506 lb.lbColor = RGB(0, 0, 0);
508 if (!focusBrush.CreateHatchBrush(HS_BDIAGONAL, PALETTERGB(128, 128, 128))
509 || !penEdge.CreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT, penEdgeWidth, &lb)
510 || !penGrid.CreatePen(PS_DOT, 0, RGB(0, 0, 0))
511 || !penRoom.CreatePen(PS_SOLID, penRoomWidth, RGB(0, 0, 0)))
514 QBrush * const whiteBrush = dc->GetCurrentBrush();
515 QPen * const oldPen = dc->GetCurrentPen();
516 QFont * const oldFont = dc->SelectObject(&font);
517 const unsigned int oldAlign = dc->SetTextAlign(TA_CENTER | TA_BASELINE | TA_NOUPDATECP);
519 QBrush focusBrush(QColor(0xC0, 0xC0, 0xC0), Qt::BDiagPattern);
520 QPen penEdge(QColor(0, 0, 0), penEdgeWidth);
521 QPen penEdgeDashed(QColor(0, 0, 0), penEdgeWidth, Qt::DashLine);
522 QPen penEdgeHover(QColor(0xFF, 0, 0), penEdgeWidth);
523 QPen penEdgeDashedHover(QColor(0xFF, 0, 0), penEdgeWidth, Qt::DashLine);
524 QPen penGrid(QColor(0, 0, 0), 1, Qt::DotLine);
525 QPen penRoom(QColor(0, 0, 0), 1);
531 // Clipping in OnPrepareDC screws up print preview
532 QRect r = doc->getPageRect(printingPage - 1);
533 /// dc->SetWindowOrg(r.left(), r.bottom()); // FIXME adjust for margins
534 /// dc->IntersectClipRect(&r);
535 // dc->setTransform(QTransform::translate(r.left(), r.bottom()));
539 // Draw the page outlines:
540 if ((showPages || numSelectedPages) && !dc->IsPrinting())
542 dc->SetBkMode(TRANSPARENT);
544 lb.lbColor = RGB(192, 192, 192);
545 VERIFY(penPage.CreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT, penPageWidth, &lb));
546 dc->SelectObject(&penPage);
547 const PageVec & pageVec = doc->page;
550 dc->SelectStockObject(NULL_BRUSH);
551 QBrush * const nullBrush = dc->GetCurrentBrush();
552 bool focused = false;
554 for(PageConstItr p=pageVec.begin(); p!=pageVec.end(); ++p, ++pNum)
556 doc->getPageRect(*p, r);
558 if (dc->RectVisible(r))
560 const bool selectedI = selectedPage[pNum - 1] != 0;
562 if (!selectedI && !showPages)
563 continue; // Only show selected pages
565 if (selectedI != focused)
568 dc->SelectObject(focused ? &focusBrush : nullBrush);
570 /// CRgn inside,outside;
571 /// VERIFY(outside.CreateRectRgnIndirect(&r));
572 /// VERIFY(inside.CreateRectRgn(r.left + gridX, r.bottom - gridY,
573 /// r.right - gridX, r.top + gridY));
574 /// outside.CombineRgn(&outside, &inside, RGN_DIFF);
575 /// dc->FillRgn(&outside, &focusBrush);
577 dc->Rectangle(r.left + penPageWidth / 4, r.bottom - penPageWidth / 4, r.right, r.top);
579 _itot(pNum, pageNum, 10);
580 const int pageNumL = strlen(pageNum);
582 for(i=r.left+3*gridX/2; i<r.right-gridX; i+=gridX)
584 dc->TextOut(i, r.bottom - gridY * 7 / 8, pageNum, pageNumL);
585 dc->TextOut(i, r.top + gridY / 4, pageNum, pageNumL);
588 for(i=r.bottom-gridY*15/8; i>r.top+gridY; i-=gridY)
590 dc->TextOut(r.left + gridX / 2 + penPageWidth / 4, i, pageNum, pageNumL);
591 dc->TextOut(r.right- gridX / 2 - penPageWidth / 4, i, pageNum, pageNumL);
593 } // end if page visible
596 dc->SetBkMode(OPAQUE);
597 dc->SelectObject(whiteBrush);
598 dc->SelectObject(&penEdge); // Before destroying penPage
600 else // not showing pages
601 dc->SelectObject(&penEdge);
606 // Draw the background grid:
607 if (showGrid)// && (zoom >= minZoomGrid))//&& !dc->IsPrinting())
610 printf(" Preparing to draw grid...\n");
611 // dc->SetBkMode(TRANSPARENT);
612 // dc->SelectObject(&penGrid);
613 dc->setBrush(Qt::NoBrush);
617 // dc->GetClipBox(&clip);
618 // clip = dc->viewport();
619 // clip = dc->window();
621 printf(" clip = %i, %i, %i, %i\n", clip.x(), clip.y(), clip.width(), clip.height());
623 clip.setLeft(clip.left() - (clip.left() % gridX));
624 clip.setBottom(clip.bottom() - (clip.bottom() % gridY + gridY));
626 if (clip.top() < -gridY)
627 clip.setTop(clip.top() + gridY);
629 QSize docSize(doc->getDocSize());
630 docSize.setHeight(docSize.height() * -1);
632 if (clip.right() > docSize.width())
633 clip.setRight(docSize.width());
635 if (clip.bottom() < docSize.height())
636 clip.setBottom(docSize.height());
638 printf(" clip = %i, %i, %i, %i; docSize = %i, %i\n", clip.left(), clip.right(), clip.bottom(), clip.top(), docSize.width(), docSize.height());
639 for(i=clip.left(); i<=clip.right(); i+=gridX)
641 dc->drawLine(i, 0, i, docSize.height());
644 // for(i=clip.bottom(); i<=clip.top(); i+=gridY)
645 for(i=-clip.bottom(); i<=clip.top(); i+=gridY)
647 // dc->drawLine(0, i, docSize.width(), i);
648 dc->drawLine(0, i, docSize.width(), i);
651 // dc->SetBkMode(OPAQUE);
653 dc->setBrush(Qt::NoBrush);
655 QSize docSize(doc->docSize);
657 for(int i=0; i<=docSize.width(); i+=gridX)
658 dc->drawLine(i, 0, i, docSize.height());
660 for(int i=0; i<=docSize.height(); i+=gridY)
661 dc->drawLine(0, i, docSize.width(), i);
665 // Draw the room connections
666 int i = doc->edge.size();
667 EdgeConstItr edge = doc->edge.end() - 1;
668 RoomConstItr room = doc->room.v.begin();
675 doc->getEdgePoints(*edge, start, end);
677 // Offset the edge if we're dragging it...
678 if (opInProgress >= gmoDrag)
680 if (selected[edge->room1])
683 if ((selected[edge->room1] && (edge->type1 & etNoRoom2))
684 || selected[edge->room2])
688 if (edge->type1 & etObstructed)
689 dc->setPen(hoveredEdge == i ? penEdgeDashedHover : penEdgeDashed);
691 dc->setPen(hoveredEdge == i ? penEdgeHover : penEdge);
693 dc->drawLine(start, end);
695 if (edge->type1 & etOneWay)
698 dc->setBrush(Qt::black);
699 DrawArrowhead(dc, end, start);
702 else if (edge->type1 & etNoExit)
704 DrawNoExit(dc, end, start);
706 else if (edge->type1 & etLoopBack)
708 const double angle = Angle(start - end);
709 const double orthoAngle = angle + QTR_TAU;
710 const double size = 5.0;
712 QPointF ortho(cos(orthoAngle), sin(orthoAngle));
713 QPointF unit = UnitVector(end - start);
715 QPointF p1 = start + (ortho * 6.0 * size);
716 QPointF p2 = end + (ortho * 6.0 * size);
717 QPointF p3 = end + (ortho * 3.0 * size);
718 p3.rx() -= 3.0 * size;
719 p3.ry() -= 3.0 * size;
720 dc->drawLine(p1, p2);
721 QRectF r(p3, QSizeF(6.0 * size, 6.0 * size));
722 // Because painter.drawArc() uses a Cartesian (non-Y-inverted) coordinate system to determine its angles, we have to invert our Y-coords to get correct angles to feed to it. :-/
723 QPoint funnyStart(start.x(), start.y() * -1), funnyEnd(end.x(), end.y() * -1);
724 double funnyAngle = Angle(funnyStart - funnyEnd);
725 dc->drawArc(r, (int)((funnyAngle + QTR_TAU) * RADIANS_TO_DEGREES) * 16, 180 * 16);
727 dc->setBrush(Qt::black);
728 DrawArrowhead(dc, p1, p2);
734 if (edge->type1 & etDirection)
736 QPoint ex = QPoint(0, 0);
737 double angle = Angle(end - start);
742 // Adjust text position if it runs into an edge
743 if (((edge->end1 == rcNNW || edge->end1 == rcN || edge->end1 == rcNNE) && (angle > THREE_QTR_TAU && angle < TAU))
744 || ((edge->end1 == rcSSW || edge->end1 == rcS || edge->end1 == rcSSE) && (angle > QTR_TAU && angle < HALF_TAU))
745 || ((edge->end1 == rcE) && (angle > 0 && angle < QTR_TAU))
746 || ((edge->end1 == rcW) && (angle > HALF_TAU && angle < THREE_QTR_TAU)))
748 ex = edgLblOffsetEx[edge->end1];
751 const QPoint & p = room[edge->room1]->pos;
752 elBuf[0] = edgeLabel[(edge->type1 & etDirection) - 1];
753 dc->drawText(p + edgLblOffset[edge->end1] + ex, elBuf);
756 if (edge->type2 & etDirection)
758 QPoint ex = QPoint(0, 0);
759 double angle = Angle(start - end);
764 // Adjust text position if it runs into an edge
765 if (((edge->end2 == rcNNW || edge->end2 == rcN || edge->end2 == rcNNE) && (angle > THREE_QTR_TAU && angle < TAU))
766 || ((edge->end2 == rcSSW || edge->end2 == rcS || edge->end2 == rcSSE) && (angle > QTR_TAU && angle < HALF_TAU))
767 || ((edge->end2 == rcE) && (angle > 0 && angle < QTR_TAU))
768 || ((edge->end2 == rcW) && (angle > HALF_TAU && angle < THREE_QTR_TAU)))
770 ex = edgLblOffsetEx[edge->end2];
773 const QPoint & p = room[edge->room2]->pos;
774 elBuf[0] = edgeLabel[(edge->type2 & etDirection) - 1];
775 dc->drawText(p + edgLblOffset[edge->end2] + ex, elBuf);
783 bool focused = false;
786 for(const int count=doc->room.size(); i<count; i++, room++)
788 QRect r = (*room)->getRect();
789 QPoint cp = (*room)->GetCenter();
791 // Translate room if dragging operation in progress
792 if ((opInProgress >= gmoDrag) && selected[i])// && !(*room)->isCorner())
794 r.translate(p2Tmp.x(), p2Tmp.y());
798 // if (!dc->RectVisible((*room)->getRect(r)))
799 // if (!r.intersects(dc->viewport()))
802 const bool selectedI = (selected[i] != 0);
804 if (selectedI != focused)
807 bool hovered = r.contains(mouse);
808 RoomCorner rc = rcNone;
811 rc = doc->CornerHit(mouse - r.topLeft());
813 dc->setPen(hovered ? penEdgeHover : penRoom);
815 dc->setBrush(focused ? focusBrush : Qt::white);
817 bool needOpaqueText = false;
819 if ((*room)->flags & rfBorder)
825 dc->setBrush(focusBrush);
828 else // Room has no border and is not focused
829 needOpaqueText = !(*room)->name.empty();
831 if ((*room)->isCorner() && (focused || showCorners))
833 QRect centerRect(cp - QPoint(20, 20), QSize(40, 40));
835 dc->setPen(Qt::black);
836 dc->setBrush(Qt::black);
837 dc->drawEllipse(centerRect);
841 // dc->SetBkMode(TRANSPARENT);
843 if ((rc >= 0) && (rc <= 11) && ((*room)->isCorner() == false))
845 QRect rCrnr = cornerRect[rc].translated(r.topLeft());
846 // rCrnr.translate(r.topLeft());
848 dc->setPen(Qt::NoPen);
849 dc->setBrush(Qt::green);
854 if (!(*room)->note.empty())
856 QPoint noteOffset(roomWidth - 29, 57);
859 dc->setPen(QColor(0xFF, 0x00, 0x00));
860 dc->drawText(r.topLeft() + noteOffset, "*");
864 // if (needOpaqueText)
865 // dc->SetBkMode(OPAQUE);
867 // Shrink the rect left/right margins just a bit
868 QMargins margin(20, 0, 20, 0); // LTRB
869 r = r.marginsRemoved(margin);
871 //dc->setPen(Qt::blue);
875 dc->drawText(r, Qt::AlignCenter | Qt::TextWordWrap, (*room)->name.c_str());
877 // dc->SetBkMode(OPAQUE);
880 // JLH: Draw special stuffs (crap that was stuffed into OnMouseDown & OnMouseMove & the like...
881 if (opInProgress == gmoSelectBox)
883 dc->setPen(QPen(QColor(0x00, 0xFF, 0x00, 0xFF)));
884 dc->setBrush(QBrush(QColor(0x00, 0xFF, 0x00, 0x64)));
887 else if (opInProgress == gmoAddEdge)
890 dc->drawLine(p1Tmp, p2Tmp);
895 void MapView::keyPressEvent(QKeyEvent * event)
897 bool oldShift = shiftDown;
898 bool oldCtrl = ctrlDown;
899 bool oldAlt = altDown;
901 if (event->key() == Qt::Key_Shift)
903 else if (event->key() == Qt::Key_Control)
905 else if (event->key() == Qt::Key_Alt)
908 double oldZoom = zoom;
910 if (event->key() == Qt::Key_0)
912 else if (event->key() == Qt::Key_9)
914 else if (event->key() == Qt::Key_8)
916 else if (event->key() == Qt::Key_7)
918 else if (event->key() == Qt::Key_6)
920 else if (event->key() == Qt::Key_5)
922 else if (event->key() == Qt::Key_4)
924 else if (event->key() == Qt::Key_3)
926 else if (event->key() == Qt::Key_2)
928 else if (event->key() == Qt::Key_1)
935 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
938 ToolHandler(ToolKeyDown, Point(0, 0));
944 if (oldAlt != altDown)
947 setCursor(Qt::SizeAllCursor);
948 // oldPoint = oldScrollPoint;
952 if (select.size() > 0)
954 if (event->key() == Qt::Key_Up)
956 TranslateObjects(select, Point(0, +1.0));
959 else if (event->key() == Qt::Key_Down)
961 TranslateObjects(select, Point(0, -1.0));
964 else if (event->key() == Qt::Key_Right)
966 TranslateObjects(select, Point(+1.0, 0));
969 else if (event->key() == Qt::Key_Left)
971 TranslateObjects(select, Point(-1.0, 0));
979 void MapView::keyReleaseEvent(QKeyEvent * event)
981 bool oldShift = shiftDown;
982 bool oldCtrl = ctrlDown;
983 bool oldAlt = altDown;
985 if (event->key() == Qt::Key_Shift)
987 else if (event->key() == Qt::Key_Control)
989 else if (event->key() == Qt::Key_Alt)
993 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
996 ToolHandler(ToolKeyUp, Point(0, 0));
1002 if (oldAlt != altDown)
1005 setCursor(Qt::ArrowCursor);
1011 /////////////////////////////////////////////////////////////////////////////
1012 // CMapView printing
1013 //--------------------------------------------------------------------
1014 // Record which page we're printing, if any:
1016 // Output Variables:
1019 void MapView::OnPrepareDC(QPainter * dc, CPrintInfo * pInfo)
1021 CScrollZoomView::OnPrepareDC(dc, pInfo);
1024 printingPage = pInfo->m_nCurPage;
1030 bool MapView::OnPreparePrinting(CPrintInfo * pInfo)
1032 // Require registration before printing map with more than 10 rooms:
1033 MapDoc * const doc = GetDocument();
1036 if (doc->needRepaginate())
1040 switch (d.DoModal())
1050 pInfo->SetMaxPage(doc->page.size());
1051 return DoPreparePrinting(pInfo);
1055 void MapView::OnBeginPrinting(QPainter * /*pDC*/, CPrintInfo * /*pInfo*/)
1057 // Don't show selection or corners while printing:
1058 // We do this here instead of OnPreparePrinting, because OnEndPrinting
1059 // is only guaranteed to be called if OnBeginPrinting is.
1062 showCorners = false;
1066 void MapView::OnEndPrinting(QPainter* /*pDC*/, CPrintInfo* /*pInfo*/)
1068 if (GetDocument()->locked)
1069 OnUpdate(NULL, dupNavigationMode, NULL); // Select a room
1075 // CMapView miscellaneous
1080 // point: The point where the mouse was clicked (in logical coordinates)
1082 void MapView::addRoom(QPoint & point)
1084 QSize docSize(doc->docSize);
1086 if (doc->locked || (point.x() > docSize.width()) || (point.y() > docSize.height()))
1088 // MessageBeep(MB_ICONASTERISK);
1092 point.rx() -= roomWidth / 2 - gridX / 2;
1093 point.rx() -= point.x() % gridX;
1094 point.ry() -= roomHeight / 2 - gridY / 2;
1095 point.ry() -= point.y() % gridY;
1099 else if (point.x() + roomWidth > docSize.width())
1100 point.setX(docSize.width() - roomWidth);
1104 else if (point.y() + roomHeight > docSize.height())
1105 point.setY(docSize.height() - roomHeight);
1108 const bool wasModified = doc->isDirty;
1109 const int rNum = doc->addRoom(point);
1114 selectDone(); // FIXME disable floating comments
1115 editProperties(epuAddRoom, wasModified);
1120 // Make sure we aren't dragging a room off the edge:
1123 // p: The proposed offset for the selected rooms
1126 // rTmp: Rectangle enclosing the original positions of the selected rooms
1129 // p: The corrected (if necessary) offset for selected rooms
1131 void MapView::adjustOffset(QPoint & p) const
1133 const QSize size(doc->docSize);
1135 if (p.x() + rTmp.left() < 0)
1136 p.rx() = -rTmp.left();
1138 if (p.x() + rTmp.right() > size.width())
1139 p.rx() = size.width() - rTmp.right();
1141 if (p.y() + rTmp.top() < 0)
1142 p.ry() = -rTmp.top();
1144 if (p.y() + rTmp.bottom() > size.height())
1145 p.ry() = size.height() - rTmp.bottom();
1150 // Deselect all rooms:
1153 // update: TRUE means the selected rooms should be redrawn
1155 void MapView::clearSelection(bool update/* = true*/)
1157 if (doc->room.size() != selected.size())
1158 selected.resize(doc->room.size(), 0);
1160 if (update && numSelected)
1166 RoomConstItr room = doc->room.getVector().begin();
1168 for(int i=doc->room.size()-1; numSelected && i>=0; --i)
1173 room[i]->getRect(r);
1184 for(ByteItr b=selected.begin(); b!=selected.end(); ++b)
1187 deselectPages(update);
1192 // Compute a rectangle enclosing all selected rooms:
1201 // r: A rectangle enclosing all selected rooms
1203 void MapView::computeSelectedRect(QRect & r) const
1205 // r.SetRectEmpty();
1210 RoomConstItr room = doc->room.getVector().begin();
1212 for(int i=doc->room.size()-1; i>=0; i--)
1215 r |= room[i]->getRect();
1219 if (numSelectedPages)
1221 for(int i=doc->page.size()-1; i>=0; i--)
1223 if (selectedPage[i])
1224 r |= doc->getPageRect(i);
1234 // n: The page number to be deselected
1235 // update: TRUE means the page should be redrawn if necessary
1237 // Output Variables:
1242 void MapView::deselectPage(int n, bool update/* = true*/)
1244 ASSERT((n >= 0) && (n < doc->page.size()));
1246 if (selectedPage[n])
1248 selectedPage[n] = false;
1250 if ((--numSelectedPages == 0) && (numSelected == 1))
1252 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1253 ; // Find the selected room
1264 doc->getPageRect(n, r);
1275 // Deselect all pages:
1278 // update: TRUE means the selected pages should be redrawn
1280 // Output Variables:
1285 void MapView::deselectPages(bool update/* = true*/)
1287 ASSERT(selectedPage.size() == doc->page.size());
1289 if (update && numSelectedPages)
1292 // CClientDC dc(this);
1293 // OnPrepareDC(&dc);
1295 for(int i=doc->page.size()-1; numSelectedPages&&i>=0; i--)
1297 if (selectedPage[i])
1300 r = doc->getPageRect(i);
1304 // make sure it disappears
1305 r.setBottom(r.bottom() + penPageWidth);
1306 r.setLeft(r.left() - penPageWidth);
1310 // r.NormalizeRect();
1311 // InvalidateRect(r);
1316 numSelectedPages = 0;
1318 for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
1321 if (numSelected == 1)
1323 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1324 ; // Find the selected room
1335 // n: The room number to be deselected
1336 // update: TRUE means the room should be redrawn if necessary
1338 // Output Variables:
1343 void MapView::deselectRoom(RoomNum n, bool update/* = true*/)
1345 ASSERT((n >= 0) && (n < doc->room.size()));
1347 if (doc->room.size() != selected.size())
1348 selected.resize(doc->room.size(), 0);
1352 selected[n] = false;
1354 if ((--numSelected == 1) && !numSelectedPages)
1356 for (selectedOne=0; !selected[selectedOne]; selectedOne++)
1357 ; // Find the selected room
1368 doc->room[n].getRect(r);
1379 // Update the room comments dialog after the selection changes:
1381 void MapView::selectDone()
1383 // static_cast<CMapApp *>(AfxGetApp())->setComment(this, (selectedOne >= 0 ? &(GetDocument()->getRoom(selectedOne)) : NULL));
1391 // n: The page number to select
1392 // update: TRUE means the selected page should be redrawn if necessary
1394 // Output Variables:
1399 void MapView::selectPage(int n, bool update/* = true*/)
1401 if (!selectedPage[n])
1403 selectedPage[n] = true;
1413 doc->getPageRect(n, r);
1427 // n: The room number to select
1428 // update: TRUE means the selected room should be redrawn if necessary
1430 // Output Variables:
1435 void MapView::selectRoom(RoomNum n, bool update/*= true*/)
1437 if (doc->room.size() != selected.size())
1438 selected.resize(doc->room.size(), 0);
1444 if ((++numSelected == 1) && !numSelectedPages)
1455 doc->room[n].getRect(r);
1465 void MapView::selectOnlyRoom(RoomNum n)
1467 if (selectedOne != n)
1477 // Make sure that a room is visible:
1480 // n: The number of the room to be made visible
1482 void MapView::makeRoomVisible(RoomNum n)
1486 GetClientRect(&clientRect);
1487 QSize viewSize(clientRect.right, clientRect.bottom);
1489 CClientDC cdc(this);
1491 cdc.DPtoLP(&viewSize);
1493 const QSize docSize(doc->getDocSize());
1494 const QPoint curPos(getScrollPosition());
1496 doc->room[n].getRect(roomRect);
1498 QPoint newPos(curPos);
1500 if (roomRect.left - curPos.x < gridX)
1502 newPos.x -= curPos.x - roomRect.left + gridX;
1507 else if (roomRect.right - curPos.x + gridX > viewSize.cx)
1509 newPos.x += roomRect.right + gridX - viewSize.cx - curPos.x;
1511 if (newPos.x + viewSize.cx > docSize.cx)
1512 newPos.x = docSize.cx - viewSize.cx;
1515 if (curPos.y - roomRect.bottom < gridY)
1517 newPos.y += roomRect.bottom + gridY - curPos.y;
1522 else if (curPos.y - roomRect.top + gridY > viewSize.cy)
1524 newPos.y += roomRect.top + viewSize.cy - curPos.y - gridY;
1526 if (viewSize.cy - newPos.y > docSize.cy)
1527 newPos.y = viewSize.cy - docSize.cy;
1530 if (newPos != curPos)
1532 scrollToPosition(newPos);
1533 // Must adjust the room position because the DC won't be updated:
1534 roomRect.OffsetRect(curPos.x - newPos.x, curPos.y - newPos.y);
1537 // Make sure the floating comments dialog isn't in the way:
1538 cdc.LPtoDP(&roomRect);
1539 ClientToScreen(&roomRect);
1540 roomRect.NormalizeRect();
1541 static_cast<CMapApp *>(AfxGetApp())->adjustCommentPos(roomRect);
1547 // Paste a string to the clipboard:
1550 // text: The string to paste
1553 // True: String was successfully pasted
1554 // False: Could not paste (clipboard unavailable or out of memory)
1556 bool MapView::pasteClipboard(const string & text)
1559 if (!OpenClipboard())
1564 // Allocate a global memory object for the text
1565 HGLOBAL mem = GlobalAlloc(GMEM_DDESHARE, text.length() + sizeof(TCHAR));
1573 // Lock the handle and copy the text to the buffer
1575 strcpy(LPTSTR(GlobalLock(mem)), text.c_str());
1578 SetClipboardData(CFtrEXT, mem); // Place the handle on the clipboard
1587 // Update the selected room's comment from the floating dialog:
1590 // comment: The new room comment (must not be NULL)
1592 void MapView::setRoomNote(const char * comment)
1594 ASSERT(selectedOne >= 0);
1596 if (doc->room[selectedOne].note != comment)
1598 doc->setUndoData(new UndoRoomInfo(*doc, selectedOne));
1599 doc->setRoomNote(selectedOne, comment);
1604 int MapView::FindHoveredEdge(void)
1606 for(int i=0; i<doc->edge.size(); i++)
1610 doc->getEdgePoints(doc->edge[i], ep1, ep2);
1612 QPointF lineSegment = ep2 - ep1;
1613 QPointF v1 = mouse - ep1;
1614 QPointF v2 = mouse - ep2;
1615 double t = ParameterOfLineAndPoint(ep1, ep2, mouse);
1619 distance = Magnitude(v1);
1621 distance = Magnitude(v2);
1623 // distance = ?Det?(ls, v1) / |ls|
1624 distance = fabs(Determinant(lineSegment, v1)
1625 / Magnitude(lineSegment));
1627 if (distance < 20.0)
1631 // Didn't find a closely hovered edge
1637 // Set the step size of the scroll bars:
1639 void MapView::setScrollBars()
1643 GetClientRect(&clientRect);
1644 QSize viewSize(clientRect.right, clientRect.bottom);
1646 CClientDC cdc(this);
1648 cdc.DPtoLP(&viewSize);
1650 viewSize.cx -= gridX / 2;
1651 viewSize.cx -= viewSize.cx % gridX + roomWidth;
1652 viewSize.cx = max(viewSize.cx, gridX);
1654 viewSize.cy -= gridY / 2;
1655 viewSize.cy -= viewSize.cy % gridY + roomHeight;
1656 viewSize.cy = max(viewSize.cy, gridY);
1658 setPageStep(viewSize);
1664 // Zoom the display:
1667 // newZoom: The new zoom factor (in percent)
1669 void MapView::zoomTo(short newZoom)
1671 if (opInProgress == gmoAddCorner)
1672 opInProgress = gmoNone;
1674 if (newZoom == zoom)
1677 if (opInProgress || scrollDrag || (newZoom < minZoom) || (newZoom > maxZoom))
1678 ;//MessageBeep(MB_OK);
1681 // setDisplayZoom(zoom = newZoom);
1683 if (selectedOne >= 0)
1684 makeRoomVisible(selectedOne);
1687 // GetParentFrame()->OnUpdateFrameTitle(TRUE);
1694 //---------------------------------------------------------------------------
1695 // Keyboard navigation:
1698 // corner: The direction to move
1699 // toggleStubs: TRUE means adding an existing stub should delete it
1702 // edgeTmp: If type1 or type2 is nonzero, use that for a new edge
1703 // selectedOne: Must be a valid room number
1705 void MapView::navigate(RoomCorner corner, bool toggleStubs/* = true*/)
1707 const short roomXoffset[] =
1709 0, 0, gridX + roomWidth, -(gridX + roomWidth),
1710 gridX + roomWidth, -(gridX + roomWidth), gridX + roomWidth, -(gridX + roomWidth)
1711 }; // FIXME add NNE, etc.
1713 const short roomYoffset[] =
1715 gridY + roomHeight, -(gridY + roomHeight), 0, 0,
1716 gridY + roomHeight, gridY + roomHeight, -(gridY + roomHeight), -(gridY + roomHeight)
1717 }; // FIXME add NNE, etc.
1721 ASSERT(selectedOne >= 0);
1723 if (doc->room[selectedOne].isCorner())
1725 // MessageBeep(MB_ICONASTERISK);
1729 // Look for an existing connection:
1730 int eNum = doc->findEdge(selectedOne, corner, e);
1732 if ((eNum >= 0) && !(e.type2 & etUnexplored))
1734 // Found existing connection, and 2nd end is not explored
1735 if (toggleStubs && (e.type2 & etNoExit))
1737 if (edgeTmp.type1 & etNoExit)
1739 doc->setUndoData(new UndoDelete(doc->isDirty, doc->edge[eNum]));
1743 doc->setUndoData(new UndoChangeEdge(doc->isDirty, doc->edge[eNum]));
1744 edgeTmp.room1 = selectedOne;
1745 edgeTmp.end1 = corner;
1746 doc->addEdge(edgeTmp);
1749 doc->deleteEdge(eNum);
1753 if (edgeTmp.type1 || edgeTmp.type2 || (e.type1 & etOneWay)
1754 || (e.type2 & etNoRoom2))
1756 // We were trying to add connection
1757 // MessageBeep(MB_ICONASTERISK);
1761 if (e.end1 == rcCorner)
1763 int r = doc->findOtherEnd(eNum);
1769 // MessageBeep(MB_ICONASTERISK);
1772 } // end if this connection leads to a corner
1774 selectOnlyRoom(e.room1);
1775 makeRoomVisible(e.room1);
1776 eNum = -1; // Don't delete this edge
1778 else // eNum < 0 || e.type2 & etUnexplored
1780 // Try to add a new connection
1781 if (doc->locked || (corner >= rcNNE))
1783 // MessageBeep(MB_ICONASTERISK);
1787 const bool wasModified = doc->isDirty;
1790 // If adding stub where there's already a stub
1791 if ((eNum >= 0) && (e.type2 & etUnexplored)
1792 && (edgeTmp.type1 & etUnexplored))
1796 // Remove stub connection:
1797 doc->setUndoData(new UndoDelete(wasModified, doc->edge[eNum]));
1798 doc->deleteEdge(eNum);
1801 // MessageBeep(MB_ICONASTERISK);
1806 e.room1 = selectedOne;
1809 if (edgeTmp.type1 || edgeTmp.type2)
1811 e.type1 = edgeTmp.type1;
1812 e.type2 = edgeTmp.type2;
1817 EdgeVec deletedEdges;
1819 // If there's a room #2 connected to this corner of the room...
1820 if (!(e.type1 & etNoRoom2))
1822 QPoint pos(doc->room[selectedOne].pos);
1823 pos.rx() += roomXoffset[corner];
1824 pos.ry() += roomYoffset[corner];
1826 int room = doc->findRoom(pos);
1830 // We're off the grid
1831 // MessageBeep(MB_ICONASTERISK);
1836 // We need to add a new room
1837 room = doc->addRoom(pos);
1842 // Existing room, check for existing connection
1844 int oeNum = doc->findEdge(room, oppositeCorner[corner], oEdge);
1847 { // There is an existing connection
1848 if (oEdge.type2 & etUnexplored)
1850 deletedEdges.push_back(doc->edge[oeNum]);
1851 doc->deleteEdge(oeNum);
1854 eNum--; // The edge number might have changed
1857 room = -1; // Room has a non-stub connection there
1863 // MessageBeep(MB_ICONASTERISK);
1867 selectOnlyRoom(room);
1868 makeRoomVisible(room);
1871 e.end2 = oppositeCorner[corner];
1876 deletedEdges.push_back(doc->edge[eNum]);
1877 doc->deleteEdge(eNum);
1886 if (gueApp()->editAfterAdd()) //editAfterAdd() returns autoEdit
1887 editProperties(epuAddRoomEdge, wasModified, (deletedEdges.size() ? &deletedEdges : NULL));
1888 else if (deletedEdges.size())
1889 doc->setUndoData(new UndoChanges(wasModified, selectedOne, 1, deletedEdges));
1891 doc->setUndoData(new UndoAdd(wasModified, selectedOne, 1));
1893 else if (deletedEdges.size())
1895 if (deletedEdges.size() == 1)
1896 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
1898 doc->setUndoData(new UndoChanges(wasModified, -1, 1, deletedEdges));
1901 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
1908 // Fill in the edge type from the navigation box:
1910 // If the text in the navigation bar is up, down, in, or out, then set
1911 // the edge type appropriately. Otherwise, set the edge type to normal.
1912 // Always clears the navigation bar text.
1914 // Can only create up-down and in-out passages.
1917 // e.type1 and e.type2
1919 void MapView::setEdgeType(MapEdge & e)
1921 e.type1 = e.type2 = etNormal;
1923 RoomCorner corner1, corner2;
1924 char initial, separator;
1925 getNavText(initial, corner1, separator, corner2);
1927 if (initial && initial != '%' && initial != '>')
1929 // MessageBeep(MB_ICONASTERISK);
1937 case rcUp: e.type1 = etUp; break;
1938 case rcDown: e.type1 = etDown; break;
1939 case rcIn: e.type1 = etIn; break;
1940 case rcOut: e.type1 = etOut; break;
1943 if (separator == '-')
1947 case rcUp: e.type2 = etUp; break;
1948 case rcDown: e.type2 = etDown; break;
1949 case rcIn: e.type2 = etIn; break;
1950 case rcOut: e.type2 = etOut; break;
1954 else // standard edge
1958 case rcUp: e.type1 = etUp; e.type2 = etDown; break;
1959 case rcDown: e.type1 = etDown; e.type2 = etUp; break;
1960 case rcIn: e.type1 = etIn; e.type2 = etOut; break;
1961 case rcOut: e.type1 = etOut; e.type2 = etIn; break;
1966 e.type1 |= etObstructed;
1968 if (initial == '>' || separator == '>')
1969 e.type1 |= etOneWay;
1971 if (initial == '?' || separator == '?')
1973 e.type1 &= ~etSpecial;
1974 e.type1 |= etUnexplored;
1978 else if (initial == '!' || separator == '!')
1980 e.type1 &= ~etSpecial;
1981 e.type1 |= etNoExit;
1989 // Start typing in the navigation bar:
1991 // Stuffs the given char in the navigation bar and sets focus to there.
1994 // c: The character to put in the navigation bar.
1996 void MapView::setNavText(char c)
1999 CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
2002 nb.SetDlgItemText(IDC_NAV_DIR, text);
2004 CEdit * dir = static_cast<CEdit *>(nb.GetDlgItem(IDC_NAV_DIR));
2012 void MapView::OnChar(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
2014 if (((c >= 'A') && (c <= 'Z'))
2015 || ((c >= 'a') && (c <= 'z'))
2016 || (c == '-') || (c == '!') || (c == '?') || (c == '/')
2017 || (c == '>') || (c == '.') || (c == '~') || (c == '`')
2025 // Parse the text in the navigation bar:
2028 // mustBeMove: (default FALSE)
2029 // If TRUE, beep and don't clear text if not a direction.
2030 // If FALSE, clear text and don't beep even if it's meaningless.
2034 // The non-alphabetic character beginning the text (0 if none)
2036 // The direction indicated by the first direction word
2037 // rcNone if no direction words
2039 // The non-alphabetic character following the first direction word
2041 // Must be either '-', '>', or '?'
2043 // The direction indicated by the second direction word
2044 // rcNone if no second direction
2046 void MapView::getNavText(char & initial, RoomCorner & dir1, char & separator, RoomCorner & dir2, bool mustBeMove)
2049 CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
2052 nb.GetDlgItemText(IDC_NAV_DIR, text);
2055 dir1 = dir2 = rcNone;
2056 initial = separator = '\0';
2058 if (!text.IsEmpty() && (text[0] < 'a' || text[0] > 'z'))
2069 else if (!text.IsEmpty() && (text[0] == '-' || text[0] == '>'))
2071 separator = text[0];
2076 dir1 = parseDirection(text);
2078 if (!text.IsEmpty() && (text[0]=='-' || text[0]=='>' || text[0]=='?'))
2080 separator = text[0];
2086 dir2 = parseDirection(text);
2088 if (mustBeMove && (dir1 == rcNone || separator == '-' || ((initial || separator) && (dir1 >= rcNumCorners))))
2089 MessageBeep(MB_ICONASTERISK);
2092 nb.SetDlgItemText(IDC_NAV_DIR, tr(""));
2101 // Handle the Go button in the navigation bar:
2103 void MapView::OnNavGo()
2105 if (selectedOne < 0)
2107 MessageBeep(MB_ICONASTERISK);
2111 RoomCorner corner, junk2;
2112 char initial, separator;
2113 getNavText(initial, corner, separator, junk2, true);
2115 if (separator == '-' || junk2 != rcNone)
2117 MessageBeep(MB_ICONASTERISK);
2121 if (corner != rcNone)
2123 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2126 edgeTmp.type1 = etObstructed;
2128 if (initial == '>' || separator == '>')
2129 edgeTmp.type1 |= etOneWay;
2131 if (initial == '?' || separator == '?')
2132 edgeTmp.type1 = etUnexplored;
2133 else if (initial == '!' || separator == '!')
2134 edgeTmp.type1 = etNoExit;
2142 // Handle the direction keys on the numeric keypad:
2145 // cmd: Which command was executed
2147 void MapView::OnNavGoDir(unsigned int cmd)
2149 ASSERT(cmd >= ID_NAV_GO_NORTH && cmd <= ID_NAV_STUB2_OUT);
2151 if (selectedOne >= 0)
2154 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2155 bool toggleStubs = false;
2157 if (cmd >= ID_NAV_STUB1_NORTH)
2159 if (cmd >= ID_NAV_STUB2_NORTH)
2162 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etNoExit : etUnexplored);
2163 cmd -= ID_NAV_STUB2_NORTH - ID_NAV_GO_NORTH;
2168 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etUnexplored : etNoExit);
2169 cmd -= ID_NAV_STUB1_NORTH - ID_NAV_GO_NORTH;
2175 navigate(RoomCorner(cmd - ID_NAV_GO_NORTH), toggleStubs);
2178 MessageBeep(MB_ICONASTERISK);
2183 // CMapView message handlers
2184 //---------------------------------------------------------------------------
2185 // Update the comments dialog when the view is activated:
2187 void MapView::OnActivateView(bool bActivate, CView * pActivateView, CView * pDeactiveView)
2190 selectDone(); // Update comments dialog
2192 CScrollZoomView::OnActivateView(bActivate, pActivateView, pDeactiveView);
2197 // N.B.: If you click on a corner with no edge coming out of it, it will crash here... !!! FIX !!! [MOSTLY DONE--there's still a way to make it crash with "Add Corner", but I can't remember the specifics...]
2198 //void MapView::OnEditAddCorner()
2199 void MapView::HandleAddCorner(void)
2201 //printf("MapView::HandleAddCorner... (iTmp=%i, opInProgress=%i)\n", iTmp, opInProgress);
2202 ASSERT(opInProgress == gmoAddCorner);
2203 ASSERT(selectedOne >= 0);
2205 int room = doc->addCorner(selectedOne, iTmp);
2208 selectOnlyRoom(room);
2210 // MessageBeep(MB_ICONASTERISK);
2212 opInProgress = gmoNone;
2217 void MapView::HandleAddUnexplored(void)
2219 EdgeVec deletedEdges;
2220 const bool wasModified = doc->isDirty;
2223 edgeTmp.room1 = roomClicked;
2224 edgeTmp.end1 = cornerClicked;
2225 // This is from MapView::setEdgeType()
2226 edgeTmp.type1 &= ~etSpecial;
2227 edgeTmp.type1 |= etUnexplored;
2229 edgeTmp.end2 = rcSW;
2231 // Look for an existing connection:
2232 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2233 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2237 deletedEdges.push_back(doc->edge[eNum]);
2238 doc->deleteEdge(eNum);
2241 doc->addEdge(edgeTmp);
2243 if (deletedEdges.size())
2244 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2246 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2248 opInProgress = gmoNone;
2253 void MapView::HandleAddLoopBack(void)
2255 EdgeVec deletedEdges;
2256 const bool wasModified = doc->isDirty;
2259 edgeTmp.room1 = roomClicked;
2260 edgeTmp.end1 = cornerClicked;
2261 // This is from MapView::setEdgeType()
2262 edgeTmp.type1 &= ~etSpecial;
2263 edgeTmp.type1 |= etLoopBack;
2265 edgeTmp.end2 = rcSW;
2267 // Look for an existing connection:
2268 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2269 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2273 deletedEdges.push_back(doc->edge[eNum]);
2274 doc->deleteEdge(eNum);
2277 doc->addEdge(edgeTmp);
2279 if (deletedEdges.size())
2280 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2282 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2284 opInProgress = gmoNone;
2289 void MapView::HandleAddNoExit(void)
2291 EdgeVec deletedEdges;
2292 const bool wasModified = doc->isDirty;
2295 edgeTmp.room1 = roomClicked;
2296 edgeTmp.end1 = cornerClicked;
2297 // This is from MapView::setEdgeType()
2298 edgeTmp.type1 &= ~etSpecial;
2299 edgeTmp.type1 |= etNoExit;
2301 edgeTmp.end2 = rcSW;
2303 // Look for an existing connection:
2304 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2305 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2309 deletedEdges.push_back(doc->edge[eNum]);
2310 doc->deleteEdge(eNum);
2313 doc->addEdge(edgeTmp);
2315 if (deletedEdges.size())
2316 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2318 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2320 opInProgress = gmoNone;
2325 void MapView::SetEdgeDirection(EdgeType et)
2329 // Look for an existing connection:
2330 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2334 // Is the room we clicked on room1 of this edge?
2335 if (doc->edge[eNum].room1 == roomClicked)
2337 doc->edge[eNum].type1 &= ~etDirection;
2338 doc->edge[eNum].type1 |= et;
2342 doc->edge[eNum].type2 &= ~etDirection;
2343 doc->edge[eNum].type2 |= et;
2347 opInProgress = gmoNone;
2352 void MapView::HandleAddUp(void)
2354 SetEdgeDirection(etUp);
2358 void MapView::HandleAddDown(void)
2360 SetEdgeDirection(etDown);
2364 void MapView::HandleAddIn(void)
2366 SetEdgeDirection(etIn);
2370 void MapView::HandleAddOut(void)
2372 SetEdgeDirection(etOut);
2376 void MapView::SetEdges(int room, int edgeNum, EdgeType eType)
2379 OK, this is where the data structures let us down because they were poorly designed. Apparently, we have to walk thru the Edge vector for EVERY edge that has a corner to get to the other side. THIS LITERALLY FUCKING SUCKS.
2382 1) Figure out which side of the Edge has the room we're starting with.
2383 2) Using the other room # from the current Edge, loop thru the Edge vector to find the Edge connected to the room # we're looking at.
2384 3) Once the Edge is found, check to see if it's an rfCorner.
2388 Also, it seems that etOneWay only gets picked up by... type1. Which means, you have to swap the edge to get the arrowhead to point the other way...
2389 Which means you could do away with type2, only it uses that to distinguish the Up/Down/In/Out annotations put on map edges...
2392 One way to make that less awful is to overload the RoomCorner (which, for some reason, already has I/O/U/D in it as distinct members) so that it has bitfields reserved for labels attached... Then EdgeType would really be an edge type and separate from the room exits.
2394 // Go thru from current room to all connected "corner" rooms
2397 // Check for the unlikely case that we got passed bad info
2398 if (doc->edge[edgeNum].HasRoom(room) == false)
2401 if (doc->edge[edgeNum].room2 == room)
2402 doc->edge[edgeNum].Swap();
2404 if (((eType == etOneWay) && (doc->edge[edgeNum].end2 != rcCorner))
2405 || (eType != etOneWay))
2406 doc->edge[edgeNum].type1 |= eType;
2408 if (doc->edge[edgeNum].end2 != rcCorner)
2411 // Find next Edge... :-/
2412 room = doc->edge[edgeNum].room2;
2414 for(int i=0; i<doc->edge.size(); i++)
2416 // Exclude the edge we just came from...
2420 if (doc->edge[i].HasRoom(room))
2430 void MapView::ClearEdges(int room, int edgeNum, EdgeType eType)
2432 // Go thru from current room to all connected "corner" rooms
2435 // Check for the unlikely case that we got passed bad info
2436 if (doc->edge[edgeNum].HasRoom(room) == false)
2439 if (doc->edge[edgeNum].room2 == room)
2440 doc->edge[edgeNum].Swap();
2442 doc->edge[edgeNum].type1 &= ~eType;
2444 if (doc->edge[edgeNum].end2 != rcCorner)
2447 // Find next Edge... :-/
2448 room = doc->edge[edgeNum].room2;
2450 for(int i=0; i<doc->edge.size(); i++)
2452 // Exclude the edge we just came from...
2456 if (doc->edge[i].HasRoom(room))
2466 void MapView::HandleAddOneWay(void)
2470 // Look for an existing connection:
2471 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2474 SetEdges(roomClicked, eNum, etOneWay);
2476 opInProgress = gmoNone;
2481 void MapView::HandleClearOneWay(void)
2485 // Look for an existing connection:
2486 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2489 ClearEdges(roomClicked, eNum, etOneWay);
2491 opInProgress = gmoNone;
2496 void MapView::HandleAddRestricted(void)
2500 // Look for an existing connection:
2501 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2504 SetEdges(roomClicked, eNum, etObstructed);
2506 opInProgress = gmoNone;
2511 void MapView::HandleClearRestricted(void)
2515 // Look for an existing connection:
2516 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2519 ClearEdges(roomClicked, eNum, etObstructed);
2521 opInProgress = gmoNone;
2526 //void MapView::OnEditAddRoom()
2527 void MapView::HandleAddRoom(void)
2529 addRoom(scrollDragStart);
2533 void MapView::HandleMapProperties(void)
2537 dlg.title.setText(doc->name.c_str());
2538 dlg.comment.setPlainText(doc->note.c_str());
2539 dlg.navMode.setChecked(doc->locked);
2540 dlg.displayGrid.setChecked(showGrid);
2541 dlg.showCorners.setChecked(showCorners);
2542 dlg.showPages.setChecked(showPages);
2544 if (dlg.exec() == false)
2547 doc->setName(dlg.title.text().toUtf8().data());
2548 doc->setNote(dlg.comment.document()->toPlainText().toUtf8().data());
2549 doc->locked = dlg.navMode.isChecked();
2550 showGrid = dlg.displayGrid.isChecked();
2551 showCorners = dlg.showCorners.isChecked();
2552 showPages = dlg.showPages.isChecked();
2554 if (dlg.title.text().isEmpty() == false)
2555 setWindowTitle(dlg.title.text());
2561 //void MapView::OnEditClear()
2562 void MapView::HandleDelete(void)
2570 //--------------------------------------------------------------------
2571 void MapView::OnEditAddPage()
2573 MapDoc * const doc = GetDocument();
2576 const int p = doc->page.size();
2581 page.pos = scrollDragStart;
2582 page.pos.x -= page.pos.x % gridX;
2583 page.pos.y -= page.pos.y % gridY;
2584 doc->setUndoData(new UndoAdd(doc->isDirty, p));
2586 doc->addPage(p, page);
2596 // removeCorner: (default true)
2597 // When TRUE, if a single corner is selected, remove it but leave
2598 // the connection. When FALSE, the connection is always removed.
2600 void MapView::deleteSelection(bool removeCorner/* = true*/)
2602 if (numSelected || numSelectedPages)
2604 if (doc->locked || (numSelectedPages == doc->page.size()))
2606 // MessageBeep(MB_ICONASTERISK);
2610 if (removeCorner && (selectedOne >= 0)
2611 && doc->room[selectedOne].isCorner())
2613 // Replace this corner with a single edge:
2614 doc->deleteCorner(selectedOne);
2615 clearSelection(false);
2621 // Select all corners if one corner or one end is selected:
2622 const EdgeVec & edge = doc->edge;
2623 int wereSelected = 0;
2625 while (wereSelected != numSelected)
2627 wereSelected = numSelected;
2629 for(EdgeConstItr e=edge.begin(); e!=edge.end(); ++e)
2631 if ((e->end1 == rcCorner) && selected[e->room2] && !selected[e->room1])
2632 selectRoom(e->room1, false);
2633 else if ((e->end2==rcCorner) && selected[e->room1] && !selected[e->room2])
2634 selectRoom(e->room2, false);
2639 UndoDelete * undoRec = new UndoDelete(*doc, numSelected, selected, numSelectedPages, selectedPage);
2640 opInProgress = gmoDeleting; // Don't clear selection after first room
2641 int pos = numSelected;
2643 for(int i=doc->room.size()-1; i>=0; i--)
2646 undoRec->addRoom(--pos, i, doc->extractRoom(i));
2649 for(int i=doc->page.size()-1; i>=0; i--)
2651 if (selectedPage[i])
2655 selectedPage.resize(doc->page.size(), 0);
2656 opInProgress = gmoNone;
2657 doc->setUndoData(undoRec);
2659 clearSelection(false);
2666 //--------------------------------------------------------------------
2667 // Cut, Copy & Paste:
2669 void MapView::OnEditCopy()
2671 if (numSelected || numSelectedPages)
2673 MapDoc * doc = GetDocument();
2676 // Deselect all corner rooms:
2677 for(int i=doc->room.size()-1; i>=0; --i)
2679 if (selected[i] && doc->room[i].isCorner())
2683 if (!numSelected && !numSelectedPages)
2686 MessageBeep(MB_ICONASTERISK);
2688 } // end if only corners were selected
2690 // Select corners between selected rooms:
2691 const EdgeVec& edges = doc->edge;
2693 for (EdgeConstItr e = edges.begin(); e != edges.end(); ++e)
2695 if ((selected[e->room1] && (e->end2 == rcCorner)
2696 && (e->end1 != rcCorner) && !selected[e->room2])
2697 || (selected[e->room2] && (e->end1 == rcCorner)
2698 && (e->end2 != rcCorner) && !selected[e->room1]))
2700 RoomNumVec cornerRooms;
2701 cornerRooms.reserve(16); // Should be plenty
2702 int otherEnd = doc->findOtherEnd(e, &cornerRooms);
2704 if ((otherEnd >= 0) && selected[otherEnd])
2706 for(RNConstItr rn=cornerRooms.begin(); rn!=cornerRooms.end(); ++rn)
2709 } // end if edge connects selected room to unselected corner
2714 // Copy selected rooms and pages:
2715 gueApp()->setClipboard(new RoomScrap(*doc, numSelected, selected, numSelectedPages, selectedPage));
2720 void MapView::OnEditCut()
2727 void MapView::OnEditPaste()
2729 const RoomScrap * scrap = static_cast<CMapApp *>(AfxGetApp())->getClipboard();
2733 MapDoc * doc = GetDocument();
2736 int i = doc->room.size();
2738 doc->setUndoData(scrap->paste(*doc));
2739 const int numRooms = doc->room.size();
2744 clearSelection(true);
2746 while (i < numRooms)
2747 selectRoom(i++, true);
2754 void MapView::OnEditPaginate()
2757 GetDocument()->layoutPages();
2763 // Handle Edit Properties:
2765 void MapView::HandleRoomProperties(void)
2767 // editProperties((cmd == ID_EDIT_MAP_PROPERTIES) ? epuMapInfo : epuRoomInfo);
2768 editProperties(epuRoomInfo);
2773 // Bring up the Properties dialog:
2777 // The type of action we should create an undo record for
2778 // epuRoomInfo: Just changing room info
2779 // epuAddRoom: Adding a new room
2780 // epuAddRoomEdge: Adding a new room and a new edge
2782 // The modification state of the document
2783 // (Necessary only if undoType is not epuRoomInfo)
2784 // edges: (default NULL)
2785 // A pointer to the edges that were deleted before adding this room
2786 // NULL if no edges were deleted
2787 // Must be NULL unless undoType is epuAddRoomEdge
2788 // The vector is emptied but not deleted
2790 void MapView::editProperties(EditPropUndo undoType, bool wasModified/*= false*/, EdgeVec * edges/*= NULL*/)
2792 bool forceMap = false;
2794 if (undoType == epuMapInfo)
2796 undoType = epuRoomInfo;
2800 // CPropertySheet dlg(tr("Properties"));
2801 RoomDialog dlg(this);
2802 // CRoomPropPage room;
2803 UndoRoomInfo * undoRoom = NULL;
2804 const bool roomTab = (selectedOne >= 0) && !doc->room[selectedOne].isCorner();
2806 // CMapPropPage map;
2807 // Strcpy(map.name, doc->getName());
2808 // Strcpy(map.note, doc->getNote());
2809 // map.corners = showCorners;
2810 // map.locked = doc->locked;
2811 // map.grid = showGrid;
2812 // map.pages = showPages;
2813 // dlg.AddPage(&map);
2817 // VERIFY(undoRoom = new UndoRoomInfo(*doc, selectedOne));
2818 undoRoom = new UndoRoomInfo(*doc, selectedOne);
2819 // room.border = bool((undoRoom->getRoom().flags & rfBorder) != 0);
2820 // Strcpy(room.name, undoRoom->getRoom().name);
2821 // Strcpy(room.note, undoRoom->getRoom().note);
2822 // dlg.AddPage(&room);
2823 // static_cast<CMapApp *>(AfxGetApp())->setComment(this, NULL, false);
2824 dlg.name.setText(undoRoom->room.name.c_str());
2825 dlg.comment.insertPlainText(undoRoom->room.note.c_str());
2826 dlg.border.setChecked(bool((undoRoom->room.flags & rfBorder) != 0));
2829 // dlg.SetActivePage(&room);
2832 // if (dlg.DoModal() == IDOK)
2837 bool changed = false;
2839 // if (room.border != (0 != (undoRoom->getRoom().flags & rfBorder)))
2840 if (dlg.border.isChecked() != (0 != (undoRoom->room.flags & rfBorder)))
2842 // doc->setRoomFlags(selectedOne, (room.border ? rfBorder : 0), rfBorder);
2843 doc->setRoomFlags(selectedOne, (dlg.border.isChecked() ? rfBorder : 0), rfBorder);
2847 // if (room.name != undoRoom->getRoom().name.c_str())
2848 if (dlg.name.text().toUtf8().data() != undoRoom->room.name.c_str())
2850 // if (room.name.Find('\n') < 0)
2851 // wrapRoomName(room.name);
2853 doc->setRoomName(selectedOne, dlg.name.text().toUtf8().data());
2857 // room.note.TrimRight();
2859 if (dlg.comment.document()->toPlainText().toUtf8().data() != undoRoom->room.note.c_str())
2861 // doc->setRoomNote(selectedOne, room.note);
2862 doc->setRoomNote(selectedOne, dlg.comment.document()->toPlainText().toUtf8().data());
2866 if (changed && (undoType == epuRoomInfo))
2868 doc->setUndoData(undoRoom);
2869 undoRoom = NULL; // undoRoom now belongs to the document
2873 if (undoType != epuRoomInfo)
2875 ASSERT(selectedOne >= 0);
2878 doc->setUndoData(new UndoChanges(wasModified, selectedOne, 1, *edges));
2880 doc->setUndoData(new UndoAdd(wasModified, selectedOne, (undoType == epuAddRoomEdge ? 1 : 0)));
2883 // doc->setName(map.name);
2884 // doc->setNote(map.note);
2886 // if (doc->locked != bool(map.locked))
2887 // doc->OnNavigationMode();
2889 /* if ((showCorners != bool(map.corners))
2890 || (showGrid != bool(map.grid))
2891 || (showPages != bool(map.pages)))
2893 showCorners = map.corners != 0;
2894 showGrid = map.grid != 0;
2895 showPages = map.pages != 0;
2898 deselectPages(false);
2900 // InvalidateRect(NULL);
2903 else if (undoType != epuRoomInfo)
2905 // Cancel adding the room:
2906 ASSERT(selectedOne >= 0);
2907 const RoomNum roomNum = selectedOne;
2910 if (undoType == epuAddRoomEdge)
2912 const MapEdge & edge = doc->edge[doc->edge.size() - 1];
2913 selectOnlyRoom(edge.room1 == roomNum ? edge.room2 : edge.room1);
2916 opInProgress = gmoDeleting; // Don't clear selection
2917 doc->deleteRoom(roomNum);
2918 opInProgress = gmoNone;
2922 doc->addEdges(edges->size());
2924 for(EdgeConstItr e=edges->begin(); e!=edges->end(); e++)
2928 doc->isDirty = wasModified;
2939 // Select all rooms:
2941 void MapView::OnEditSelectAll()
2943 for(int i=doc->room.size()-1; i>=0; i--)
2950 //---------------------------------------------------------------------------
2951 // Select connected rooms:
2953 void MapView::OnEditSelectConnected()
2955 const MapDoc * doc = GetDocument();
2958 const int numEdges = doc->getEdgeCount();
2959 const EdgeVec & edge = doc->edge;
2961 int wasSelected = 0;
2963 while (wasSelected != numSelected)
2965 wasSelected = numSelected;
2967 for(int i=0; i<numEdges; i++)
2969 if ((edge[i].type1 & etNoRoom2) == 0)
2971 if (selected[edge[i].room1])
2972 selectRoom(edge[i].room2);
2973 else if (selected[edge[i].room2])
2974 selectRoom(edge[i].room1);
2975 } // end if edge connects two rooms
2977 } // end while more rooms were selected on this pass
2983 void MapView::OnInitialUpdate()
2985 const MapDoc * doc = GetDocument();
2988 selectedPage.resize(doc->page.size(), 0);
2989 clearSelection(false);
2991 // Set the document size:
2992 init(4 * gridX, doc->getDocSize(), QSize(gridX, gridY));
2993 // CClientDC dc(this);
2994 // OnPrepareDC(&dc);
2996 memset(&lf, 0, sizeof(lf));
2998 strcpy(lf.lfFaceName, "Arial");
2999 font.CreateFontIndirect(&lf);
3002 OnUpdate(NULL, dupNavigationMode, NULL);
3004 CScrollZoomView::OnInitialUpdate();
3005 setScrollBars(); // Now fix the scroll bars
3009 void MapView::OnKeyDown(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
3011 bool ctrl = (GetKeyState(VK_CONTROL) < 0);
3015 case VK_LEFT: OnHScroll(ctrl ? SB_PAGELEFT : SB_LINELEFT, 0, NULL); break;
3016 case VK_RIGHT: OnHScroll(ctrl ? SB_PAGERIGHT : SB_LINERIGHT, 0, NULL); break;
3017 case VK_UP: OnVScroll(ctrl ? SB_PAGEUP : SB_LINEUP, 0, NULL); break;
3018 case VK_DOWN: OnVScroll(ctrl ? SB_PAGEDOWN : SB_LINEDOWN, 0, NULL); break;
3019 case VK_PRIOR: OnVScroll(ctrl ? SBtrOP : SB_PAGEUP, 0, NULL); break;
3020 case VK_NEXT: OnVScroll(ctrl ? SB_BOTTOM : SB_PAGEDOWN, 0, NULL); break;
3021 case VKtrAB: gueApp()->editComment(); break;
3023 if ((GetKeyState(VK_SHIFT) >= 0) && (c >= '0') && (c <= '9'))
3025 if (!ctrl && (c < '2'))
3027 else if (ctrl && (c == '0'))
3030 zoomTo(10 * (c - '0') + (ctrl ? 100 : 0));
3031 } // end if number key (not shifted)
3039 void MapView::mouseDoubleClickEvent(QMouseEvent * event)
3040 //void MapView::OnLButtonDblClk(unsigned int nFlags, QPoint point)
3042 if (scrollDrag || scrollDragTimer)
3045 // QPoint point = event->pos() * 5;
3046 QPointF ptf = event->localPos() * (100.0 / zoom);
3047 QPoint point = ptf.toPoint();
3048 // QPoint point = event->pos() * (100.0 / zoom);
3049 int nFlags = event->modifiers();
3054 opInProgress = gmoNone;
3056 // if (GetCapture() == this)
3057 // ReleaseCapture();
3059 // CClientDC dc(this);
3060 // OnPrepareDC(&dc);
3061 // dc.DPtoLP(&point);
3063 const int i = doc->roomHit(point);
3064 //printf("MapView::mouseDoubleClickEvent: roomHit=%i\n", i);
3066 if ((i >= 0) && (doc->room[i].isCorner() == false))
3069 editProperties(epuRoomInfo);
3076 /* N.B.: Handles RButton & MButton too */
3077 void MapView::mousePressEvent(QMouseEvent * event)
3078 //void MapView::OnLButtonDown(unsigned int nFlags, QPoint point)
3080 nFlags = MK_CONTROL, MK_SHIFT, MK_{LMR}BUTTON
3083 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3084 // QPoint point = event->pos() * 5;
3085 QPointF ptf = event->localPos() * (100.0 / zoom);
3086 QPoint point = ptf.toPoint();
3087 // QPoint point = event->pos() * (100 / zoom);
3088 int nFlags = event->modifiers();
3089 mouseDown = true;//will prolly have to separate this out to L, M & R :-P
3091 Qt::NoModifier, Qt::ShiftModifier, Qt::ControlModifier, Qt::AltModifier, etc.
3094 // Scrolling support (save this for now)...
3096 offsetScrollDragStart = offset;
3097 scrollDragStart = point + offset;
3099 if (event->button() == Qt::LeftButton)
3101 if (scrollDrag || scrollDragTimer)
3104 // CClientDC dc(this);
3105 // OnPrepareDC(&dc);
3106 // dc.DPtoLP(&point);
3109 int room = doc->roomHit(point, &corner);
3114 return; // Must have 1 room selected during navigation
3116 if (showPages && ((room = doc->pageBorderHit(point)) >= 0))
3120 // if (nFlags & MK_CONTROL)
3121 if (nFlags & Qt::ControlModifier)
3123 opInProgress = gmoControlDown;
3124 bTmp = selectedPage[room];
3125 b2Tmp = true; // In page
3129 // if (nFlags & MK_SHIFT)
3130 if (nFlags & Qt::ShiftModifier)
3131 opInProgress = gmoShiftDown;
3133 opInProgress = gmoNone;
3144 p1Tmp.rx() = point.x() / gridX;
3145 p1Tmp.ry() = point.y() / gridY;
3146 p2Tmp = doc->page[room].pos;
3152 opInProgress = gmoSelectBox;
3154 // if (nFlags & MK_CONTROL)
3155 if (nFlags & Qt::ControlModifier)
3157 // If Ctrl pressed, don't clear selection:
3158 for(ByteItr b=selected.begin(); b!=selected.end(); b++)
3161 *b = 2; // Mark the rooms that should stay selected
3164 for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
3167 *b = 2; // Mark the pages that should stay selected
3178 // rTmp.left = rTmp.right = point.x;
3179 // rTmp.bottom = rTmp.top = point.y;
3180 rTmp.setCoords(point.x(), point.y(), point.x(), point.y());
3185 else if (doc->locked)
3188 ASSERT(selectedOne >= 0);
3190 if ((room != selectedOne) && !doc->room[room].isCorner())
3193 doc->shortestPath(path, selectedOne, room);
3196 path = tr("Sorry, I don't know how to get there.");
3197 else if (path.find('.') != String::npos)
3199 const CMapApp * app = static_cast<CMapApp *>(AfxGetApp());
3201 if (app->navigationCopy())
3203 if (app->navigationCRLF())
3206 pasteClipboard(path);
3209 // end else if path has multiple steps
3211 static_cast<CMainFrame *>(AfxGetMainWnd())->setStatusBar(path.c_str());
3212 selectOnlyRoom(room);
3214 // end if moving to new room in navigation mode
3217 // else if (nFlags & MK_CONTROL)
3218 else if (nFlags & Qt::ControlModifier)
3221 opInProgress = gmoControlDown;
3222 ASSERT(room < selected.size());
3223 bTmp = selected[room];
3224 b2Tmp = false; // In room
3228 p1Tmp.rx() = point.x() / gridX;
3229 p1Tmp.ry() = point.y() / gridY;
3232 // else if (nFlags & MK_SHIFT)
3233 else if (nFlags & Qt::ShiftModifier)
3236 opInProgress = gmoShiftDown;
3237 selectOnlyRoom(room);
3238 iTmp = -1; // Not a page
3239 p1Tmp.rx() = point.x() / gridX;
3240 p1Tmp.ry() = point.y() / gridY;
3241 p2Tmp = doc->room[room].pos;
3245 selectOnlyRoom(room);
3247 if (!(doc->room[room].isCorner()))
3248 emit(RoomClicked(doc, room));
3250 if (doc->room[room].isCorner())
3253 opInProgress = gmoControlDown;
3254 bTmp = b2Tmp = false; // Leave the corner selected; in room
3255 p1Tmp.rx() = point.x() / gridX;
3256 p1Tmp.ry() = point.y() / gridY;
3258 else if (corner != rcNone)
3261 opInProgress = gmoAddEdge;
3262 bTmp = doc->isDirty;
3263 iTmp = doc->findEdge(room, corner, edgeTmp);
3267 opInProgress = gmoChangeEdge;
3268 rTmp.setCoords(point.x() - 29, point.y() - 29, point.x() + 29, point.y() + 29);
3269 // rTmp.InflateRect(29, 29);
3272 if ((iTmp < 0) || (edgeTmp.type2 & etNoRoom2))
3274 edgeTmp.room1 = room;
3275 edgeTmp.end1 = corner;
3276 setEdgeType(edgeTmp);
3280 edgeTmp.end2 = rcSW;
3281 doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3284 // OnPaint(); // Update the view immediately
3287 if (opInProgress == gmoAddEdge)
3292 if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3295 QPen * oldPen = dc.SelectObject(&penEdge);
3298 dc.SelectObject(oldPen);
3306 else if (event->button() == Qt::RightButton)
3308 //void MapView::OnRButtonDown(unsigned int nFlags, QPoint point)
3310 /* if (opInProgress == gmoAddCorner)
3311 opInProgress = gmoNone;
3313 // CClientDC dc(this);
3314 // OnPrepareDC(&dc);
3315 // dc.DPtoLP(&point);
3317 scrollDragStart = point;
3323 // ::SetCursor(handCursor);
3326 // scrollDragTimer = SetTimer(menuTimer, clickTicks, NULL);
3333 bool MapView::calcScrollPosition(QPoint & pt) const
3343 // VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_HORZ, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3345 // if (pt.x + si.nPage > si.nMax + scrollStep.cx)
3346 // pt.x = si.nMax - si.nPage + scrollStep.cx;
3348 // VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_VERT, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3350 // if (-pt.y + si.nPage > si.nMax + scrollStep.cy)
3351 // pt.y = si.nPage - si.nMax - scrollStep.cy;
3353 // pt.x -= pt.x % scrollStep.cx;
3354 // pt.y -= pt.y % scrollStep.cy;
3356 return offset != pt;
3361 void MapView::scrollToPosition(const QPoint & pt)
3363 QPoint newOffset(pt);
3365 if (!calcScrollPosition(newOffset))
3366 return; // Didn't scroll
3368 // QSize delta(newOffset - offset);
3371 // CClientDC dc(this);
3372 // OnPrepareDC(&dc);
3373 // dc.LPtoDP(&delta);
3375 // SetScrollPos(SB_VERT, -offset.y);
3376 // SetScrollPos(SB_HORZ, offset.x);
3377 // ScrollWindow(-delta.cx, delta.cy);
3381 void MapView::mouseMoveEvent(QMouseEvent * event)
3382 //void MapView::OnMouseMove(unsigned int nFlags, QPoint point)
3384 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3385 // QPoint point = event->pos() * 5;
3386 QPointF ptf = event->localPos() * (100.0 / zoom);
3387 QPoint point = ptf.toPoint();
3388 // QPoint point = event->pos() * (100 / zoom);
3389 int nFlags = event->modifiers();
3394 // Location where the mouse is currently pointing
3397 // Bail if we're just mousing around...
3398 if (mouseDown == false)
3400 hoveredEdge = FindHoveredEdge();
3406 /* if (scrollDragTimer)
3408 // KillTimer(scrollDragTimer);
3409 scrollDragTimer = 0;
3412 // ::SetCursor(handCursor);
3415 // if (GetCapture() != this)
3418 // CClientDC dc(this);
3419 // OnPrepareDC(&dc);
3420 // dc.DPtoLP(&point);
3427 // p.Offset(scrollDragStart - point);
3428 p += (scrollDragStart - point);
3430 if (!calcScrollPosition(p)
3431 )// || PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3434 if ((opInProgress == gmoAddEdge) || (opInProgress == gmoSelectBox)
3435 || (opInProgress >= gmoDrag))
3438 // We must handle these specially to avoid display artifacts:
3440 CGdiObject * old = NULL;
3444 switch (opInProgress)
3447 if (!pen.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0 ,0)))
3450 old = dc.SelectObject(&pen);
3456 old = dc.SelectStockObject(HOLLOW_BRUSH);
3462 break; // Must be gmoDrag
3466 scrollToPosition(p);
3468 // OnPrepareDC(&dc); // Update with new offset
3471 switch (opInProgress)
3473 case gmoAddEdge: dc.MoveTo(p1Tmp); dc.LineTo(p2Tmp); break;
3474 case gmoSelectBox: dc.Rectangle(rTmp); break;
3475 default: /* drag*/ drawSelected(&dc); break;
3479 dc.SelectObject(old);
3483 scrollToPosition(p);
3485 QPoint delta = (point + offset) - scrollDragStart;
3486 offset = offsetScrollDragStart + delta;
3492 if (opInProgress == gmoChangeEdge)
3494 // if (rTmp.PtInRect(point))
3495 if (rTmp.contains(point))
3498 // e2Tmp = doc->getEdge(iTmp); // Save the current edge data
3499 e2Tmp = doc->edge[iTmp]; // Save the current edge data
3500 doc->deleteEdge(iTmp); // Delete the old edge
3501 // OnPaint(); // Update the view immediately
3502 opInProgress = gmoAddEdge; // Now fall through to gmoAddEdge
3505 if (opInProgress == gmoAddEdge)
3507 // dc.SetROP2(R2_NOT);
3510 // if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0))) // PORT
3513 // QPen * oldPen = dc.SelectObject(&penEdge);
3514 // dc.MoveTo(p1Tmp);
3515 // dc.LineTo(p2Tmp);
3518 int room = doc->roomHit(point, &corner);
3520 if (corner != rcNone && room != edgeTmp.room1)
3522 edgeTmp.room2 = room;
3523 edgeTmp.end2 = corner;
3524 doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3531 // dc.MoveTo(p1Tmp);
3532 // dc.LineTo(p2Tmp);
3533 // dc.SelectObject(oldPen);
3535 else if (opInProgress == gmoSelectBox)
3540 // dc.SetROP2(R2_NOT);
3541 // QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3542 // dc.Rectangle(rTmp);
3544 rTmp = QRect(p1Tmp, point).normalized();
3545 // rTmp.NormalizeRect();
3546 RoomConstItr room = doc->room.getVector().begin();
3547 // QRect intersect, r;
3549 for(int i=doc->room.size()-1; i>=0; i--)
3551 if (selected[i] == 2)
3552 continue; // Room was already selected before selection box
3555 room[i]->getRect(r);
3556 // intersect.IntersectRect(rTmp, r);
3557 intersect = rTmp & r;
3559 QRect r = room[i]->getRect();
3560 QRect intersect = rTmp & r;
3563 if ((selected[i] != 0) != bool(r == intersect))
3565 selected[i] = !selected[i];
3566 numSelected += (selected[i] ? 1 : -1);
3567 // r.DeflateRect(5, 5);
3569 // r.NormalizeRect();
3570 // InvalidateRect(&r);
3576 for(int i=doc->page.size()-1; i>=0; i--)
3578 if (selectedPage[i] == 2)
3579 continue; // Page was already selected before selection box
3582 doc->getPageRect(i, r);
3583 // intersect.IntersectRect(rTmp, r);
3584 intersect = rTmp & r;
3586 QRect r = doc->getPageRect(i);
3587 QRect intersect = rTmp & r;
3590 if ((selectedPage[i] != 0) != bool(r == intersect))
3592 selectedPage[i] = !selectedPage[i];
3593 numSelectedPages += (selectedPage[i] ? 1 : -1);
3594 // r.DeflateRect(5, 5);
3596 // r.NormalizeRect();
3597 // InvalidateRect(&r);
3602 if ((numSelected == 1) && !numSelectedPages)
3604 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
3605 ; // Find the selected room
3612 // if (GetUpdateRect(NULL))
3615 // dc.Rectangle(rTmp);
3616 // dc.SelectObject(oldBrush);
3619 else if (opInProgress == gmoControlDown)
3621 p2Tmp.rx() = gridX * (point.x() / gridX - p1Tmp.x());
3622 p2Tmp.ry() = gridY * (point.y() / gridY - p1Tmp.y());
3624 if (p2Tmp.x() || p2Tmp.y())
3626 opInProgress = gmoControlDrag;
3627 computeSelectedRect(rTmp);
3628 // drawBackground(&dc);
3629 // dc.SetROP2(R2_NOT);
3630 // drawSelected(&dc);
3633 else if (opInProgress == gmoShiftDown)
3635 long ox = point.x() / gridX;
3636 long oy = point.y() / gridY;
3639 opInProgress = gmoShiftDragLeft;
3640 else if (ox > p1Tmp.x())
3641 opInProgress = gmoShiftDragRight;
3642 else if (oy > p1Tmp.y())
3643 opInProgress = gmoShiftDragUp;
3644 else if (oy < p1Tmp.y())
3645 opInProgress = gmoShiftDragDown;
3649 // if dragging a page
3652 ASSERT(!numSelected && (numSelectedPages == 1));
3653 rTmp = doc->getPageRect(iTmp);
3655 switch (opInProgress)
3657 case gmoShiftDragUp:
3658 p2Tmp.ry() = rTmp.top();
3660 case gmoShiftDragLeft:
3661 p2Tmp.rx() = rTmp.right() - roomWidth + gridX;
3666 RoomConstItr room = doc->room.getVector().begin();
3667 const int roomCount = doc->room.size();
3669 for(int i=0; i<roomCount; i++)
3671 if ((opInProgress == gmoShiftDragLeft && room[i]->pos.x() <= p2Tmp.x())
3672 || (opInProgress == gmoShiftDragRight && room[i]->pos.x() >= p2Tmp.x())
3673 || (opInProgress == gmoShiftDragUp && room[i]->pos.y() >= p2Tmp.y())
3674 || (opInProgress == gmoShiftDragDown && room[i]->pos.y() <= p2Tmp.y()))
3675 selectRoom(i, false);
3679 // shift-dragging a room
3680 if (opInProgress == gmoShiftDragLeft)
3681 p2Tmp.rx() += roomWidth;
3682 else if (opInProgress == gmoShiftDragDown)
3683 p2Tmp.ry() += roomHeight;
3687 // shift-dragging a page
3688 if (opInProgress == gmoShiftDragLeft)
3689 p2Tmp.rx() += gridX;
3690 else if (opInProgress == gmoShiftDragRight)
3691 p2Tmp.rx() -= gridX;
3692 else if (opInProgress == gmoShiftDragUp)
3693 p2Tmp.ry() -= gridY;
3694 else if (opInProgress == gmoShiftDragDown)
3695 p2Tmp.ry() += gridY;
3698 for(i=doc->page.size()-1; i>=0; --i)
3700 rTmp = doc->getPageRect(i);
3702 if ((opInProgress == gmoShiftDragLeft && rTmp.right() < p2Tmp.x())
3703 || (opInProgress == gmoShiftDragRight && rTmp.left() > p2Tmp.x())
3704 || (opInProgress == gmoShiftDragUp && rTmp.top() > p2Tmp.y())
3705 || (opInProgress == gmoShiftDragDown && rTmp.bottom() < p2Tmp.y()))
3706 selectPage(i, false);
3712 p2Tmp.rx() = gridX * (point.x() / gridX - p1Tmp.x());
3713 p2Tmp.ry() = gridY * (point.y() / gridY - p1Tmp.y());
3714 computeSelectedRect(rTmp);
3715 // drawBackground(&dc);
3716 // dc.SetROP2(R2_NOT);
3717 // drawSelected(&dc);
3719 // end else if gmoShiftDown
3720 else if (opInProgress >= gmoDrag)
3724 // if (PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3725 // return; // The mouse has already been moved again
3727 QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3736 // dc.SetROP2(R2_NOT);
3737 // drawSelected(&dc);
3739 // drawSelected(&dc);
3743 // Maybe have more discriminant updating... Maybe.
3748 //void MapView::OnLButtonUp(unsigned int nFlags, QPoint point)
3749 void MapView::mouseReleaseEvent(QMouseEvent * event)
3751 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3752 // QPoint point = event->pos() * 5;
3753 QPointF ptf = event->localPos() * (100.0 / zoom);
3754 QPoint point = ptf.toPoint();
3755 // QPoint point = event->pos() * (100 / zoom);
3756 int nFlags = event->modifiers();
3762 if (event->button() == Qt::LeftButton)
3764 // if (GetCapture() != this)
3766 // if (selectedOne >= 0)
3767 // makeRoomVisible(selectedOne);
3773 // ReleaseCapture();
3775 // CClientDC dc(this);
3776 // OnPrepareDC(&dc);
3777 // dc.DPtoLP(&point);
3779 if (opInProgress == gmoAddEdge)
3781 // Erase the rubber band:
3782 // dc.SetROP2(R2_NOT);
3785 // if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3788 // QPen * oldPen = dc.SelectObject(&penEdge);
3789 // dc.MoveTo(p1Tmp);
3790 // dc.LineTo(p2Tmp);
3791 // dc.SelectObject(oldPen);
3793 // Find out where the edge goes:
3795 int room = doc->roomHit(point, &corner);
3797 if (corner != rcNone && room != edgeTmp.room1)
3799 // The edge goes somewhere
3800 // If there's a stub on the other end, delete it:
3801 EdgeVec deletedEdges;
3803 int oldEdgeNum = doc->findEdge(room, corner, oldEdge);
3805 if (oldEdgeNum >= 0 && (oldEdge.type2 & etUnexplored))
3807 // deletedEdges.push_back(doc->getEdge(oldEdgeNum));
3808 deletedEdges.push_back(doc->edge[oldEdgeNum]);
3809 doc->deleteEdge(oldEdgeNum);
3812 // Add the new or changed edge:
3813 if (edgeTmp.type2 & etOneWay)
3815 edgeTmp.room2 = edgeTmp.room1;
3816 edgeTmp.end2 = edgeTmp.end1;
3817 edgeTmp.type1 = edgeTmp.type2 | (edgeTmp.type1 & etObstructed);
3818 edgeTmp.type2 = etNormal;
3819 edgeTmp.room1 = room;
3820 edgeTmp.end1 = corner;
3824 edgeTmp.room2 = room;
3825 edgeTmp.end2 = corner;
3829 deletedEdges.push_back(e2Tmp);
3831 if (deletedEdges.size())
3833 if (deletedEdges.size() == 1)
3834 doc->setUndoData(new UndoChangeEdge(bTmp, deletedEdges[0]));
3836 doc->setUndoData(new UndoChanges(bTmp, -1, 1, deletedEdges));
3839 doc->setUndoData(new UndoAdd(bTmp));
3841 doc->addEdge(edgeTmp);
3847 // We just deleted the old edge
3848 if ((e2Tmp.end1 == rcCorner)
3849 || (!(e2Tmp.type1 & etNoRoom2) && (e2Tmp.end2 == rcCorner)))
3851 doc->addEdge(e2Tmp); // Put it back temporarily
3852 selectOnlyRoom((e2Tmp.end1 == rcCorner) ? e2Tmp.room1 : e2Tmp.room2);
3853 deleteSelection(false); // Remove entire connection
3856 // not a connection to a corner
3857 doc->setUndoData(new UndoDelete(bTmp, e2Tmp));
3859 else if (selectedOne >= 0)
3860 makeRoomVisible(selectedOne); // We just clicked on a room corner
3862 // end if edge doesn't go anywhere
3864 else if (opInProgress == gmoChangeEdge)
3866 // We didn't change the edge
3867 if (selectedOne >= 0)
3868 makeRoomVisible(selectedOne);
3870 // else if (opInProgress == gmoSelectBox)
3872 // dc.SetROP2(R2_NOT);
3873 // QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3874 // dc.Rectangle(rTmp);
3875 // dc.SelectObject(oldBrush);
3877 else if (opInProgress == gmoControlDown)
3888 // end if room or page was already selected
3890 else if (opInProgress >= gmoDrag)
3892 QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3897 const QSize offset1(p.x(), p.y());
3898 doc->setUndoData(new UndoMove(doc->isDirty, offset1, numSelected, selected, numSelectedPages, selectedPage));
3900 for(int i=doc->room.size()-1; i>=0; i--)
3903 doc->moveRoom(i, offset1);
3906 for(int i=doc->page.size()-1; i>=0; i--)
3908 if (selectedPage[i])
3909 doc->movePage(i, offset1);
3913 // InvalidateRect(NULL);
3916 opInProgress = gmoNone;
3918 else if (event->button() == Qt::RightButton)
3920 //void MapView::OnRButtonUp(unsigned int nFlags, QPoint point)
3922 // if (scrollDragTimer)
3925 // KillTimer(scrollDragTimer);
3926 scrollDragTimer = 0;
3929 int room = doc->roomHit(point, &corner);
3930 cornerClicked = corner; // Bleah
3931 roomClicked = room; // Bleah
3935 if (showPages && (room = doc->pageBorderHit(point)) >= 0)
3942 mapContextMenu->popup(mapToGlobal(event->pos()));
3946 selectOnlyRoom(room);
3947 opInProgress = gmoNone;
3949 if ((corner != rcNone) && !doc->locked)
3951 iTmp = doc->findEdge(room, corner, edgeTmp);
3953 if ((iTmp >= 0) && !(edgeTmp.type2 & etNoRoom2))
3955 opInProgress = gmoAddCorner;
3956 addCornerAct->setEnabled(true);
3959 addCornerAct->setEnabled(false);
3962 roomContextMenu->popup(mapToGlobal(event->pos()));
3963 mouse = QPoint(0, 0);
3966 // dc.LPtoDP(&point);
3967 // ClientToScreen(&point);
3968 // pop->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
3969 // //opInProgress = gmoNone; // FIXME
3971 // else if (!scrollDrag || (GetCapture() != this))
3972 // CScrollZoomView::OnRButtonUp(nFlags, point);
3975 // if (opInProgress == gmoNone)
3976 // ReleaseCapture();
3978 // ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3980 // scrollDrag = false;
3990 int MapView::OnMouseActivate(CWnd * wnd, unsigned int hitTest, unsigned int message)
3992 const int result = CScrollZoomView::OnMouseActivate(wnd, hitTest, message);
3994 if ((result == MA_ACTIVATE) && GUEmapEatClicks)
3995 return MA_ACTIVATEANDEAT;
4001 bool MapView::OnMouseWheel(unsigned int nFlags, short zDelta, QPoint pt)
4003 if (nFlags == MK_CONTROL)
4005 OnViewZoom((zDelta > 0) ? ID_VIEW_ZOOM_IN : ID_VIEW_ZOOM_OUT);
4009 return CScrollZoomView::OnMouseWheel(nFlags, zDelta, pt);
4013 void MapView::OnSize(unsigned int nType, int cx, int cy)
4015 CScrollZoomView::OnSize(nType, cx, cy);
4017 if (initialized()) // Make sure view has been initialized
4022 void MapView::OnTimer(unsigned int idEvent)
4024 if (idEvent == menuTimer)
4026 // We've held the button down long enough, switch to dragging:
4028 KillTimer(scrollDragTimer);
4029 scrollDragTimer = 0;
4031 ::SetCursor(handCursor);
4034 CScrollZoomView::OnTimer(idEvent);
4039 // Update the view after the document changes:
4044 // A room has just been deleted
4046 // The number of pages has changed
4048 // A room comment was just changed
4049 // Do not invalidate the view unless pHint is not NULL.
4050 // dupNavigationMode:
4051 // We are entering navigation mode
4052 // Ignore pHint, do not invalidate the view, just make sure
4053 // exactly one room is selected and that it's visible.
4055 // NULL means invalidate the entire view (but see lHint)
4056 // Otherwise, it's a QRect* to the area to invalidate
4058 void MapView::OnUpdate(CView * pSender, LPARAM lHint, CObject * pHint)
4060 if (lHint == dupDeletedRoom && opInProgress != gmoDeleting && numSelected)
4062 // Clear selection unless this is the view doing the deleting:
4066 else if (lHint == dupPageCount && opInProgress != gmoDeleting)
4068 selectedPage.resize(GetDocument()->getPages().size(), 0);
4070 if (numSelectedPages)
4076 else if (lHint == dupRoomComment)
4078 // Update comment for this view, but don't override any other view:
4079 static_cast<CMapApp*>(AfxGetApp())->setComment(this,
4080 (selectedOne >= 0 ? &(GetDocument()->getRoom(selectedOne)) : NULL), false);
4083 return; // Don't invalidate entire view
4086 if (lHint == dupNavigationMode)
4088 const MapDoc * doc = GetDocument();
4090 const int numRooms = doc->room.size();
4092 if (selectedOne < 0 || doc->room[selectedOne].isCorner())
4095 makeRoomVisible(selectedOne);
4096 } // end if switching to navigation mode
4099 QRect rect(*reinterpret_cast<const QRect *>(pHint));
4103 rect.NormalizeRect();
4104 InvalidateRect(rect);
4106 else if (lHint != dupRoomComment)
4111 void MapView::OnUpdateEditAddCorner(CCmdUI* pCmdUI)
4113 pCmdUI->Enable(opInProgress == gmoAddCorner);
4117 void MapView::OnUpdateEditPaste(CCmdUI* pCmdUI)
4119 pCmdUI->Enable((static_cast<CMapApp*>(AfxGetApp())->getClipboard() != NULL)
4120 && !GetDocument()->locked);
4125 // Commands which require selected rooms and unlocked document:
4130 // Edit|Select connected
4132 void MapView::OnUpdateSelectedUnlocked(CCmdUI * pCmdUI)
4134 MapDoc * const doc = GetDocument();
4137 bool selected = numSelected;
4139 if ((pCmdUI->m_nID != ID_EDIT_SELECT_CONNECTED) && numSelectedPages)
4141 if ((pCmdUI->m_nID != ID_EDIT_COPY)
4142 && (doc->page.size() == numSelectedPages))
4143 selected = false; // Can't delete all pages
4148 pCmdUI->Enable(selected && !doc->locked);
4153 // Commands which require the document to be unlocked:
4157 void MapView::OnUpdateUnlocked(CCmdUI* pCmdUI)
4159 pCmdUI->Enable(!GetDocument()->locked);
4164 // Toggle the grid on and off:
4166 void MapView::OnViewGrid()
4168 showGrid = !showGrid;
4169 InvalidateRect(NULL);
4173 void MapView::OnUpdateViewGrid(CCmdUI * pCmdUI)
4175 pCmdUI->SetCheck(showGrid);
4179 void MapView::OnViewZoom(unsigned int cmd)
4181 ASSERT((cmd == ID_VIEW_ZOOM_IN) || (cmd == ID_VIEW_ZOOM_OUT));
4183 if (cmd == ID_VIEW_ZOOM_OUT)
4184 zoomTo(zoom % 10 ? zoom - zoom % 10 : zoom - 10);
4186 zoomTo(zoom + 10 - zoom % 10);
4190 void MapView::OnUpdateViewZoom(CCmdUI * pCmdUI)
4192 pCmdUI->Enable((pCmdUI->m_nID == ID_VIEW_ZOOM_IN)
4193 ? (zoom < maxZoom) : (zoom > minZoom));
4198 // Redraw the window:
4200 void MapView::OnWindowRefresh()
4202 InvalidateRect(NULL);
4206 /////////////////////////////////////////////////////////////////////////////
4207 /////////////////////////////////////////////////////////////////////////////
4208 // CRepaginateDlg dialog
4210 CRepaginateDlg::CRepaginateDlg(): CDialog(CRepaginateDlg::IDD, NULL)
4212 //{{AFX_DATA_INIT(CRepaginateDlg)
4216 BEGIN_MESSAGE_MAP(CRepaginateDlg, CDialog)
4217 //{{AFX_MSG_MAP(CRepaginateDlg)
4218 ON_BN_CLICKED(IDYES, OnYes)
4222 /////////////////////////////////////////////////////////////////////////////
4223 // CRepaginateDlg message handlers
4226 bool CRepaginateDlg::OnInitDialog()
4228 CDialog::OnInitDialog();
4230 SendDlgItemMessage(IDC_EXCLAMATION, STM_SETICON, (WPARAM) gueApp()->LoadStandardIcon(IDI_EXCLAMATION));
4231 MessageBeep(MB_ICONEXCLAMATION);
4233 return true; // return TRUE unless you set the focus to a control
4237 void CRepaginateDlg::OnYes()