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 }
429 cantGoAngleX = gridY / 6,
430 cantGoAngleY = gridX / 6;
433 rcNE, rcNW, rcSE, rcSW,
434 rcNNE, rcNNW, rcSSE, rcSSW,
436 QRect cornerRect[] = {
437 { roomWidth * 5 / 16, 0, roomWidth * 6 / 16, roomHeight / 4 },
438 { roomWidth * 5 / 16, roomHeight * 3 / 4, roomWidth * 6 / 16, roomHeight / 4 },
439 { roomWidth * 3 / 4, roomHeight / 4, roomWidth / 4, roomHeight / 2 },
440 { 0, roomHeight / 4, roomWidth / 4, roomHeight / 2 },
441 { roomWidth * 13 / 16, 0, roomWidth * 3 / 16, roomHeight / 4 },
442 { 0, 0, roomWidth * 3 / 16, roomHeight / 4 },
443 { roomWidth * 13 / 16, roomHeight * 3 / 4, roomWidth * 3 / 16, roomHeight / 4 },
444 { 0, roomHeight * 3 / 4, roomWidth * 3 / 16, roomHeight / 4 },
445 { roomWidth * 11 / 16, 0, roomWidth * 2 / 16, roomHeight / 4 },
446 { roomWidth * 3 / 16, 0, roomWidth * 2 / 16, roomHeight / 4 },
447 { roomWidth * 11 / 16, roomHeight * 3 / 4, roomWidth * 2 / 16, roomHeight / 4 },
448 { roomWidth * 3 / 16, roomHeight * 3 / 4, roomWidth * 2 / 16, roomHeight / 4 },
451 QPainter painter(this);
452 QPainter * dc = &painter;
454 painter.setRenderHint(QPainter::Antialiasing);
459 dc->setPen(QColor(0xFF, 0x7F, 0x00));
460 // dc->drawText(20, 50, QString("<%1, %2>").arg(mouse.x()).arg(mouse.y()));
461 dc->drawText(20, 50, QString("<%1, %2>, %3").arg(mouse.x()).arg(mouse.y()).arg(hoveredEdge));
466 t1.scale(zoom / 100.0, zoom / 100.0);
467 // t1.scale(+0.20, +0.20);
468 // t1.scale(+0.40, +0.40);
469 t1.translate(offset.x(), offset.y());
470 painter.setTransform(t1);
472 // QPainter painter(this);
474 painter.setPen(QPen(Qt::blue, 1, Qt::DashLine));
475 painter.drawRect(0, 0, 100, 100);
477 QTransform transform;
478 transform.translate(50, 50);
479 transform.rotate(45);
480 transform.scale(0.5, -1.0);
481 painter.setTransform(transform);
483 painter.setFont(QFont("Helvetica", 24));
484 painter.setPen(QPen(Qt::black, 1));
485 painter.drawText(20, 10, "QTransform");
495 lb.lbStyle = BS_SOLID;
496 lb.lbColor = RGB(0, 0, 0);
498 if (!focusBrush.CreateHatchBrush(HS_BDIAGONAL, PALETTERGB(128, 128, 128))
499 || !penEdge.CreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT, penEdgeWidth, &lb)
500 || !penGrid.CreatePen(PS_DOT, 0, RGB(0, 0, 0))
501 || !penRoom.CreatePen(PS_SOLID, penRoomWidth, RGB(0, 0, 0)))
504 QBrush * const whiteBrush = dc->GetCurrentBrush();
505 QPen * const oldPen = dc->GetCurrentPen();
506 QFont * const oldFont = dc->SelectObject(&font);
507 const unsigned int oldAlign = dc->SetTextAlign(TA_CENTER | TA_BASELINE | TA_NOUPDATECP);
509 QBrush focusBrush(QColor(0xC0, 0xC0, 0xC0), Qt::BDiagPattern);
510 QPen penEdge(QColor(0, 0, 0), penEdgeWidth);
511 QPen penEdgeDashed(QColor(0, 0, 0), penEdgeWidth, Qt::DashLine);
512 QPen penEdgeHover(QColor(0xFF, 0, 0), penEdgeWidth);
513 QPen penEdgeDashedHover(QColor(0xFF, 0, 0), penEdgeWidth, Qt::DashLine);
514 QPen penGrid(QColor(0, 0, 0), 1, Qt::DotLine);
515 QPen penRoom(QColor(0, 0, 0), 1);
521 // Clipping in OnPrepareDC screws up print preview
522 QRect r = doc->getPageRect(printingPage - 1);
523 /// dc->SetWindowOrg(r.left(), r.bottom()); // FIXME adjust for margins
524 /// dc->IntersectClipRect(&r);
525 // dc->setTransform(QTransform::translate(r.left(), r.bottom()));
529 // Draw the page outlines:
530 if ((showPages || numSelectedPages) && !dc->IsPrinting())
532 dc->SetBkMode(TRANSPARENT);
534 lb.lbColor = RGB(192, 192, 192);
535 VERIFY(penPage.CreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT, penPageWidth, &lb));
536 dc->SelectObject(&penPage);
537 const PageVec & pageVec = doc->page;
540 dc->SelectStockObject(NULL_BRUSH);
541 QBrush * const nullBrush = dc->GetCurrentBrush();
542 bool focused = false;
544 for(PageConstItr p=pageVec.begin(); p!=pageVec.end(); ++p, ++pNum)
546 doc->getPageRect(*p, r);
548 if (dc->RectVisible(r))
550 const bool selectedI = selectedPage[pNum - 1] != 0;
552 if (!selectedI && !showPages)
553 continue; // Only show selected pages
555 if (selectedI != focused)
558 dc->SelectObject(focused ? &focusBrush : nullBrush);
560 /// CRgn inside,outside;
561 /// VERIFY(outside.CreateRectRgnIndirect(&r));
562 /// VERIFY(inside.CreateRectRgn(r.left + gridX, r.bottom - gridY,
563 /// r.right - gridX, r.top + gridY));
564 /// outside.CombineRgn(&outside, &inside, RGN_DIFF);
565 /// dc->FillRgn(&outside, &focusBrush);
567 dc->Rectangle(r.left + penPageWidth / 4, r.bottom - penPageWidth / 4, r.right, r.top);
569 _itot(pNum, pageNum, 10);
570 const int pageNumL = strlen(pageNum);
572 for(i=r.left+3*gridX/2; i<r.right-gridX; i+=gridX)
574 dc->TextOut(i, r.bottom - gridY * 7 / 8, pageNum, pageNumL);
575 dc->TextOut(i, r.top + gridY / 4, pageNum, pageNumL);
578 for(i=r.bottom-gridY*15/8; i>r.top+gridY; i-=gridY)
580 dc->TextOut(r.left + gridX / 2 + penPageWidth / 4, i, pageNum, pageNumL);
581 dc->TextOut(r.right- gridX / 2 - penPageWidth / 4, i, pageNum, pageNumL);
583 } // end if page visible
586 dc->SetBkMode(OPAQUE);
587 dc->SelectObject(whiteBrush);
588 dc->SelectObject(&penEdge); // Before destroying penPage
590 else // not showing pages
591 dc->SelectObject(&penEdge);
596 // Draw the background grid:
597 if (showGrid)// && (zoom >= minZoomGrid))//&& !dc->IsPrinting())
600 printf(" Preparing to draw grid...\n");
601 // dc->SetBkMode(TRANSPARENT);
602 // dc->SelectObject(&penGrid);
603 dc->setBrush(Qt::NoBrush);
607 // dc->GetClipBox(&clip);
608 // clip = dc->viewport();
609 // clip = dc->window();
611 printf(" clip = %i, %i, %i, %i\n", clip.x(), clip.y(), clip.width(), clip.height());
613 clip.setLeft(clip.left() - (clip.left() % gridX));
614 clip.setBottom(clip.bottom() - (clip.bottom() % gridY + gridY));
616 if (clip.top() < -gridY)
617 clip.setTop(clip.top() + gridY);
619 QSize docSize(doc->getDocSize());
620 docSize.setHeight(docSize.height() * -1);
622 if (clip.right() > docSize.width())
623 clip.setRight(docSize.width());
625 if (clip.bottom() < docSize.height())
626 clip.setBottom(docSize.height());
628 printf(" clip = %i, %i, %i, %i; docSize = %i, %i\n", clip.left(), clip.right(), clip.bottom(), clip.top(), docSize.width(), docSize.height());
629 for(i=clip.left(); i<=clip.right(); i+=gridX)
631 dc->drawLine(i, 0, i, docSize.height());
634 // for(i=clip.bottom(); i<=clip.top(); i+=gridY)
635 for(i=-clip.bottom(); i<=clip.top(); i+=gridY)
637 // dc->drawLine(0, i, docSize.width(), i);
638 dc->drawLine(0, i, docSize.width(), i);
641 // dc->SetBkMode(OPAQUE);
643 dc->setBrush(Qt::NoBrush);
645 QSize docSize(doc->docSize);
647 for(int i=0; i<=docSize.width(); i+=gridX)
648 dc->drawLine(i, 0, i, docSize.height());
650 for(int i=0; i<=docSize.height(); i+=gridY)
651 dc->drawLine(0, i, docSize.width(), i);
655 // Draw the room connections
656 int i = doc->edge.size();
657 EdgeConstItr edge = doc->edge.end() - 1;
658 RoomConstItr room = doc->room.v.begin();
665 doc->getEdgePoints(*edge, start, end);
667 // Offset the edge if we're dragging it...
668 if (opInProgress >= gmoDrag)
670 if (selected[edge->room1])
673 if ((selected[edge->room1] && (edge->type1 & etNoRoom2))
674 || selected[edge->room2])
678 if (edge->type1 & etObstructed)
679 dc->setPen(hoveredEdge == i ? penEdgeDashedHover : penEdgeDashed);
681 dc->setPen(hoveredEdge == i ? penEdgeHover : penEdge);
683 dc->drawLine(start, end);
685 if (edge->type1 & etOneWay)
688 dc->setBrush(Qt::black);
689 DrawArrowhead(dc, end, start);
692 else if (edge->type1 & etNoExit)
694 DrawNoExit(dc, end, start);
696 else if (edge->type1 & etLoopBack)
698 const double angle = Angle(start - end);
699 const double orthoAngle = angle + QTR_TAU;
700 const double size = 5.0;
702 QPointF ortho(cos(orthoAngle), sin(orthoAngle));
703 QPointF unit = UnitVector(end - start);
705 QPointF p1 = start + (ortho * 6.0 * size);
706 QPointF p2 = end + (ortho * 6.0 * size);
707 QPointF p3 = end + (ortho * 3.0 * size);
708 p3.rx() -= 3.0 * size;
709 p3.ry() -= 3.0 * size;
710 dc->drawLine(p1, p2);
711 QRectF r(p3, QSizeF(6.0 * size, 6.0 * size));
712 // 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. :-/
713 QPoint funnyStart(start.x(), start.y() * -1), funnyEnd(end.x(), end.y() * -1);
714 double funnyAngle = Angle(funnyStart - funnyEnd);
715 dc->drawArc(r, (int)((funnyAngle + QTR_TAU) * RADIANS_TO_DEGREES) * 16, 180 * 16);
717 dc->setBrush(Qt::black);
718 DrawArrowhead(dc, p1, p2);
724 if (edge->type1 & etDirection)
726 const QPoint & p = room[edge->room1]->pos;
727 // dc->TextOut(p.x + edgLblXOffset[edge->end1], p.y + edgLblYOffset[edge->end1], &edgeLabel[(edge->type1 & etDirection) - 1], 1);
728 elBuf[0] = edgeLabel[(edge->type1 & etDirection) - 1];
729 dc->drawText(p + edgLblOffset[edge->end1], elBuf);
732 if (edge->type2 & etDirection)
734 const QPoint & p = room[edge->room2]->pos;
735 // dc->TextOut(p.x + edgLblXOffset[edge->end2], p.y + edgLblYOffset[edge->end2], &edgeLabel[(edge->type2 & etDirection) - 1], 1);
736 elBuf[0] = edgeLabel[(edge->type2 & etDirection) - 1];
737 dc->drawText(p + edgLblOffset[edge->end2], elBuf);
745 bool focused = false;
748 for(const int count=doc->room.size(); i<count; i++, room++)
750 QRect r = (*room)->getRect();
751 QPoint cp = (*room)->GetCenter();
753 // Translate room if dragging operation in progress
754 if ((opInProgress >= gmoDrag) && selected[i])// && !(*room)->isCorner())
756 r.translate(p2Tmp.x(), p2Tmp.y());
760 // if (!dc->RectVisible((*room)->getRect(r)))
761 // if (!r.intersects(dc->viewport()))
764 const bool selectedI = (selected[i] != 0);
766 if (selectedI != focused)
769 bool hovered = r.contains(mouse);
770 RoomCorner rc = rcNone;
773 rc = doc->CornerHit(mouse - r.topLeft());
775 dc->setPen(hovered ? penEdgeHover : penRoom);
777 dc->setBrush(focused ? focusBrush : Qt::white);
779 bool needOpaqueText = false;
781 if ((*room)->flags & rfBorder)
787 dc->setBrush(focusBrush);
790 else // Room has no border and is not focused
791 needOpaqueText = !(*room)->name.empty();
793 if ((*room)->isCorner() && (focused || showCorners))
795 QRect centerRect(cp - QPoint(20, 20), QSize(40, 40));
797 dc->setPen(Qt::black);
798 dc->setBrush(Qt::black);
799 dc->drawEllipse(centerRect);
803 // dc->SetBkMode(TRANSPARENT);
805 if ((rc >= 0) && (rc <= 11) && ((*room)->isCorner() == false))
807 QRect rCrnr = cornerRect[rc].translated(r.topLeft());
808 // rCrnr.translate(r.topLeft());
810 dc->setPen(Qt::NoPen);
811 dc->setBrush(Qt::green);
816 if (!(*room)->note.empty())
818 QPoint noteOffset(roomWidth - 29, 57);
821 dc->setPen(QColor(0xFF, 0x00, 0x00));
822 dc->drawText(r.topLeft() + noteOffset, "*");
826 // if (needOpaqueText)
827 // dc->SetBkMode(OPAQUE);
829 // Shrink the rect left/right margins just a bit
830 QMargins margin(20, 0, 20, 0); // LTRB
831 r = r.marginsRemoved(margin);
833 //dc->setPen(Qt::blue);
837 dc->drawText(r, Qt::AlignCenter | Qt::TextWordWrap, (*room)->name.c_str());
839 // dc->SetBkMode(OPAQUE);
842 // JLH: Draw special stuffs (crap that was stuffed into OnMouseDown & OnMouseMove & the like...
843 if (opInProgress == gmoSelectBox)
845 dc->setPen(QPen(QColor(0x00, 0xFF, 0x00, 0xFF)));
846 dc->setBrush(QBrush(QColor(0x00, 0xFF, 0x00, 0x64)));
849 else if (opInProgress == gmoAddEdge)
852 dc->drawLine(p1Tmp, p2Tmp);
857 void MapView::keyPressEvent(QKeyEvent * event)
859 bool oldShift = shiftDown;
860 bool oldCtrl = ctrlDown;
861 bool oldAlt = altDown;
863 if (event->key() == Qt::Key_Shift)
865 else if (event->key() == Qt::Key_Control)
867 else if (event->key() == Qt::Key_Alt)
870 double oldZoom = zoom;
872 if (event->key() == Qt::Key_0)
874 else if (event->key() == Qt::Key_9)
876 else if (event->key() == Qt::Key_8)
878 else if (event->key() == Qt::Key_7)
880 else if (event->key() == Qt::Key_6)
882 else if (event->key() == Qt::Key_5)
884 else if (event->key() == Qt::Key_4)
886 else if (event->key() == Qt::Key_3)
888 else if (event->key() == Qt::Key_2)
890 else if (event->key() == Qt::Key_1)
897 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
900 ToolHandler(ToolKeyDown, Point(0, 0));
906 if (oldAlt != altDown)
909 setCursor(Qt::SizeAllCursor);
910 // oldPoint = oldScrollPoint;
914 if (select.size() > 0)
916 if (event->key() == Qt::Key_Up)
918 TranslateObjects(select, Point(0, +1.0));
921 else if (event->key() == Qt::Key_Down)
923 TranslateObjects(select, Point(0, -1.0));
926 else if (event->key() == Qt::Key_Right)
928 TranslateObjects(select, Point(+1.0, 0));
931 else if (event->key() == Qt::Key_Left)
933 TranslateObjects(select, Point(-1.0, 0));
941 void MapView::keyReleaseEvent(QKeyEvent * event)
943 bool oldShift = shiftDown;
944 bool oldCtrl = ctrlDown;
945 bool oldAlt = altDown;
947 if (event->key() == Qt::Key_Shift)
949 else if (event->key() == Qt::Key_Control)
951 else if (event->key() == Qt::Key_Alt)
955 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
958 ToolHandler(ToolKeyUp, Point(0, 0));
964 if (oldAlt != altDown)
967 setCursor(Qt::ArrowCursor);
973 /////////////////////////////////////////////////////////////////////////////
975 //--------------------------------------------------------------------
976 // Record which page we're printing, if any:
981 void MapView::OnPrepareDC(QPainter * dc, CPrintInfo * pInfo)
983 CScrollZoomView::OnPrepareDC(dc, pInfo);
986 printingPage = pInfo->m_nCurPage;
992 bool MapView::OnPreparePrinting(CPrintInfo * pInfo)
994 // Require registration before printing map with more than 10 rooms:
995 MapDoc * const doc = GetDocument();
998 if (doc->needRepaginate())
1002 switch (d.DoModal())
1012 pInfo->SetMaxPage(doc->page.size());
1013 return DoPreparePrinting(pInfo);
1017 void MapView::OnBeginPrinting(QPainter * /*pDC*/, CPrintInfo * /*pInfo*/)
1019 // Don't show selection or corners while printing:
1020 // We do this here instead of OnPreparePrinting, because OnEndPrinting
1021 // is only guaranteed to be called if OnBeginPrinting is.
1024 showCorners = false;
1028 void MapView::OnEndPrinting(QPainter* /*pDC*/, CPrintInfo* /*pInfo*/)
1030 if (GetDocument()->locked)
1031 OnUpdate(NULL, dupNavigationMode, NULL); // Select a room
1037 // CMapView miscellaneous
1042 // point: The point where the mouse was clicked (in logical coordinates)
1044 void MapView::addRoom(QPoint & point)
1046 QSize docSize(doc->docSize);
1048 if (doc->locked || (point.x() > docSize.width()) || (point.y() > docSize.height()))
1050 // MessageBeep(MB_ICONASTERISK);
1054 point.rx() -= roomWidth / 2 - gridX / 2;
1055 point.rx() -= point.x() % gridX;
1056 point.ry() -= roomHeight / 2 - gridY / 2;
1057 point.ry() -= point.y() % gridY;
1061 else if (point.x() + roomWidth > docSize.width())
1062 point.setX(docSize.width() - roomWidth);
1066 else if (point.y() + roomHeight > docSize.height())
1067 point.setY(docSize.height() - roomHeight);
1070 const bool wasModified = doc->isDirty;
1071 const int rNum = doc->addRoom(point);
1076 selectDone(); // FIXME disable floating comments
1077 editProperties(epuAddRoom, wasModified);
1082 // Make sure we aren't dragging a room off the edge:
1085 // p: The proposed offset for the selected rooms
1088 // rTmp: Rectangle enclosing the original positions of the selected rooms
1091 // p: The corrected (if necessary) offset for selected rooms
1093 void MapView::adjustOffset(QPoint & p) const
1095 const QSize size(doc->docSize);
1097 if (p.x() + rTmp.left() < 0)
1098 p.rx() = -rTmp.left();
1100 if (p.x() + rTmp.right() > size.width())
1101 p.rx() = size.width() - rTmp.right();
1103 if (p.y() + rTmp.top() < 0)
1104 p.ry() = -rTmp.top();
1106 if (p.y() + rTmp.bottom() > size.height())
1107 p.ry() = size.height() - rTmp.bottom();
1112 // Deselect all rooms:
1115 // update: TRUE means the selected rooms should be redrawn
1117 void MapView::clearSelection(bool update/* = true*/)
1119 if (doc->room.size() != selected.size())
1120 selected.resize(doc->room.size(), 0);
1122 if (update && numSelected)
1128 RoomConstItr room = doc->room.getVector().begin();
1130 for(int i=doc->room.size()-1; numSelected && i>=0; --i)
1135 room[i]->getRect(r);
1146 for(ByteItr b=selected.begin(); b!=selected.end(); ++b)
1149 deselectPages(update);
1154 // Compute a rectangle enclosing all selected rooms:
1163 // r: A rectangle enclosing all selected rooms
1165 void MapView::computeSelectedRect(QRect & r) const
1167 // r.SetRectEmpty();
1172 RoomConstItr room = doc->room.getVector().begin();
1174 for(int i=doc->room.size()-1; i>=0; i--)
1177 r |= room[i]->getRect();
1181 if (numSelectedPages)
1183 for(int i=doc->page.size()-1; i>=0; i--)
1185 if (selectedPage[i])
1186 r |= doc->getPageRect(i);
1196 // n: The page number to be deselected
1197 // update: TRUE means the page should be redrawn if necessary
1199 // Output Variables:
1204 void MapView::deselectPage(int n, bool update/* = true*/)
1206 ASSERT((n >= 0) && (n < doc->page.size()));
1208 if (selectedPage[n])
1210 selectedPage[n] = false;
1212 if ((--numSelectedPages == 0) && (numSelected == 1))
1214 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1215 ; // Find the selected room
1226 doc->getPageRect(n, r);
1237 // Deselect all pages:
1240 // update: TRUE means the selected pages should be redrawn
1242 // Output Variables:
1247 void MapView::deselectPages(bool update/* = true*/)
1249 ASSERT(selectedPage.size() == doc->page.size());
1251 if (update && numSelectedPages)
1254 // CClientDC dc(this);
1255 // OnPrepareDC(&dc);
1257 for(int i=doc->page.size()-1; numSelectedPages&&i>=0; i--)
1259 if (selectedPage[i])
1262 r = doc->getPageRect(i);
1266 // make sure it disappears
1267 r.setBottom(r.bottom() + penPageWidth);
1268 r.setLeft(r.left() - penPageWidth);
1272 // r.NormalizeRect();
1273 // InvalidateRect(r);
1278 numSelectedPages = 0;
1280 for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
1283 if (numSelected == 1)
1285 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1286 ; // Find the selected room
1297 // n: The room number to be deselected
1298 // update: TRUE means the room should be redrawn if necessary
1300 // Output Variables:
1305 void MapView::deselectRoom(RoomNum n, bool update/* = true*/)
1307 ASSERT((n >= 0) && (n < doc->room.size()));
1309 if (doc->room.size() != selected.size())
1310 selected.resize(doc->room.size(), 0);
1314 selected[n] = false;
1316 if ((--numSelected == 1) && !numSelectedPages)
1318 for (selectedOne=0; !selected[selectedOne]; selectedOne++)
1319 ; // Find the selected room
1330 doc->room[n].getRect(r);
1341 // Update the room comments dialog after the selection changes:
1343 void MapView::selectDone()
1345 // static_cast<CMapApp *>(AfxGetApp())->setComment(this, (selectedOne >= 0 ? &(GetDocument()->getRoom(selectedOne)) : NULL));
1353 // n: The page number to select
1354 // update: TRUE means the selected page should be redrawn if necessary
1356 // Output Variables:
1361 void MapView::selectPage(int n, bool update/* = true*/)
1363 if (!selectedPage[n])
1365 selectedPage[n] = true;
1375 doc->getPageRect(n, r);
1389 // n: The room number to select
1390 // update: TRUE means the selected room should be redrawn if necessary
1392 // Output Variables:
1397 void MapView::selectRoom(RoomNum n, bool update/*= true*/)
1399 if (doc->room.size() != selected.size())
1400 selected.resize(doc->room.size(), 0);
1406 if ((++numSelected == 1) && !numSelectedPages)
1417 doc->room[n].getRect(r);
1427 void MapView::selectOnlyRoom(RoomNum n)
1429 if (selectedOne != n)
1439 // Make sure that a room is visible:
1442 // n: The number of the room to be made visible
1444 void MapView::makeRoomVisible(RoomNum n)
1448 GetClientRect(&clientRect);
1449 QSize viewSize(clientRect.right, clientRect.bottom);
1451 CClientDC cdc(this);
1453 cdc.DPtoLP(&viewSize);
1455 const QSize docSize(doc->getDocSize());
1456 const QPoint curPos(getScrollPosition());
1458 doc->room[n].getRect(roomRect);
1460 QPoint newPos(curPos);
1462 if (roomRect.left - curPos.x < gridX)
1464 newPos.x -= curPos.x - roomRect.left + gridX;
1469 else if (roomRect.right - curPos.x + gridX > viewSize.cx)
1471 newPos.x += roomRect.right + gridX - viewSize.cx - curPos.x;
1473 if (newPos.x + viewSize.cx > docSize.cx)
1474 newPos.x = docSize.cx - viewSize.cx;
1477 if (curPos.y - roomRect.bottom < gridY)
1479 newPos.y += roomRect.bottom + gridY - curPos.y;
1484 else if (curPos.y - roomRect.top + gridY > viewSize.cy)
1486 newPos.y += roomRect.top + viewSize.cy - curPos.y - gridY;
1488 if (viewSize.cy - newPos.y > docSize.cy)
1489 newPos.y = viewSize.cy - docSize.cy;
1492 if (newPos != curPos)
1494 scrollToPosition(newPos);
1495 // Must adjust the room position because the DC won't be updated:
1496 roomRect.OffsetRect(curPos.x - newPos.x, curPos.y - newPos.y);
1499 // Make sure the floating comments dialog isn't in the way:
1500 cdc.LPtoDP(&roomRect);
1501 ClientToScreen(&roomRect);
1502 roomRect.NormalizeRect();
1503 static_cast<CMapApp *>(AfxGetApp())->adjustCommentPos(roomRect);
1509 // Paste a string to the clipboard:
1512 // text: The string to paste
1515 // True: String was successfully pasted
1516 // False: Could not paste (clipboard unavailable or out of memory)
1518 bool MapView::pasteClipboard(const string & text)
1521 if (!OpenClipboard())
1526 // Allocate a global memory object for the text
1527 HGLOBAL mem = GlobalAlloc(GMEM_DDESHARE, text.length() + sizeof(TCHAR));
1535 // Lock the handle and copy the text to the buffer
1537 strcpy(LPTSTR(GlobalLock(mem)), text.c_str());
1540 SetClipboardData(CFtrEXT, mem); // Place the handle on the clipboard
1549 // Update the selected room's comment from the floating dialog:
1552 // comment: The new room comment (must not be NULL)
1554 void MapView::setRoomNote(const char * comment)
1556 ASSERT(selectedOne >= 0);
1558 if (doc->room[selectedOne].note != comment)
1560 doc->setUndoData(new UndoRoomInfo(*doc, selectedOne));
1561 doc->setRoomNote(selectedOne, comment);
1566 int MapView::FindHoveredEdge(void)
1568 for(int i=0; i<doc->edge.size(); i++)
1572 doc->getEdgePoints(doc->edge[i], ep1, ep2);
1574 QPointF lineSegment = ep2 - ep1;
1575 QPointF v1 = mouse - ep1;
1576 QPointF v2 = mouse - ep2;
1577 double t = ParameterOfLineAndPoint(ep1, ep2, mouse);
1581 distance = Magnitude(v1);
1583 distance = Magnitude(v2);
1585 // distance = ?Det?(ls, v1) / |ls|
1586 distance = fabs(Determinant(lineSegment, v1)
1587 / Magnitude(lineSegment));
1589 if (distance < 20.0)
1593 // Didn't find a closely hovered edge
1599 // Set the step size of the scroll bars:
1601 void MapView::setScrollBars()
1605 GetClientRect(&clientRect);
1606 QSize viewSize(clientRect.right, clientRect.bottom);
1608 CClientDC cdc(this);
1610 cdc.DPtoLP(&viewSize);
1612 viewSize.cx -= gridX / 2;
1613 viewSize.cx -= viewSize.cx % gridX + roomWidth;
1614 viewSize.cx = max(viewSize.cx, gridX);
1616 viewSize.cy -= gridY / 2;
1617 viewSize.cy -= viewSize.cy % gridY + roomHeight;
1618 viewSize.cy = max(viewSize.cy, gridY);
1620 setPageStep(viewSize);
1626 // Zoom the display:
1629 // newZoom: The new zoom factor (in percent)
1631 void MapView::zoomTo(short newZoom)
1633 if (opInProgress == gmoAddCorner)
1634 opInProgress = gmoNone;
1636 if (newZoom == zoom)
1639 if (opInProgress || scrollDrag || (newZoom < minZoom) || (newZoom > maxZoom))
1640 ;//MessageBeep(MB_OK);
1643 // setDisplayZoom(zoom = newZoom);
1645 if (selectedOne >= 0)
1646 makeRoomVisible(selectedOne);
1649 // GetParentFrame()->OnUpdateFrameTitle(TRUE);
1656 //---------------------------------------------------------------------------
1657 // Keyboard navigation:
1660 // corner: The direction to move
1661 // toggleStubs: TRUE means adding an existing stub should delete it
1664 // edgeTmp: If type1 or type2 is nonzero, use that for a new edge
1665 // selectedOne: Must be a valid room number
1667 void MapView::navigate(RoomCorner corner, bool toggleStubs/* = true*/)
1669 const short roomXoffset[] =
1671 0, 0, gridX + roomWidth, -(gridX + roomWidth),
1672 gridX + roomWidth, -(gridX + roomWidth), gridX + roomWidth, -(gridX + roomWidth)
1673 }; // FIXME add NNE, etc.
1675 const short roomYoffset[] =
1677 gridY + roomHeight, -(gridY + roomHeight), 0, 0,
1678 gridY + roomHeight, gridY + roomHeight, -(gridY + roomHeight), -(gridY + roomHeight)
1679 }; // FIXME add NNE, etc.
1683 ASSERT(selectedOne >= 0);
1685 if (doc->room[selectedOne].isCorner())
1687 // MessageBeep(MB_ICONASTERISK);
1691 // Look for an existing connection:
1692 int eNum = doc->findEdge(selectedOne, corner, e);
1694 if ((eNum >= 0) && !(e.type2 & etUnexplored))
1696 // Found existing connection, and 2nd end is not explored
1697 if (toggleStubs && (e.type2 & etNoExit))
1699 if (edgeTmp.type1 & etNoExit)
1701 doc->setUndoData(new UndoDelete(doc->isDirty, doc->edge[eNum]));
1705 doc->setUndoData(new UndoChangeEdge(doc->isDirty, doc->edge[eNum]));
1706 edgeTmp.room1 = selectedOne;
1707 edgeTmp.end1 = corner;
1708 doc->addEdge(edgeTmp);
1711 doc->deleteEdge(eNum);
1715 if (edgeTmp.type1 || edgeTmp.type2 || (e.type1 & etOneWay)
1716 || (e.type2 & etNoRoom2))
1718 // We were trying to add connection
1719 // MessageBeep(MB_ICONASTERISK);
1723 if (e.end1 == rcCorner)
1725 int r = doc->findOtherEnd(eNum);
1731 // MessageBeep(MB_ICONASTERISK);
1734 } // end if this connection leads to a corner
1736 selectOnlyRoom(e.room1);
1737 makeRoomVisible(e.room1);
1738 eNum = -1; // Don't delete this edge
1740 else // eNum < 0 || e.type2 & etUnexplored
1742 // Try to add a new connection
1743 if (doc->locked || (corner >= rcNNE))
1745 // MessageBeep(MB_ICONASTERISK);
1749 const bool wasModified = doc->isDirty;
1752 // If adding stub where there's already a stub
1753 if ((eNum >= 0) && (e.type2 & etUnexplored)
1754 && (edgeTmp.type1 & etUnexplored))
1758 // Remove stub connection:
1759 doc->setUndoData(new UndoDelete(wasModified, doc->edge[eNum]));
1760 doc->deleteEdge(eNum);
1763 // MessageBeep(MB_ICONASTERISK);
1768 e.room1 = selectedOne;
1771 if (edgeTmp.type1 || edgeTmp.type2)
1773 e.type1 = edgeTmp.type1;
1774 e.type2 = edgeTmp.type2;
1779 EdgeVec deletedEdges;
1781 // If there's a room #2 connected to this corner of the room...
1782 if (!(e.type1 & etNoRoom2))
1784 QPoint pos(doc->room[selectedOne].pos);
1785 pos.rx() += roomXoffset[corner];
1786 pos.ry() += roomYoffset[corner];
1788 int room = doc->findRoom(pos);
1792 // We're off the grid
1793 // MessageBeep(MB_ICONASTERISK);
1798 // We need to add a new room
1799 room = doc->addRoom(pos);
1804 // Existing room, check for existing connection
1806 int oeNum = doc->findEdge(room, oppositeCorner[corner], oEdge);
1809 { // There is an existing connection
1810 if (oEdge.type2 & etUnexplored)
1812 deletedEdges.push_back(doc->edge[oeNum]);
1813 doc->deleteEdge(oeNum);
1816 eNum--; // The edge number might have changed
1819 room = -1; // Room has a non-stub connection there
1825 // MessageBeep(MB_ICONASTERISK);
1829 selectOnlyRoom(room);
1830 makeRoomVisible(room);
1833 e.end2 = oppositeCorner[corner];
1838 deletedEdges.push_back(doc->edge[eNum]);
1839 doc->deleteEdge(eNum);
1848 if (gueApp()->editAfterAdd()) //editAfterAdd() returns autoEdit
1849 editProperties(epuAddRoomEdge, wasModified, (deletedEdges.size() ? &deletedEdges : NULL));
1850 else if (deletedEdges.size())
1851 doc->setUndoData(new UndoChanges(wasModified, selectedOne, 1, deletedEdges));
1853 doc->setUndoData(new UndoAdd(wasModified, selectedOne, 1));
1855 else if (deletedEdges.size())
1857 if (deletedEdges.size() == 1)
1858 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
1860 doc->setUndoData(new UndoChanges(wasModified, -1, 1, deletedEdges));
1863 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
1870 // Fill in the edge type from the navigation box:
1872 // If the text in the navigation bar is up, down, in, or out, then set
1873 // the edge type appropriately. Otherwise, set the edge type to normal.
1874 // Always clears the navigation bar text.
1876 // Can only create up-down and in-out passages.
1879 // e.type1 and e.type2
1881 void MapView::setEdgeType(MapEdge & e)
1883 e.type1 = e.type2 = etNormal;
1885 RoomCorner corner1, corner2;
1886 char initial, separator;
1887 getNavText(initial, corner1, separator, corner2);
1889 if (initial && initial != '%' && initial != '>')
1891 // MessageBeep(MB_ICONASTERISK);
1899 case rcUp: e.type1 = etUp; break;
1900 case rcDown: e.type1 = etDown; break;
1901 case rcIn: e.type1 = etIn; break;
1902 case rcOut: e.type1 = etOut; break;
1905 if (separator == '-')
1909 case rcUp: e.type2 = etUp; break;
1910 case rcDown: e.type2 = etDown; break;
1911 case rcIn: e.type2 = etIn; break;
1912 case rcOut: e.type2 = etOut; break;
1916 else // standard edge
1920 case rcUp: e.type1 = etUp; e.type2 = etDown; break;
1921 case rcDown: e.type1 = etDown; e.type2 = etUp; break;
1922 case rcIn: e.type1 = etIn; e.type2 = etOut; break;
1923 case rcOut: e.type1 = etOut; e.type2 = etIn; break;
1928 e.type1 |= etObstructed;
1930 if (initial == '>' || separator == '>')
1931 e.type1 |= etOneWay;
1933 if (initial == '?' || separator == '?')
1935 e.type1 &= ~etSpecial;
1936 e.type1 |= etUnexplored;
1940 else if (initial == '!' || separator == '!')
1942 e.type1 &= ~etSpecial;
1943 e.type1 |= etNoExit;
1951 // Start typing in the navigation bar:
1953 // Stuffs the given char in the navigation bar and sets focus to there.
1956 // c: The character to put in the navigation bar.
1958 void MapView::setNavText(char c)
1961 CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
1964 nb.SetDlgItemText(IDC_NAV_DIR, text);
1966 CEdit * dir = static_cast<CEdit *>(nb.GetDlgItem(IDC_NAV_DIR));
1974 void MapView::OnChar(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
1976 if (((c >= 'A') && (c <= 'Z'))
1977 || ((c >= 'a') && (c <= 'z'))
1978 || (c == '-') || (c == '!') || (c == '?') || (c == '/')
1979 || (c == '>') || (c == '.') || (c == '~') || (c == '`')
1987 // Parse the text in the navigation bar:
1990 // mustBeMove: (default FALSE)
1991 // If TRUE, beep and don't clear text if not a direction.
1992 // If FALSE, clear text and don't beep even if it's meaningless.
1996 // The non-alphabetic character beginning the text (0 if none)
1998 // The direction indicated by the first direction word
1999 // rcNone if no direction words
2001 // The non-alphabetic character following the first direction word
2003 // Must be either '-', '>', or '?'
2005 // The direction indicated by the second direction word
2006 // rcNone if no second direction
2008 void MapView::getNavText(char & initial, RoomCorner & dir1, char & separator, RoomCorner & dir2, bool mustBeMove)
2011 CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
2014 nb.GetDlgItemText(IDC_NAV_DIR, text);
2017 dir1 = dir2 = rcNone;
2018 initial = separator = '\0';
2020 if (!text.IsEmpty() && (text[0] < 'a' || text[0] > 'z'))
2031 else if (!text.IsEmpty() && (text[0] == '-' || text[0] == '>'))
2033 separator = text[0];
2038 dir1 = parseDirection(text);
2040 if (!text.IsEmpty() && (text[0]=='-' || text[0]=='>' || text[0]=='?'))
2042 separator = text[0];
2048 dir2 = parseDirection(text);
2050 if (mustBeMove && (dir1 == rcNone || separator == '-' || ((initial || separator) && (dir1 >= rcNumCorners))))
2051 MessageBeep(MB_ICONASTERISK);
2054 nb.SetDlgItemText(IDC_NAV_DIR, tr(""));
2063 // Handle the Go button in the navigation bar:
2065 void MapView::OnNavGo()
2067 if (selectedOne < 0)
2069 MessageBeep(MB_ICONASTERISK);
2073 RoomCorner corner, junk2;
2074 char initial, separator;
2075 getNavText(initial, corner, separator, junk2, true);
2077 if (separator == '-' || junk2 != rcNone)
2079 MessageBeep(MB_ICONASTERISK);
2083 if (corner != rcNone)
2085 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2088 edgeTmp.type1 = etObstructed;
2090 if (initial == '>' || separator == '>')
2091 edgeTmp.type1 |= etOneWay;
2093 if (initial == '?' || separator == '?')
2094 edgeTmp.type1 = etUnexplored;
2095 else if (initial == '!' || separator == '!')
2096 edgeTmp.type1 = etNoExit;
2104 // Handle the direction keys on the numeric keypad:
2107 // cmd: Which command was executed
2109 void MapView::OnNavGoDir(unsigned int cmd)
2111 ASSERT(cmd >= ID_NAV_GO_NORTH && cmd <= ID_NAV_STUB2_OUT);
2113 if (selectedOne >= 0)
2116 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2117 bool toggleStubs = false;
2119 if (cmd >= ID_NAV_STUB1_NORTH)
2121 if (cmd >= ID_NAV_STUB2_NORTH)
2124 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etNoExit : etUnexplored);
2125 cmd -= ID_NAV_STUB2_NORTH - ID_NAV_GO_NORTH;
2130 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etUnexplored : etNoExit);
2131 cmd -= ID_NAV_STUB1_NORTH - ID_NAV_GO_NORTH;
2137 navigate(RoomCorner(cmd - ID_NAV_GO_NORTH), toggleStubs);
2140 MessageBeep(MB_ICONASTERISK);
2145 // CMapView message handlers
2146 //---------------------------------------------------------------------------
2147 // Update the comments dialog when the view is activated:
2149 void MapView::OnActivateView(bool bActivate, CView * pActivateView, CView * pDeactiveView)
2152 selectDone(); // Update comments dialog
2154 CScrollZoomView::OnActivateView(bActivate, pActivateView, pDeactiveView);
2159 // 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...]
2160 //void MapView::OnEditAddCorner()
2161 void MapView::HandleAddCorner(void)
2163 //printf("MapView::HandleAddCorner... (iTmp=%i, opInProgress=%i)\n", iTmp, opInProgress);
2164 ASSERT(opInProgress == gmoAddCorner);
2165 ASSERT(selectedOne >= 0);
2167 int room = doc->addCorner(selectedOne, iTmp);
2170 selectOnlyRoom(room);
2172 // MessageBeep(MB_ICONASTERISK);
2174 opInProgress = gmoNone;
2179 void MapView::HandleAddUnexplored(void)
2181 EdgeVec deletedEdges;
2182 const bool wasModified = doc->isDirty;
2185 edgeTmp.room1 = roomClicked;
2186 edgeTmp.end1 = cornerClicked;
2187 // This is from MapView::setEdgeType()
2188 edgeTmp.type1 &= ~etSpecial;
2189 edgeTmp.type1 |= etUnexplored;
2191 edgeTmp.end2 = rcSW;
2193 // Look for an existing connection:
2194 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2195 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2199 deletedEdges.push_back(doc->edge[eNum]);
2200 doc->deleteEdge(eNum);
2203 doc->addEdge(edgeTmp);
2205 if (deletedEdges.size())
2206 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2208 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2210 opInProgress = gmoNone;
2215 void MapView::HandleAddLoopBack(void)
2217 EdgeVec deletedEdges;
2218 const bool wasModified = doc->isDirty;
2221 edgeTmp.room1 = roomClicked;
2222 edgeTmp.end1 = cornerClicked;
2223 // This is from MapView::setEdgeType()
2224 edgeTmp.type1 &= ~etSpecial;
2225 edgeTmp.type1 |= etLoopBack;
2227 edgeTmp.end2 = rcSW;
2229 // Look for an existing connection:
2230 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2231 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2235 deletedEdges.push_back(doc->edge[eNum]);
2236 doc->deleteEdge(eNum);
2239 doc->addEdge(edgeTmp);
2241 if (deletedEdges.size())
2242 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2244 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2246 opInProgress = gmoNone;
2251 void MapView::HandleAddNoExit(void)
2253 EdgeVec deletedEdges;
2254 const bool wasModified = doc->isDirty;
2257 edgeTmp.room1 = roomClicked;
2258 edgeTmp.end1 = cornerClicked;
2259 // This is from MapView::setEdgeType()
2260 edgeTmp.type1 &= ~etSpecial;
2261 edgeTmp.type1 |= etNoExit;
2263 edgeTmp.end2 = rcSW;
2265 // Look for an existing connection:
2266 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2267 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2271 deletedEdges.push_back(doc->edge[eNum]);
2272 doc->deleteEdge(eNum);
2275 doc->addEdge(edgeTmp);
2277 if (deletedEdges.size())
2278 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2280 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2282 opInProgress = gmoNone;
2287 void MapView::SetEdgeDirection(EdgeType et)
2291 // Look for an existing connection:
2292 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2296 // Is the room we clicked on room1 of this edge?
2297 if (doc->edge[eNum].room1 == roomClicked)
2299 doc->edge[eNum].type1 &= ~etDirection;
2300 doc->edge[eNum].type1 |= et;
2304 doc->edge[eNum].type2 &= ~etDirection;
2305 doc->edge[eNum].type2 |= et;
2309 opInProgress = gmoNone;
2314 void MapView::HandleAddUp(void)
2316 SetEdgeDirection(etUp);
2320 void MapView::HandleAddDown(void)
2322 SetEdgeDirection(etDown);
2326 void MapView::HandleAddIn(void)
2328 SetEdgeDirection(etIn);
2332 void MapView::HandleAddOut(void)
2334 SetEdgeDirection(etOut);
2338 void MapView::SetEdges(int room, int edgeNum, EdgeType eType)
2341 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.
2344 1) Figure out which side of the Edge has the room we're starting with.
2345 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.
2346 3) Once the Edge is found, check to see if it's an rfCorner.
2350 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...
2351 Which means you could do away with type2, only it uses that to distinguish the Up/Down/In/Out annotations put on map edges...
2354 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.
2356 // Go thru from current room to all connected "corner" rooms
2359 // Check for the unlikely case that we got passed bad info
2360 if (doc->edge[edgeNum].HasRoom(room) == false)
2363 if (doc->edge[edgeNum].room2 == room)
2364 doc->edge[edgeNum].Swap();
2366 if (((eType == etOneWay) && (doc->edge[edgeNum].end2 != rcCorner))
2367 || (eType != etOneWay))
2368 doc->edge[edgeNum].type1 |= eType;
2370 if (doc->edge[edgeNum].end2 != rcCorner)
2373 // Find next Edge... :-/
2374 room = doc->edge[edgeNum].room2;
2376 for(int i=0; i<doc->edge.size(); i++)
2378 // Exclude the edge we just came from...
2382 if (doc->edge[i].HasRoom(room))
2392 void MapView::ClearEdges(int room, int edgeNum, EdgeType eType)
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 doc->edge[edgeNum].type1 &= ~eType;
2406 if (doc->edge[edgeNum].end2 != rcCorner)
2409 // Find next Edge... :-/
2410 room = doc->edge[edgeNum].room2;
2412 for(int i=0; i<doc->edge.size(); i++)
2414 // Exclude the edge we just came from...
2418 if (doc->edge[i].HasRoom(room))
2428 void MapView::HandleAddOneWay(void)
2432 // Look for an existing connection:
2433 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2436 SetEdges(roomClicked, eNum, etOneWay);
2438 opInProgress = gmoNone;
2443 void MapView::HandleClearOneWay(void)
2447 // Look for an existing connection:
2448 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2451 ClearEdges(roomClicked, eNum, etOneWay);
2453 opInProgress = gmoNone;
2458 void MapView::HandleAddRestricted(void)
2462 // Look for an existing connection:
2463 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2466 SetEdges(roomClicked, eNum, etObstructed);
2468 opInProgress = gmoNone;
2473 void MapView::HandleClearRestricted(void)
2477 // Look for an existing connection:
2478 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2481 ClearEdges(roomClicked, eNum, etObstructed);
2483 opInProgress = gmoNone;
2488 //void MapView::OnEditAddRoom()
2489 void MapView::HandleAddRoom(void)
2491 addRoom(scrollDragStart);
2495 void MapView::HandleMapProperties(void)
2499 dlg.title.setText(doc->name.c_str());
2500 dlg.comment.setPlainText(doc->note.c_str());
2501 dlg.navMode.setChecked(doc->locked);
2502 dlg.displayGrid.setChecked(showGrid);
2503 dlg.showCorners.setChecked(showCorners);
2504 dlg.showPages.setChecked(showPages);
2506 if (dlg.exec() == false)
2509 doc->setName(dlg.title.text().toUtf8().data());
2510 doc->setNote(dlg.comment.document()->toPlainText().toUtf8().data());
2511 doc->locked = dlg.navMode.isChecked();
2512 showGrid = dlg.displayGrid.isChecked();
2513 showCorners = dlg.showCorners.isChecked();
2514 showPages = dlg.showPages.isChecked();
2516 if (dlg.title.text().isEmpty() == false)
2517 setWindowTitle(dlg.title.text());
2523 //void MapView::OnEditClear()
2524 void MapView::HandleDelete(void)
2532 //--------------------------------------------------------------------
2533 void MapView::OnEditAddPage()
2535 MapDoc * const doc = GetDocument();
2538 const int p = doc->page.size();
2543 page.pos = scrollDragStart;
2544 page.pos.x -= page.pos.x % gridX;
2545 page.pos.y -= page.pos.y % gridY;
2546 doc->setUndoData(new UndoAdd(doc->isDirty, p));
2548 doc->addPage(p, page);
2558 // removeCorner: (default true)
2559 // When TRUE, if a single corner is selected, remove it but leave
2560 // the connection. When FALSE, the connection is always removed.
2562 void MapView::deleteSelection(bool removeCorner/* = true*/)
2564 if (numSelected || numSelectedPages)
2566 if (doc->locked || (numSelectedPages == doc->page.size()))
2568 // MessageBeep(MB_ICONASTERISK);
2572 if (removeCorner && (selectedOne >= 0)
2573 && doc->room[selectedOne].isCorner())
2575 // Replace this corner with a single edge:
2576 doc->deleteCorner(selectedOne);
2577 clearSelection(false);
2583 // Select all corners if one corner or one end is selected:
2584 const EdgeVec & edge = doc->edge;
2585 int wereSelected = 0;
2587 while (wereSelected != numSelected)
2589 wereSelected = numSelected;
2591 for(EdgeConstItr e=edge.begin(); e!=edge.end(); ++e)
2593 if ((e->end1 == rcCorner) && selected[e->room2] && !selected[e->room1])
2594 selectRoom(e->room1, false);
2595 else if ((e->end2==rcCorner) && selected[e->room1] && !selected[e->room2])
2596 selectRoom(e->room2, false);
2601 UndoDelete * undoRec = new UndoDelete(*doc, numSelected, selected, numSelectedPages, selectedPage);
2602 opInProgress = gmoDeleting; // Don't clear selection after first room
2603 int pos = numSelected;
2605 for(int i=doc->room.size()-1; i>=0; i--)
2608 undoRec->addRoom(--pos, i, doc->extractRoom(i));
2611 for(int i=doc->page.size()-1; i>=0; i--)
2613 if (selectedPage[i])
2617 selectedPage.resize(doc->page.size(), 0);
2618 opInProgress = gmoNone;
2619 doc->setUndoData(undoRec);
2621 clearSelection(false);
2628 //--------------------------------------------------------------------
2629 // Cut, Copy & Paste:
2631 void MapView::OnEditCopy()
2633 if (numSelected || numSelectedPages)
2635 MapDoc * doc = GetDocument();
2638 // Deselect all corner rooms:
2639 for(int i=doc->room.size()-1; i>=0; --i)
2641 if (selected[i] && doc->room[i].isCorner())
2645 if (!numSelected && !numSelectedPages)
2648 MessageBeep(MB_ICONASTERISK);
2650 } // end if only corners were selected
2652 // Select corners between selected rooms:
2653 const EdgeVec& edges = doc->edge;
2655 for (EdgeConstItr e = edges.begin(); e != edges.end(); ++e)
2657 if ((selected[e->room1] && (e->end2 == rcCorner)
2658 && (e->end1 != rcCorner) && !selected[e->room2])
2659 || (selected[e->room2] && (e->end1 == rcCorner)
2660 && (e->end2 != rcCorner) && !selected[e->room1]))
2662 RoomNumVec cornerRooms;
2663 cornerRooms.reserve(16); // Should be plenty
2664 int otherEnd = doc->findOtherEnd(e, &cornerRooms);
2666 if ((otherEnd >= 0) && selected[otherEnd])
2668 for(RNConstItr rn=cornerRooms.begin(); rn!=cornerRooms.end(); ++rn)
2671 } // end if edge connects selected room to unselected corner
2676 // Copy selected rooms and pages:
2677 gueApp()->setClipboard(new RoomScrap(*doc, numSelected, selected, numSelectedPages, selectedPage));
2682 void MapView::OnEditCut()
2689 void MapView::OnEditPaste()
2691 const RoomScrap * scrap = static_cast<CMapApp *>(AfxGetApp())->getClipboard();
2695 MapDoc * doc = GetDocument();
2698 int i = doc->room.size();
2700 doc->setUndoData(scrap->paste(*doc));
2701 const int numRooms = doc->room.size();
2706 clearSelection(true);
2708 while (i < numRooms)
2709 selectRoom(i++, true);
2716 void MapView::OnEditPaginate()
2719 GetDocument()->layoutPages();
2725 // Handle Edit Properties:
2727 void MapView::HandleRoomProperties(void)
2729 // editProperties((cmd == ID_EDIT_MAP_PROPERTIES) ? epuMapInfo : epuRoomInfo);
2730 editProperties(epuRoomInfo);
2735 // Bring up the Properties dialog:
2739 // The type of action we should create an undo record for
2740 // epuRoomInfo: Just changing room info
2741 // epuAddRoom: Adding a new room
2742 // epuAddRoomEdge: Adding a new room and a new edge
2744 // The modification state of the document
2745 // (Necessary only if undoType is not epuRoomInfo)
2746 // edges: (default NULL)
2747 // A pointer to the edges that were deleted before adding this room
2748 // NULL if no edges were deleted
2749 // Must be NULL unless undoType is epuAddRoomEdge
2750 // The vector is emptied but not deleted
2752 void MapView::editProperties(EditPropUndo undoType, bool wasModified/*= false*/, EdgeVec * edges/*= NULL*/)
2754 bool forceMap = false;
2756 if (undoType == epuMapInfo)
2758 undoType = epuRoomInfo;
2762 // CPropertySheet dlg(tr("Properties"));
2763 RoomDialog dlg(this);
2764 // CRoomPropPage room;
2765 UndoRoomInfo * undoRoom = NULL;
2766 const bool roomTab = (selectedOne >= 0) && !doc->room[selectedOne].isCorner();
2768 // CMapPropPage map;
2769 // Strcpy(map.name, doc->getName());
2770 // Strcpy(map.note, doc->getNote());
2771 // map.corners = showCorners;
2772 // map.locked = doc->locked;
2773 // map.grid = showGrid;
2774 // map.pages = showPages;
2775 // dlg.AddPage(&map);
2779 // VERIFY(undoRoom = new UndoRoomInfo(*doc, selectedOne));
2780 undoRoom = new UndoRoomInfo(*doc, selectedOne);
2781 // room.border = bool((undoRoom->getRoom().flags & rfBorder) != 0);
2782 // Strcpy(room.name, undoRoom->getRoom().name);
2783 // Strcpy(room.note, undoRoom->getRoom().note);
2784 // dlg.AddPage(&room);
2785 // static_cast<CMapApp *>(AfxGetApp())->setComment(this, NULL, false);
2786 dlg.name.setText(undoRoom->room.name.c_str());
2787 dlg.comment.insertPlainText(undoRoom->room.note.c_str());
2788 dlg.border.setChecked(bool((undoRoom->room.flags & rfBorder) != 0));
2791 // dlg.SetActivePage(&room);
2794 // if (dlg.DoModal() == IDOK)
2799 bool changed = false;
2801 // if (room.border != (0 != (undoRoom->getRoom().flags & rfBorder)))
2802 if (dlg.border.isChecked() != (0 != (undoRoom->room.flags & rfBorder)))
2804 // doc->setRoomFlags(selectedOne, (room.border ? rfBorder : 0), rfBorder);
2805 doc->setRoomFlags(selectedOne, (dlg.border.isChecked() ? rfBorder : 0), rfBorder);
2809 // if (room.name != undoRoom->getRoom().name.c_str())
2810 if (dlg.name.text().toUtf8().data() != undoRoom->room.name.c_str())
2812 // if (room.name.Find('\n') < 0)
2813 // wrapRoomName(room.name);
2815 doc->setRoomName(selectedOne, dlg.name.text().toUtf8().data());
2819 // room.note.TrimRight();
2821 if (dlg.comment.document()->toPlainText().toUtf8().data() != undoRoom->room.note.c_str())
2823 // doc->setRoomNote(selectedOne, room.note);
2824 doc->setRoomNote(selectedOne, dlg.comment.document()->toPlainText().toUtf8().data());
2828 if (changed && (undoType == epuRoomInfo))
2830 doc->setUndoData(undoRoom);
2831 undoRoom = NULL; // undoRoom now belongs to the document
2835 if (undoType != epuRoomInfo)
2837 ASSERT(selectedOne >= 0);
2840 doc->setUndoData(new UndoChanges(wasModified, selectedOne, 1, *edges));
2842 doc->setUndoData(new UndoAdd(wasModified, selectedOne, (undoType == epuAddRoomEdge ? 1 : 0)));
2845 // doc->setName(map.name);
2846 // doc->setNote(map.note);
2848 // if (doc->locked != bool(map.locked))
2849 // doc->OnNavigationMode();
2851 /* if ((showCorners != bool(map.corners))
2852 || (showGrid != bool(map.grid))
2853 || (showPages != bool(map.pages)))
2855 showCorners = map.corners != 0;
2856 showGrid = map.grid != 0;
2857 showPages = map.pages != 0;
2860 deselectPages(false);
2862 // InvalidateRect(NULL);
2865 else if (undoType != epuRoomInfo)
2867 // Cancel adding the room:
2868 ASSERT(selectedOne >= 0);
2869 const RoomNum roomNum = selectedOne;
2872 if (undoType == epuAddRoomEdge)
2874 const MapEdge & edge = doc->edge[doc->edge.size() - 1];
2875 selectOnlyRoom(edge.room1 == roomNum ? edge.room2 : edge.room1);
2878 opInProgress = gmoDeleting; // Don't clear selection
2879 doc->deleteRoom(roomNum);
2880 opInProgress = gmoNone;
2884 doc->addEdges(edges->size());
2886 for(EdgeConstItr e=edges->begin(); e!=edges->end(); e++)
2890 doc->isDirty = wasModified;
2901 // Select all rooms:
2903 void MapView::OnEditSelectAll()
2905 for(int i=doc->room.size()-1; i>=0; i--)
2912 //---------------------------------------------------------------------------
2913 // Select connected rooms:
2915 void MapView::OnEditSelectConnected()
2917 const MapDoc * doc = GetDocument();
2920 const int numEdges = doc->getEdgeCount();
2921 const EdgeVec & edge = doc->edge;
2923 int wasSelected = 0;
2925 while (wasSelected != numSelected)
2927 wasSelected = numSelected;
2929 for(int i=0; i<numEdges; i++)
2931 if ((edge[i].type1 & etNoRoom2) == 0)
2933 if (selected[edge[i].room1])
2934 selectRoom(edge[i].room2);
2935 else if (selected[edge[i].room2])
2936 selectRoom(edge[i].room1);
2937 } // end if edge connects two rooms
2939 } // end while more rooms were selected on this pass
2945 void MapView::OnInitialUpdate()
2947 const MapDoc * doc = GetDocument();
2950 selectedPage.resize(doc->page.size(), 0);
2951 clearSelection(false);
2953 // Set the document size:
2954 init(4 * gridX, doc->getDocSize(), QSize(gridX, gridY));
2955 // CClientDC dc(this);
2956 // OnPrepareDC(&dc);
2958 memset(&lf, 0, sizeof(lf));
2960 strcpy(lf.lfFaceName, "Arial");
2961 font.CreateFontIndirect(&lf);
2964 OnUpdate(NULL, dupNavigationMode, NULL);
2966 CScrollZoomView::OnInitialUpdate();
2967 setScrollBars(); // Now fix the scroll bars
2971 void MapView::OnKeyDown(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
2973 bool ctrl = (GetKeyState(VK_CONTROL) < 0);
2977 case VK_LEFT: OnHScroll(ctrl ? SB_PAGELEFT : SB_LINELEFT, 0, NULL); break;
2978 case VK_RIGHT: OnHScroll(ctrl ? SB_PAGERIGHT : SB_LINERIGHT, 0, NULL); break;
2979 case VK_UP: OnVScroll(ctrl ? SB_PAGEUP : SB_LINEUP, 0, NULL); break;
2980 case VK_DOWN: OnVScroll(ctrl ? SB_PAGEDOWN : SB_LINEDOWN, 0, NULL); break;
2981 case VK_PRIOR: OnVScroll(ctrl ? SBtrOP : SB_PAGEUP, 0, NULL); break;
2982 case VK_NEXT: OnVScroll(ctrl ? SB_BOTTOM : SB_PAGEDOWN, 0, NULL); break;
2983 case VKtrAB: gueApp()->editComment(); break;
2985 if ((GetKeyState(VK_SHIFT) >= 0) && (c >= '0') && (c <= '9'))
2987 if (!ctrl && (c < '2'))
2989 else if (ctrl && (c == '0'))
2992 zoomTo(10 * (c - '0') + (ctrl ? 100 : 0));
2993 } // end if number key (not shifted)
3001 void MapView::mouseDoubleClickEvent(QMouseEvent * event)
3002 //void MapView::OnLButtonDblClk(unsigned int nFlags, QPoint point)
3004 if (scrollDrag || scrollDragTimer)
3007 // QPoint point = event->pos() * 5;
3008 QPointF ptf = event->localPos() * (100.0 / zoom);
3009 QPoint point = ptf.toPoint();
3010 // QPoint point = event->pos() * (100.0 / zoom);
3011 int nFlags = event->modifiers();
3016 opInProgress = gmoNone;
3018 // if (GetCapture() == this)
3019 // ReleaseCapture();
3021 // CClientDC dc(this);
3022 // OnPrepareDC(&dc);
3023 // dc.DPtoLP(&point);
3025 const int i = doc->roomHit(point);
3026 //printf("MapView::mouseDoubleClickEvent: roomHit=%i\n", i);
3028 if ((i >= 0) && (doc->room[i].isCorner() == false))
3031 editProperties(epuRoomInfo);
3038 /* N.B.: Handles RButton & MButton too */
3039 void MapView::mousePressEvent(QMouseEvent * event)
3040 //void MapView::OnLButtonDown(unsigned int nFlags, QPoint point)
3042 nFlags = MK_CONTROL, MK_SHIFT, MK_{LMR}BUTTON
3045 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3046 // QPoint point = event->pos() * 5;
3047 QPointF ptf = event->localPos() * (100.0 / zoom);
3048 QPoint point = ptf.toPoint();
3049 // QPoint point = event->pos() * (100 / zoom);
3050 int nFlags = event->modifiers();
3051 mouseDown = true;//will prolly have to separate this out to L, M & R :-P
3053 Qt::NoModifier, Qt::ShiftModifier, Qt::ControlModifier, Qt::AltModifier, etc.
3056 // Scrolling support (save this for now)...
3058 offsetScrollDragStart = offset;
3059 scrollDragStart = point + offset;
3061 if (event->button() == Qt::LeftButton)
3063 if (scrollDrag || scrollDragTimer)
3066 // CClientDC dc(this);
3067 // OnPrepareDC(&dc);
3068 // dc.DPtoLP(&point);
3071 int room = doc->roomHit(point, &corner);
3076 return; // Must have 1 room selected during navigation
3078 if (showPages && ((room = doc->pageBorderHit(point)) >= 0))
3082 // if (nFlags & MK_CONTROL)
3083 if (nFlags & Qt::ControlModifier)
3085 opInProgress = gmoControlDown;
3086 bTmp = selectedPage[room];
3087 b2Tmp = true; // In page
3091 // if (nFlags & MK_SHIFT)
3092 if (nFlags & Qt::ShiftModifier)
3093 opInProgress = gmoShiftDown;
3095 opInProgress = gmoNone;
3106 p1Tmp.rx() = point.x() / gridX;
3107 p1Tmp.ry() = point.y() / gridY;
3108 p2Tmp = doc->page[room].pos;
3114 opInProgress = gmoSelectBox;
3116 // if (nFlags & MK_CONTROL)
3117 if (nFlags & Qt::ControlModifier)
3119 // If Ctrl pressed, don't clear selection:
3120 for(ByteItr b=selected.begin(); b!=selected.end(); b++)
3123 *b = 2; // Mark the rooms that should stay selected
3126 for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
3129 *b = 2; // Mark the pages that should stay selected
3140 // rTmp.left = rTmp.right = point.x;
3141 // rTmp.bottom = rTmp.top = point.y;
3142 rTmp.setCoords(point.x(), point.y(), point.x(), point.y());
3147 else if (doc->locked)
3150 ASSERT(selectedOne >= 0);
3152 if ((room != selectedOne) && !doc->room[room].isCorner())
3155 doc->shortestPath(path, selectedOne, room);
3158 path = tr("Sorry, I don't know how to get there.");
3159 else if (path.find('.') != String::npos)
3161 const CMapApp * app = static_cast<CMapApp *>(AfxGetApp());
3163 if (app->navigationCopy())
3165 if (app->navigationCRLF())
3168 pasteClipboard(path);
3171 // end else if path has multiple steps
3173 static_cast<CMainFrame *>(AfxGetMainWnd())->setStatusBar(path.c_str());
3174 selectOnlyRoom(room);
3176 // end if moving to new room in navigation mode
3179 // else if (nFlags & MK_CONTROL)
3180 else if (nFlags & Qt::ControlModifier)
3183 opInProgress = gmoControlDown;
3184 ASSERT(room < selected.size());
3185 bTmp = selected[room];
3186 b2Tmp = false; // In room
3190 p1Tmp.rx() = point.x() / gridX;
3191 p1Tmp.ry() = point.y() / gridY;
3194 // else if (nFlags & MK_SHIFT)
3195 else if (nFlags & Qt::ShiftModifier)
3198 opInProgress = gmoShiftDown;
3199 selectOnlyRoom(room);
3200 iTmp = -1; // Not a page
3201 p1Tmp.rx() = point.x() / gridX;
3202 p1Tmp.ry() = point.y() / gridY;
3203 p2Tmp = doc->room[room].pos;
3207 selectOnlyRoom(room);
3209 if (!(doc->room[room].isCorner()))
3210 emit(RoomClicked(doc, room));
3212 if (doc->room[room].isCorner())
3215 opInProgress = gmoControlDown;
3216 bTmp = b2Tmp = false; // Leave the corner selected; in room
3217 p1Tmp.rx() = point.x() / gridX;
3218 p1Tmp.ry() = point.y() / gridY;
3220 else if (corner != rcNone)
3223 opInProgress = gmoAddEdge;
3224 bTmp = doc->isDirty;
3225 iTmp = doc->findEdge(room, corner, edgeTmp);
3229 opInProgress = gmoChangeEdge;
3230 rTmp.setCoords(point.x() - 29, point.y() - 29, point.x() + 29, point.y() + 29);
3231 // rTmp.InflateRect(29, 29);
3234 if ((iTmp < 0) || (edgeTmp.type2 & etNoRoom2))
3236 edgeTmp.room1 = room;
3237 edgeTmp.end1 = corner;
3238 setEdgeType(edgeTmp);
3242 edgeTmp.end2 = rcSW;
3243 doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3246 // OnPaint(); // Update the view immediately
3249 if (opInProgress == gmoAddEdge)
3254 if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3257 QPen * oldPen = dc.SelectObject(&penEdge);
3260 dc.SelectObject(oldPen);
3268 else if (event->button() == Qt::RightButton)
3270 //void MapView::OnRButtonDown(unsigned int nFlags, QPoint point)
3272 /* if (opInProgress == gmoAddCorner)
3273 opInProgress = gmoNone;
3275 // CClientDC dc(this);
3276 // OnPrepareDC(&dc);
3277 // dc.DPtoLP(&point);
3279 scrollDragStart = point;
3285 // ::SetCursor(handCursor);
3288 // scrollDragTimer = SetTimer(menuTimer, clickTicks, NULL);
3295 bool MapView::calcScrollPosition(QPoint & pt) const
3305 // VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_HORZ, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3307 // if (pt.x + si.nPage > si.nMax + scrollStep.cx)
3308 // pt.x = si.nMax - si.nPage + scrollStep.cx;
3310 // VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_VERT, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3312 // if (-pt.y + si.nPage > si.nMax + scrollStep.cy)
3313 // pt.y = si.nPage - si.nMax - scrollStep.cy;
3315 // pt.x -= pt.x % scrollStep.cx;
3316 // pt.y -= pt.y % scrollStep.cy;
3318 return offset != pt;
3323 void MapView::scrollToPosition(const QPoint & pt)
3325 QPoint newOffset(pt);
3327 if (!calcScrollPosition(newOffset))
3328 return; // Didn't scroll
3330 // QSize delta(newOffset - offset);
3333 // CClientDC dc(this);
3334 // OnPrepareDC(&dc);
3335 // dc.LPtoDP(&delta);
3337 // SetScrollPos(SB_VERT, -offset.y);
3338 // SetScrollPos(SB_HORZ, offset.x);
3339 // ScrollWindow(-delta.cx, delta.cy);
3343 void MapView::mouseMoveEvent(QMouseEvent * event)
3344 //void MapView::OnMouseMove(unsigned int nFlags, QPoint point)
3346 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3347 // QPoint point = event->pos() * 5;
3348 QPointF ptf = event->localPos() * (100.0 / zoom);
3349 QPoint point = ptf.toPoint();
3350 // QPoint point = event->pos() * (100 / zoom);
3351 int nFlags = event->modifiers();
3356 // Location where the mouse is currently pointing
3359 // Bail if we're just mousing around...
3360 if (mouseDown == false)
3362 hoveredEdge = FindHoveredEdge();
3368 /* if (scrollDragTimer)
3370 // KillTimer(scrollDragTimer);
3371 scrollDragTimer = 0;
3374 // ::SetCursor(handCursor);
3377 // if (GetCapture() != this)
3380 // CClientDC dc(this);
3381 // OnPrepareDC(&dc);
3382 // dc.DPtoLP(&point);
3389 // p.Offset(scrollDragStart - point);
3390 p += (scrollDragStart - point);
3392 if (!calcScrollPosition(p)
3393 )// || PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3396 if ((opInProgress == gmoAddEdge) || (opInProgress == gmoSelectBox)
3397 || (opInProgress >= gmoDrag))
3400 // We must handle these specially to avoid display artifacts:
3402 CGdiObject * old = NULL;
3406 switch (opInProgress)
3409 if (!pen.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0 ,0)))
3412 old = dc.SelectObject(&pen);
3418 old = dc.SelectStockObject(HOLLOW_BRUSH);
3424 break; // Must be gmoDrag
3428 scrollToPosition(p);
3430 // OnPrepareDC(&dc); // Update with new offset
3433 switch (opInProgress)
3435 case gmoAddEdge: dc.MoveTo(p1Tmp); dc.LineTo(p2Tmp); break;
3436 case gmoSelectBox: dc.Rectangle(rTmp); break;
3437 default: /* drag*/ drawSelected(&dc); break;
3441 dc.SelectObject(old);
3445 scrollToPosition(p);
3447 QPoint delta = (point + offset) - scrollDragStart;
3448 offset = offsetScrollDragStart + delta;
3454 if (opInProgress == gmoChangeEdge)
3456 // if (rTmp.PtInRect(point))
3457 if (rTmp.contains(point))
3460 // e2Tmp = doc->getEdge(iTmp); // Save the current edge data
3461 e2Tmp = doc->edge[iTmp]; // Save the current edge data
3462 doc->deleteEdge(iTmp); // Delete the old edge
3463 // OnPaint(); // Update the view immediately
3464 opInProgress = gmoAddEdge; // Now fall through to gmoAddEdge
3467 if (opInProgress == gmoAddEdge)
3469 // dc.SetROP2(R2_NOT);
3472 // if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0))) // PORT
3475 // QPen * oldPen = dc.SelectObject(&penEdge);
3476 // dc.MoveTo(p1Tmp);
3477 // dc.LineTo(p2Tmp);
3480 int room = doc->roomHit(point, &corner);
3482 if (corner != rcNone && room != edgeTmp.room1)
3484 edgeTmp.room2 = room;
3485 edgeTmp.end2 = corner;
3486 doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3493 // dc.MoveTo(p1Tmp);
3494 // dc.LineTo(p2Tmp);
3495 // dc.SelectObject(oldPen);
3497 else if (opInProgress == gmoSelectBox)
3502 // dc.SetROP2(R2_NOT);
3503 // QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3504 // dc.Rectangle(rTmp);
3506 rTmp = QRect(p1Tmp, point).normalized();
3507 // rTmp.NormalizeRect();
3508 RoomConstItr room = doc->room.getVector().begin();
3509 // QRect intersect, r;
3511 for(int i=doc->room.size()-1; i>=0; i--)
3513 if (selected[i] == 2)
3514 continue; // Room was already selected before selection box
3517 room[i]->getRect(r);
3518 // intersect.IntersectRect(rTmp, r);
3519 intersect = rTmp & r;
3521 QRect r = room[i]->getRect();
3522 QRect intersect = rTmp & r;
3525 if ((selected[i] != 0) != bool(r == intersect))
3527 selected[i] = !selected[i];
3528 numSelected += (selected[i] ? 1 : -1);
3529 // r.DeflateRect(5, 5);
3531 // r.NormalizeRect();
3532 // InvalidateRect(&r);
3538 for(int i=doc->page.size()-1; i>=0; i--)
3540 if (selectedPage[i] == 2)
3541 continue; // Page was already selected before selection box
3544 doc->getPageRect(i, r);
3545 // intersect.IntersectRect(rTmp, r);
3546 intersect = rTmp & r;
3548 QRect r = doc->getPageRect(i);
3549 QRect intersect = rTmp & r;
3552 if ((selectedPage[i] != 0) != bool(r == intersect))
3554 selectedPage[i] = !selectedPage[i];
3555 numSelectedPages += (selectedPage[i] ? 1 : -1);
3556 // r.DeflateRect(5, 5);
3558 // r.NormalizeRect();
3559 // InvalidateRect(&r);
3564 if ((numSelected == 1) && !numSelectedPages)
3566 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
3567 ; // Find the selected room
3574 // if (GetUpdateRect(NULL))
3577 // dc.Rectangle(rTmp);
3578 // dc.SelectObject(oldBrush);
3581 else if (opInProgress == gmoControlDown)
3583 p2Tmp.rx() = gridX * (point.x() / gridX - p1Tmp.x());
3584 p2Tmp.ry() = gridY * (point.y() / gridY - p1Tmp.y());
3586 if (p2Tmp.x() || p2Tmp.y())
3588 opInProgress = gmoControlDrag;
3589 computeSelectedRect(rTmp);
3590 // drawBackground(&dc);
3591 // dc.SetROP2(R2_NOT);
3592 // drawSelected(&dc);
3595 else if (opInProgress == gmoShiftDown)
3597 long ox = point.x() / gridX;
3598 long oy = point.y() / gridY;
3601 opInProgress = gmoShiftDragLeft;
3602 else if (ox > p1Tmp.x())
3603 opInProgress = gmoShiftDragRight;
3604 else if (oy > p1Tmp.y())
3605 opInProgress = gmoShiftDragUp;
3606 else if (oy < p1Tmp.y())
3607 opInProgress = gmoShiftDragDown;
3611 // if dragging a page
3614 ASSERT(!numSelected && (numSelectedPages == 1));
3615 rTmp = doc->getPageRect(iTmp);
3617 switch (opInProgress)
3619 case gmoShiftDragUp:
3620 p2Tmp.ry() = rTmp.top();
3622 case gmoShiftDragLeft:
3623 p2Tmp.rx() = rTmp.right() - roomWidth + gridX;
3628 RoomConstItr room = doc->room.getVector().begin();
3629 const int roomCount = doc->room.size();
3631 for(int i=0; i<roomCount; i++)
3633 if ((opInProgress == gmoShiftDragLeft && room[i]->pos.x() <= p2Tmp.x())
3634 || (opInProgress == gmoShiftDragRight && room[i]->pos.x() >= p2Tmp.x())
3635 || (opInProgress == gmoShiftDragUp && room[i]->pos.y() >= p2Tmp.y())
3636 || (opInProgress == gmoShiftDragDown && room[i]->pos.y() <= p2Tmp.y()))
3637 selectRoom(i, false);
3641 // shift-dragging a room
3642 if (opInProgress == gmoShiftDragLeft)
3643 p2Tmp.rx() += roomWidth;
3644 else if (opInProgress == gmoShiftDragDown)
3645 p2Tmp.ry() += roomHeight;
3649 // shift-dragging a page
3650 if (opInProgress == gmoShiftDragLeft)
3651 p2Tmp.rx() += gridX;
3652 else if (opInProgress == gmoShiftDragRight)
3653 p2Tmp.rx() -= gridX;
3654 else if (opInProgress == gmoShiftDragUp)
3655 p2Tmp.ry() -= gridY;
3656 else if (opInProgress == gmoShiftDragDown)
3657 p2Tmp.ry() += gridY;
3660 for(i=doc->page.size()-1; i>=0; --i)
3662 rTmp = doc->getPageRect(i);
3664 if ((opInProgress == gmoShiftDragLeft && rTmp.right() < p2Tmp.x())
3665 || (opInProgress == gmoShiftDragRight && rTmp.left() > p2Tmp.x())
3666 || (opInProgress == gmoShiftDragUp && rTmp.top() > p2Tmp.y())
3667 || (opInProgress == gmoShiftDragDown && rTmp.bottom() < p2Tmp.y()))
3668 selectPage(i, false);
3674 p2Tmp.rx() = gridX * (point.x() / gridX - p1Tmp.x());
3675 p2Tmp.ry() = gridY * (point.y() / gridY - p1Tmp.y());
3676 computeSelectedRect(rTmp);
3677 // drawBackground(&dc);
3678 // dc.SetROP2(R2_NOT);
3679 // drawSelected(&dc);
3681 // end else if gmoShiftDown
3682 else if (opInProgress >= gmoDrag)
3686 // if (PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3687 // return; // The mouse has already been moved again
3689 QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3698 // dc.SetROP2(R2_NOT);
3699 // drawSelected(&dc);
3701 // drawSelected(&dc);
3705 // Maybe have more discriminant updating... Maybe.
3710 //void MapView::OnLButtonUp(unsigned int nFlags, QPoint point)
3711 void MapView::mouseReleaseEvent(QMouseEvent * event)
3713 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3714 // QPoint point = event->pos() * 5;
3715 QPointF ptf = event->localPos() * (100.0 / zoom);
3716 QPoint point = ptf.toPoint();
3717 // QPoint point = event->pos() * (100 / zoom);
3718 int nFlags = event->modifiers();
3724 if (event->button() == Qt::LeftButton)
3726 // if (GetCapture() != this)
3728 // if (selectedOne >= 0)
3729 // makeRoomVisible(selectedOne);
3735 // ReleaseCapture();
3737 // CClientDC dc(this);
3738 // OnPrepareDC(&dc);
3739 // dc.DPtoLP(&point);
3741 if (opInProgress == gmoAddEdge)
3743 // Erase the rubber band:
3744 // dc.SetROP2(R2_NOT);
3747 // if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3750 // QPen * oldPen = dc.SelectObject(&penEdge);
3751 // dc.MoveTo(p1Tmp);
3752 // dc.LineTo(p2Tmp);
3753 // dc.SelectObject(oldPen);
3755 // Find out where the edge goes:
3757 int room = doc->roomHit(point, &corner);
3759 if (corner != rcNone && room != edgeTmp.room1)
3761 // The edge goes somewhere
3762 // If there's a stub on the other end, delete it:
3763 EdgeVec deletedEdges;
3765 int oldEdgeNum = doc->findEdge(room, corner, oldEdge);
3767 if (oldEdgeNum >= 0 && (oldEdge.type2 & etUnexplored))
3769 // deletedEdges.push_back(doc->getEdge(oldEdgeNum));
3770 deletedEdges.push_back(doc->edge[oldEdgeNum]);
3771 doc->deleteEdge(oldEdgeNum);
3774 // Add the new or changed edge:
3775 if (edgeTmp.type2 & etOneWay)
3777 edgeTmp.room2 = edgeTmp.room1;
3778 edgeTmp.end2 = edgeTmp.end1;
3779 edgeTmp.type1 = edgeTmp.type2 | (edgeTmp.type1 & etObstructed);
3780 edgeTmp.type2 = etNormal;
3781 edgeTmp.room1 = room;
3782 edgeTmp.end1 = corner;
3786 edgeTmp.room2 = room;
3787 edgeTmp.end2 = corner;
3791 deletedEdges.push_back(e2Tmp);
3793 if (deletedEdges.size())
3795 if (deletedEdges.size() == 1)
3796 doc->setUndoData(new UndoChangeEdge(bTmp, deletedEdges[0]));
3798 doc->setUndoData(new UndoChanges(bTmp, -1, 1, deletedEdges));
3801 doc->setUndoData(new UndoAdd(bTmp));
3803 doc->addEdge(edgeTmp);
3809 // We just deleted the old edge
3810 if ((e2Tmp.end1 == rcCorner)
3811 || (!(e2Tmp.type1 & etNoRoom2) && (e2Tmp.end2 == rcCorner)))
3813 doc->addEdge(e2Tmp); // Put it back temporarily
3814 selectOnlyRoom((e2Tmp.end1 == rcCorner) ? e2Tmp.room1 : e2Tmp.room2);
3815 deleteSelection(false); // Remove entire connection
3818 // not a connection to a corner
3819 doc->setUndoData(new UndoDelete(bTmp, e2Tmp));
3821 else if (selectedOne >= 0)
3822 makeRoomVisible(selectedOne); // We just clicked on a room corner
3824 // end if edge doesn't go anywhere
3826 else if (opInProgress == gmoChangeEdge)
3828 // We didn't change the edge
3829 if (selectedOne >= 0)
3830 makeRoomVisible(selectedOne);
3832 // else if (opInProgress == gmoSelectBox)
3834 // dc.SetROP2(R2_NOT);
3835 // QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3836 // dc.Rectangle(rTmp);
3837 // dc.SelectObject(oldBrush);
3839 else if (opInProgress == gmoControlDown)
3850 // end if room or page was already selected
3852 else if (opInProgress >= gmoDrag)
3854 QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3859 const QSize offset1(p.x(), p.y());
3860 doc->setUndoData(new UndoMove(doc->isDirty, offset1, numSelected, selected, numSelectedPages, selectedPage));
3862 for(int i=doc->room.size()-1; i>=0; i--)
3865 doc->moveRoom(i, offset1);
3868 for(int i=doc->page.size()-1; i>=0; i--)
3870 if (selectedPage[i])
3871 doc->movePage(i, offset1);
3875 // InvalidateRect(NULL);
3878 opInProgress = gmoNone;
3880 else if (event->button() == Qt::RightButton)
3882 //void MapView::OnRButtonUp(unsigned int nFlags, QPoint point)
3884 // if (scrollDragTimer)
3887 // KillTimer(scrollDragTimer);
3888 scrollDragTimer = 0;
3891 int room = doc->roomHit(point, &corner);
3892 cornerClicked = corner; // Bleah
3893 roomClicked = room; // Bleah
3897 if (showPages && (room = doc->pageBorderHit(point)) >= 0)
3904 mapContextMenu->popup(mapToGlobal(event->pos()));
3908 selectOnlyRoom(room);
3909 opInProgress = gmoNone;
3911 if ((corner != rcNone) && !doc->locked)
3913 iTmp = doc->findEdge(room, corner, edgeTmp);
3915 if ((iTmp >= 0) && !(edgeTmp.type2 & etNoRoom2))
3917 opInProgress = gmoAddCorner;
3918 addCornerAct->setEnabled(true);
3921 addCornerAct->setEnabled(false);
3924 roomContextMenu->popup(mapToGlobal(event->pos()));
3925 mouse = QPoint(0, 0);
3928 // dc.LPtoDP(&point);
3929 // ClientToScreen(&point);
3930 // pop->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
3931 // //opInProgress = gmoNone; // FIXME
3933 // else if (!scrollDrag || (GetCapture() != this))
3934 // CScrollZoomView::OnRButtonUp(nFlags, point);
3937 // if (opInProgress == gmoNone)
3938 // ReleaseCapture();
3940 // ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3942 // scrollDrag = false;
3952 int MapView::OnMouseActivate(CWnd * wnd, unsigned int hitTest, unsigned int message)
3954 const int result = CScrollZoomView::OnMouseActivate(wnd, hitTest, message);
3956 if ((result == MA_ACTIVATE) && GUEmapEatClicks)
3957 return MA_ACTIVATEANDEAT;
3963 bool MapView::OnMouseWheel(unsigned int nFlags, short zDelta, QPoint pt)
3965 if (nFlags == MK_CONTROL)
3967 OnViewZoom((zDelta > 0) ? ID_VIEW_ZOOM_IN : ID_VIEW_ZOOM_OUT);
3971 return CScrollZoomView::OnMouseWheel(nFlags, zDelta, pt);
3975 void MapView::OnSize(unsigned int nType, int cx, int cy)
3977 CScrollZoomView::OnSize(nType, cx, cy);
3979 if (initialized()) // Make sure view has been initialized
3984 void MapView::OnTimer(unsigned int idEvent)
3986 if (idEvent == menuTimer)
3988 // We've held the button down long enough, switch to dragging:
3990 KillTimer(scrollDragTimer);
3991 scrollDragTimer = 0;
3993 ::SetCursor(handCursor);
3996 CScrollZoomView::OnTimer(idEvent);
4001 // Update the view after the document changes:
4006 // A room has just been deleted
4008 // The number of pages has changed
4010 // A room comment was just changed
4011 // Do not invalidate the view unless pHint is not NULL.
4012 // dupNavigationMode:
4013 // We are entering navigation mode
4014 // Ignore pHint, do not invalidate the view, just make sure
4015 // exactly one room is selected and that it's visible.
4017 // NULL means invalidate the entire view (but see lHint)
4018 // Otherwise, it's a QRect* to the area to invalidate
4020 void MapView::OnUpdate(CView * pSender, LPARAM lHint, CObject * pHint)
4022 if (lHint == dupDeletedRoom && opInProgress != gmoDeleting && numSelected)
4024 // Clear selection unless this is the view doing the deleting:
4028 else if (lHint == dupPageCount && opInProgress != gmoDeleting)
4030 selectedPage.resize(GetDocument()->getPages().size(), 0);
4032 if (numSelectedPages)
4038 else if (lHint == dupRoomComment)
4040 // Update comment for this view, but don't override any other view:
4041 static_cast<CMapApp*>(AfxGetApp())->setComment(this,
4042 (selectedOne >= 0 ? &(GetDocument()->getRoom(selectedOne)) : NULL), false);
4045 return; // Don't invalidate entire view
4048 if (lHint == dupNavigationMode)
4050 const MapDoc * doc = GetDocument();
4052 const int numRooms = doc->room.size();
4054 if (selectedOne < 0 || doc->room[selectedOne].isCorner())
4057 makeRoomVisible(selectedOne);
4058 } // end if switching to navigation mode
4061 QRect rect(*reinterpret_cast<const QRect *>(pHint));
4065 rect.NormalizeRect();
4066 InvalidateRect(rect);
4068 else if (lHint != dupRoomComment)
4073 void MapView::OnUpdateEditAddCorner(CCmdUI* pCmdUI)
4075 pCmdUI->Enable(opInProgress == gmoAddCorner);
4079 void MapView::OnUpdateEditPaste(CCmdUI* pCmdUI)
4081 pCmdUI->Enable((static_cast<CMapApp*>(AfxGetApp())->getClipboard() != NULL)
4082 && !GetDocument()->locked);
4087 // Commands which require selected rooms and unlocked document:
4092 // Edit|Select connected
4094 void MapView::OnUpdateSelectedUnlocked(CCmdUI * pCmdUI)
4096 MapDoc * const doc = GetDocument();
4099 bool selected = numSelected;
4101 if ((pCmdUI->m_nID != ID_EDIT_SELECT_CONNECTED) && numSelectedPages)
4103 if ((pCmdUI->m_nID != ID_EDIT_COPY)
4104 && (doc->page.size() == numSelectedPages))
4105 selected = false; // Can't delete all pages
4110 pCmdUI->Enable(selected && !doc->locked);
4115 // Commands which require the document to be unlocked:
4119 void MapView::OnUpdateUnlocked(CCmdUI* pCmdUI)
4121 pCmdUI->Enable(!GetDocument()->locked);
4126 // Toggle the grid on and off:
4128 void MapView::OnViewGrid()
4130 showGrid = !showGrid;
4131 InvalidateRect(NULL);
4135 void MapView::OnUpdateViewGrid(CCmdUI * pCmdUI)
4137 pCmdUI->SetCheck(showGrid);
4141 void MapView::OnViewZoom(unsigned int cmd)
4143 ASSERT((cmd == ID_VIEW_ZOOM_IN) || (cmd == ID_VIEW_ZOOM_OUT));
4145 if (cmd == ID_VIEW_ZOOM_OUT)
4146 zoomTo(zoom % 10 ? zoom - zoom % 10 : zoom - 10);
4148 zoomTo(zoom + 10 - zoom % 10);
4152 void MapView::OnUpdateViewZoom(CCmdUI * pCmdUI)
4154 pCmdUI->Enable((pCmdUI->m_nID == ID_VIEW_ZOOM_IN)
4155 ? (zoom < maxZoom) : (zoom > minZoom));
4160 // Redraw the window:
4162 void MapView::OnWindowRefresh()
4164 InvalidateRect(NULL);
4168 /////////////////////////////////////////////////////////////////////////////
4169 /////////////////////////////////////////////////////////////////////////////
4170 // CRepaginateDlg dialog
4172 CRepaginateDlg::CRepaginateDlg(): CDialog(CRepaginateDlg::IDD, NULL)
4174 //{{AFX_DATA_INIT(CRepaginateDlg)
4178 BEGIN_MESSAGE_MAP(CRepaginateDlg, CDialog)
4179 //{{AFX_MSG_MAP(CRepaginateDlg)
4180 ON_BN_CLICKED(IDYES, OnYes)
4184 /////////////////////////////////////////////////////////////////////////////
4185 // CRepaginateDlg message handlers
4188 bool CRepaginateDlg::OnInitDialog()
4190 CDialog::OnInitDialog();
4192 SendDlgItemMessage(IDC_EXCLAMATION, STM_SETICON, (WPARAM) gueApp()->LoadStandardIcon(IDI_EXCLAMATION));
4193 MessageBeep(MB_ICONEXCLAMATION);
4195 return true; // return TRUE unless you set the focus to a control
4199 void CRepaginateDlg::OnYes()