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"
41 static const DirectionName directions[] = {
42 { rcN, "n" }, { rcS, "s" }, { rcE, "e" }, { rcW, "w" },
43 { rcNE, "ne" }, { rcNW, "nw" }, { rcSE, "se" }, { rcSW, "sw" },
44 { rcNNE, "nne" }, { rcNNW, "nnw" }, { rcSSE, "sse" }, { rcSSW, "ssw" },
45 { rcUp, "u" }, { rcDown, "d" }, { rcIn, "i" }, { rcOut, "o" },
46 { rcN, "north" }, { rcS, "south" }, { rcE, "east" }, { rcW, "west" },
47 { rcNE, "northeast" }, { rcNW, "northwest" },
48 { rcSE, "southeast" }, { rcSW, "southwest" },
49 { rcUp, "up" }, { rcDown, "down" }, { rcIn, "in" }, { rcOut, "out" },
53 static const char a2z[] = "abcdefghijklmnopqrstuvwxyz";
56 // Parse a direction word:
59 // text: The string to be tested (must be in lowercase, may be empty)
62 // text: Has the direction word and whitespace stripped from the beginning
65 // The direction indicated by the text
66 // rcNone if not a direction
68 RoomCorner parseDirection(string & text)
71 const CString word(text.SpanIncluding(a2z));
75 for(int i=0; directions[i].dir != rcNone; i++)
77 if (word == directions[i].name)
79 text = text.Mid(word.GetLength());
81 return directions[i].dir;
91 /////////////////////////////////////////////////////////////////////////////
92 // CRepaginateDlg dialog
94 class CRepaginateDlg : public CDialog
98 CRepaginateDlg(); // standard constructor
101 //{{AFX_DATA(CRepaginateDlg)
102 enum { IDD = IDD_REPAGINATE };
106 // ClassWizard generated virtual function overrides
107 //{{AFX_VIRTUAL(CRepaginateDlg)
112 // Generated message map functions
113 //{{AFX_MSG(CRepaginateDlg)
114 afx_msg void OnYes();
115 virtual bool OnInitDialog();
118 DECLARE_MESSAGE_MAP();
122 /////////////////////////////////////////////////////////////////////////////
123 /////////////////////////////////////////////////////////////////////////////
127 IMPLEMENT_DYNCREATE(CMapView, CScrollZoomView)
129 BEGIN_MESSAGE_MAP(CMapView, CScrollZoomView)
130 //{{AFX_MSG_MAP(CMapView)
131 ON_COMMAND(ID_EDIT_ADD_CORNER, OnEditAddCorner)
132 ON_COMMAND(ID_EDIT_ADD_PAGE, OnEditAddPage)
133 ON_COMMAND(ID_EDIT_ADD_ROOM, OnEditAddRoom)
134 ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
135 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
136 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
137 ON_COMMAND(ID_EDIT_PAGINATE, OnEditPaginate)
138 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
139 ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
140 ON_COMMAND(ID_EDIT_SELECT_CONNECTED, OnEditSelectConnected)
141 ON_COMMAND(ID_VIEW_GRID, OnViewGrid)
142 ON_COMMAND(ID_WINDOW_REFRESH, OnWindowRefresh)
143 ON_UPDATE_COMMAND_UI(ID_EDIT_ADD_CORNER, OnUpdateEditAddCorner)
144 ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateSelectedUnlocked)
145 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateSelectedUnlocked)
146 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateSelectedUnlocked)
147 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
148 ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateUnlocked)
149 ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_CONNECTED, OnUpdateSelectedUnlocked)
150 ON_UPDATE_COMMAND_UI(ID_VIEW_GRID, OnUpdateViewGrid)
153 ON_WM_LBUTTONDBLCLK()
163 ON_WM_MOUSEACTIVATE()
164 ON_BN_CLICKED(IDC_NAV_GO, OnNavGo)
165 ON_COMMAND_RANGE(ID_EDIT_PROPERTIES, ID_EDIT_MAP_PROPERTIES, OnEditProperties)
166 ON_COMMAND_RANGE(ID_NAV_GO_NORTH, ID_NAV_STUB2_OUT, OnNavGoDir)
167 ON_COMMAND_RANGE(ID_VIEW_ZOOM_IN, ID_VIEW_ZOOM_OUT, OnViewZoom)
168 ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_ZOOM_IN, ID_VIEW_ZOOM_OUT, OnUpdateViewZoom)
169 // Standard printing commands
170 ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
171 ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
172 ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
176 //===========================================================================
180 // opInProgress: Indicates what's happening while the mouse button is down
182 // iTmp: The edge that needs a corner
184 // bTmp: The modification state of the document
185 // edgeTmp: The edge being created
186 // e2Tmp: The original edge data (if iTmp >= 0)
187 // iTmp: -1 if this is a new edge, >=0 if changing an old edge
188 // p1Tmp: The start point (fixed) of the rubber band cursor
189 // p2Tmp: The end point (moving) of the rubber band cursor
191 // bTmp: The modification state of the document
192 // edgeTmp: Info about the other end of this edge
193 // iTmp: The edge number about to be changed
194 // p1Tmp, p2Tmp: The start point (fixed) of the rubber band cursor
196 // Used in OnEditClear to tell OnUpdate not to clear the selection
198 // p1Tmp: The fixed corner of the selection rectangle
199 // p2Tmp: The moving corner of the selection rectangle
200 // rTmp: The current selection rectangle
202 // bTmp: TRUE if the room or page was already selected
203 // b2Tmp: True if this is a page instead of a room
204 // iTmp: The room number where the button was pressed (if bTmp==TRUE)
205 // p1Tmp: The grid cell where the mouse button was pressed
206 // p2Tmp: The offset to the current grid cell (in logical coordinates)
208 // iTmp: The number of the selected page (-1 if a room)
209 // p1Tmp: The grid cell where the mouse button was pressed
210 // p2Tmp: The position of the selected room
211 // gmoDrag: (all dragging operations)
213 // The offset to the current grid cell (in logical coordinates)
215 // A rectangle enclosing the original position of all rooms being
216 // dragged. Used to ensure we don't push a room off the edge.
218 // The font used for room names
220 // The number of rooms currently selected
222 // The number of pages currently selected
224 // The number of the page being printed or previewed
225 // 0 means normal screen display
227 // True if we are in the middle of a scroll-by-drag operation
229 // The point under the cursor when a scrollDrag started
231 // The selected room's number, if exactly 1 room is selected
234 // selected[N] is non-zero iff room N is selected
235 // 2 means the room should stay selected during gmoSelectBox
237 // selectedPage[N] is non-zero iff page N is selected
238 // 2 means the page should stay selected during gmoSelectBox
240 // Zoom factor in percent
244 // CMapView construction/destruction
246 MapView::MapView(QWidget * parent/*= NULL*/): QWidget(parent),
247 opInProgress(gmoNone), hoveredEdge(-1), printingPage(0), scrollDrag(false),
248 scrollDragTimer(0), font("Arial", 24), showCorners(false), showGrid(true),
249 // showPages(false), zoom(100), shiftDown(false), ctrlDown(false),
250 showPages(false), zoom(20.0), shiftDown(false), ctrlDown(false),
251 altDown(false), mouseDown(false), offset(0, 0)
255 selectedPage.resize(doc->page.size(), 0);
256 clearSelection(false);
258 setFocusPolicy(Qt::StrongFocus);
259 // Make sure we get *ALL* mouse move events!
260 setMouseTracking(true);
262 // Set the document size:
263 //scrollzoom.cpp -> init(4 * gridX, doc->getDocSize(), QSize(gridX, gridY));
264 // CClientDC dc(this);
267 // memset(&lf, 0, sizeof(lf));
269 // strcpy(lf.lfFaceName, "Arial");
270 // font.CreateFontIndirect(&lf);
273 // OnUpdate(NULL, dupNavigationMode, NULL);
275 // CScrollZoomView::OnInitialUpdate();
276 // setScrollBars(); // Now fix the scroll bars
279 deleteRoomAct = CreateAction("Delete", "Delete room", "Deletes selected rooms", QIcon(), QKeySequence(), false, this);
280 connect(deleteRoomAct, SIGNAL(triggered()), this, SLOT(HandleDelete()));
282 roomPropertiesAct = CreateAction("Properties...", "Properties", "Opens the properties dialog for this room", QIcon(), QKeySequence(), false, this);
283 connect(roomPropertiesAct, SIGNAL(triggered()), this, SLOT(HandleRoomProperties()));
285 mapPropertiesAct = CreateAction("Properties...", "Properties", "Opens the properties dialog for this map", QIcon(), QKeySequence(), false, this);
286 connect(mapPropertiesAct, SIGNAL(triggered()), this, SLOT(HandleMapProperties()));
288 selectConnectedAct = CreateAction("Select connected rooms", "Select connected", "Selects connected rooms", QIcon(), QKeySequence(), false, this);
290 addCornerAct = CreateAction("Add corner", "Add corner", "Adds a corner to the selected room", QIcon(), QKeySequence(), false, this);
291 connect(addCornerAct, SIGNAL(triggered()), this, SLOT(HandleAddCorner()));
293 addRoomAct = CreateAction("Add room", "Add room", "Adds a rom to the map", QIcon(), QKeySequence(), false, this);
294 connect(addRoomAct, SIGNAL(triggered()), this, SLOT(HandleAddRoom()));
296 addPageAct = CreateAction("Add Page", "Add Page", "Adds a page to the map", QIcon(), QKeySequence(), false, this);
298 addUnexploredAct = CreateAction("Unexplored", "Unexplored", "Adds an unexplored notation to the selected corner", QIcon(), QKeySequence(), false, this);
299 connect(addUnexploredAct, SIGNAL(triggered()), this, SLOT(HandleAddUnexplored()));
301 addLoopBackAct = CreateAction("Loop back", "Loop back", "Adds a loop back notation to the selected corner", QIcon(), QKeySequence(), false, this);
302 connect(addLoopBackAct, SIGNAL(triggered()), this, SLOT(HandleAddLoopBack()));
304 addNoExitAct = CreateAction("No Exit", "No Exit", "Adds a no exit notation to the selected corner", QIcon(), QKeySequence(), false, this);
305 connect(addNoExitAct, SIGNAL(triggered()), this, SLOT(HandleAddNoExit()));
307 addUpAct = CreateAction("Up", "Up", "Marks the selected corner as Up", QIcon(), QKeySequence(), false, this);
308 connect(addUpAct, SIGNAL(triggered()), this, SLOT(HandleAddUp()));
310 addDownAct = CreateAction("Down", "Down", "Marks the selected corner as Down", QIcon(), QKeySequence(), false, this);
311 connect(addDownAct, SIGNAL(triggered()), this, SLOT(HandleAddDown()));
313 addInAct = CreateAction("In", "In", "Marks the selected corner as In", QIcon(), QKeySequence(), false, this);
314 connect(addInAct, SIGNAL(triggered()), this, SLOT(HandleAddIn()));
316 addOutAct = CreateAction("Out", "Out", "Marks the selected corner as Out", QIcon(), QKeySequence(), false, this);
317 connect(addOutAct, SIGNAL(triggered()), this, SLOT(HandleAddOut()));
319 addOneWayAct = CreateAction("One Way", "One Way", "Marks the selected corner as one way exit", QIcon(), QKeySequence(), false, this);
320 connect(addOneWayAct, SIGNAL(triggered()), this, SLOT(HandleAddOneWay()));
322 clearOneWayAct = CreateAction("Two Way", "Two Way", "Marks the selected corner as traversable both ways", QIcon(), QKeySequence(), false, this);
323 connect(clearOneWayAct, SIGNAL(triggered()), this, SLOT(HandleClearOneWay()));
325 addRestrictedAct = CreateAction("Restricted", "Restricted", "Marks the selected corner as a restricted exit", QIcon(), QKeySequence(), false, this);
326 connect(addRestrictedAct, SIGNAL(triggered()), this, SLOT(HandleAddRestricted()));
328 clearRestrictedAct = CreateAction("Unrestricted", "Unrestricted", "Marks the selected corner as an unrestricted exit", QIcon(), QKeySequence(), false, this);
329 connect(clearRestrictedAct, SIGNAL(triggered()), this, SLOT(HandleClearRestricted()));
332 roomContextMenu = new QMenu(this);
333 mapContextMenu = new QMenu(this);
335 roomContextMenu->addAction(deleteRoomAct);
336 roomContextMenu->addAction(roomPropertiesAct);
337 roomContextMenu->addAction(selectConnectedAct);
338 roomContextMenu->addAction(addCornerAct);
339 QMenu * sub = roomContextMenu->addMenu(tr("Edge"));
340 sub->addAction(addLoopBackAct);
341 sub->addAction(addUnexploredAct);
342 sub->addAction(addNoExitAct);
343 sub->addAction(addUpAct);
344 sub->addAction(addDownAct);
345 sub->addAction(addInAct);
346 sub->addAction(addOutAct);
347 sub->addAction(addOneWayAct);
348 sub->addAction(clearOneWayAct);
349 sub->addAction(addRestrictedAct);
350 sub->addAction(clearRestrictedAct);
352 mapContextMenu->addAction(mapPropertiesAct);
353 mapContextMenu->addAction(addRoomAct);
354 mapContextMenu->addAction(addPageAct);
355 mapContextMenu->addAction(deleteRoomAct);
361 // gueApp()->closingView(this); // Remove any comment for this view
364 void MapView::DrawArrowhead(QPainter * painter, QPointF head, QPointF tail)
368 const double angle = Angle(tail - head);
369 const double orthoAngle = angle + QTR_TAU;
370 const double size = 5.0;
372 QPointF ortho(cos(orthoAngle), sin(orthoAngle));
373 QPointF unit = UnitVector(head - tail);
375 QPointF p1 = head - (unit * 9.0 * size);
376 QPointF p2 = p1 + (ortho * 3.0 * size);
377 QPointF p3 = p1 - (ortho * 3.0 * size);
379 arrow << head << p2 << p3;
380 painter->drawPolygon(arrow);
383 void MapView::DrawNoExit(QPainter * painter, QPointF head, QPointF tail)
385 const double angle = Angle(tail - head);
386 const double orthoAngle = angle + QTR_TAU;
387 const double size = 5.0;
389 QPointF ortho(cos(orthoAngle), sin(orthoAngle));
390 QPointF unit = UnitVector(head - tail);
392 QPointF p2 = head + (ortho * 3.0 * size);
393 QPointF p3 = head - (ortho * 3.0 * size);
395 painter->drawLine(p2, p3);
401 void MapView::paintEvent(QPaintEvent * /*event*/)
403 const char edgeLabel[] = "UDIO";
405 const QPoint edgLblOffset[] = {
407 { roomWidth / 2 + 24, -13 }, { roomWidth / 2 - 48, roomHeight + 52 },
408 { roomWidth + 24, roomHeight / 2 + 57 }, { -48, roomHeight / 2 - 19 },
410 { roomWidth - 48, -13 }, { 24, -13 },
411 { roomWidth - 48, roomHeight + 52 }, { 24, roomHeight + 52 },
412 // NNE, NNW, SSE, SSW
413 { 3 * roomWidth / 4 + 24, -13 }, { roomWidth / 4 + 24, -13 },
414 { 3 * roomWidth / 4 - 48, roomHeight + 52 },
415 { roomWidth / 4 - 48, roomHeight + 52 }
418 // Offsets used when the label collides with the line...
419 const QPoint edgLblOffsetEx[] = {
421 { -48, 0 }, { 48, 0 }, { 0, -76 }, { 0, 76 },
423 { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
424 // NNE, NNW, SSE, SSW
425 { -48, 0 }, { -48, 0 }, { 48, 0 }, { 48, 0 }
430 cantGoAngleX = gridY / 6,
431 cantGoAngleY = gridX / 6;
434 rcNE, rcNW, rcSE, rcSW,
435 rcNNE, rcNNW, rcSSE, rcSSW,
437 QRect cornerRect[] = {
438 { roomWidth * 5 / 16, 0, roomWidth * 6 / 16, roomHeight / 4 },
439 { roomWidth * 5 / 16, roomHeight * 3 / 4, roomWidth * 6 / 16, roomHeight / 4 },
440 { roomWidth * 3 / 4, roomHeight / 4, roomWidth / 4, roomHeight / 2 },
441 { 0, roomHeight / 4, roomWidth / 4, roomHeight / 2 },
442 { roomWidth * 13 / 16, 0, roomWidth * 3 / 16, roomHeight / 4 },
443 { 0, 0, roomWidth * 3 / 16, roomHeight / 4 },
444 { roomWidth * 13 / 16, roomHeight * 3 / 4, roomWidth * 3 / 16, roomHeight / 4 },
445 { 0, roomHeight * 3 / 4, roomWidth * 3 / 16, roomHeight / 4 },
446 { roomWidth * 11 / 16, 0, roomWidth * 2 / 16, roomHeight / 4 },
447 { roomWidth * 3 / 16, 0, roomWidth * 2 / 16, roomHeight / 4 },
448 { roomWidth * 11 / 16, roomHeight * 3 / 4, roomWidth * 2 / 16, roomHeight / 4 },
449 { roomWidth * 3 / 16, roomHeight * 3 / 4, roomWidth * 2 / 16, roomHeight / 4 },
452 QPainter painter(this);
453 QPainter * dc = &painter;
455 painter.setRenderHint(QPainter::Antialiasing);
460 dc->setPen(QColor(0xFF, 0x7F, 0x00));
461 // dc->drawText(20, 50, QString("<%1, %2>").arg(mouse.x()).arg(mouse.y()));
462 dc->drawText(20, 50, QString("<%1, %2>, %3").arg(mouse.x()).arg(mouse.y()).arg(hoveredEdge));
467 t1.scale(zoom / 100.0, zoom / 100.0);
468 // t1.scale(+0.20, +0.20);
469 // t1.scale(+0.40, +0.40);
470 t1.translate(offset.x(), offset.y());
471 painter.setTransform(t1);
473 // QPainter painter(this);
475 painter.setPen(QPen(Qt::blue, 1, Qt::DashLine));
476 painter.drawRect(0, 0, 100, 100);
478 QTransform transform;
479 transform.translate(50, 50);
480 transform.rotate(45);
481 transform.scale(0.5, -1.0);
482 painter.setTransform(transform);
484 painter.setFont(QFont("Helvetica", 24));
485 painter.setPen(QPen(Qt::black, 1));
486 painter.drawText(20, 10, "QTransform");
496 lb.lbStyle = BS_SOLID;
497 lb.lbColor = RGB(0, 0, 0);
499 if (!focusBrush.CreateHatchBrush(HS_BDIAGONAL, PALETTERGB(128, 128, 128))
500 || !penEdge.CreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT, penEdgeWidth, &lb)
501 || !penGrid.CreatePen(PS_DOT, 0, RGB(0, 0, 0))
502 || !penRoom.CreatePen(PS_SOLID, penRoomWidth, RGB(0, 0, 0)))
505 QBrush * const whiteBrush = dc->GetCurrentBrush();
506 QPen * const oldPen = dc->GetCurrentPen();
507 QFont * const oldFont = dc->SelectObject(&font);
508 const unsigned int oldAlign = dc->SetTextAlign(TA_CENTER | TA_BASELINE | TA_NOUPDATECP);
510 QBrush focusBrush(QColor(0xC0, 0xC0, 0xC0), Qt::BDiagPattern);
511 QPen penEdge(QColor(0, 0, 0), penEdgeWidth);
512 QPen penEdgeDashed(QColor(0, 0, 0), penEdgeWidth, Qt::DashLine);
513 QPen penEdgeHover(QColor(0xFF, 0, 0), penEdgeWidth);
514 QPen penEdgeDashedHover(QColor(0xFF, 0, 0), penEdgeWidth, Qt::DashLine);
515 QPen penGrid(QColor(0, 0, 0), 1, Qt::DotLine);
516 QPen penRoom(QColor(0, 0, 0), 1);
522 // Clipping in OnPrepareDC screws up print preview
523 QRect r = doc->getPageRect(printingPage - 1);
524 /// dc->SetWindowOrg(r.left(), r.bottom()); // FIXME adjust for margins
525 /// dc->IntersectClipRect(&r);
526 // dc->setTransform(QTransform::translate(r.left(), r.bottom()));
530 // Draw the page outlines:
531 if ((showPages || numSelectedPages) && !dc->IsPrinting())
533 dc->SetBkMode(TRANSPARENT);
535 lb.lbColor = RGB(192, 192, 192);
536 VERIFY(penPage.CreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT, penPageWidth, &lb));
537 dc->SelectObject(&penPage);
538 const PageVec & pageVec = doc->page;
541 dc->SelectStockObject(NULL_BRUSH);
542 QBrush * const nullBrush = dc->GetCurrentBrush();
543 bool focused = false;
545 for(PageConstItr p=pageVec.begin(); p!=pageVec.end(); ++p, ++pNum)
547 doc->getPageRect(*p, r);
549 if (dc->RectVisible(r))
551 const bool selectedI = selectedPage[pNum - 1] != 0;
553 if (!selectedI && !showPages)
554 continue; // Only show selected pages
556 if (selectedI != focused)
559 dc->SelectObject(focused ? &focusBrush : nullBrush);
561 /// CRgn inside,outside;
562 /// VERIFY(outside.CreateRectRgnIndirect(&r));
563 /// VERIFY(inside.CreateRectRgn(r.left + gridX, r.bottom - gridY,
564 /// r.right - gridX, r.top + gridY));
565 /// outside.CombineRgn(&outside, &inside, RGN_DIFF);
566 /// dc->FillRgn(&outside, &focusBrush);
568 dc->Rectangle(r.left + penPageWidth / 4, r.bottom - penPageWidth / 4, r.right, r.top);
570 _itot(pNum, pageNum, 10);
571 const int pageNumL = strlen(pageNum);
573 for(i=r.left+3*gridX/2; i<r.right-gridX; i+=gridX)
575 dc->TextOut(i, r.bottom - gridY * 7 / 8, pageNum, pageNumL);
576 dc->TextOut(i, r.top + gridY / 4, pageNum, pageNumL);
579 for(i=r.bottom-gridY*15/8; i>r.top+gridY; i-=gridY)
581 dc->TextOut(r.left + gridX / 2 + penPageWidth / 4, i, pageNum, pageNumL);
582 dc->TextOut(r.right- gridX / 2 - penPageWidth / 4, i, pageNum, pageNumL);
584 } // end if page visible
587 dc->SetBkMode(OPAQUE);
588 dc->SelectObject(whiteBrush);
589 dc->SelectObject(&penEdge); // Before destroying penPage
591 else // not showing pages
592 dc->SelectObject(&penEdge);
597 // Draw the background grid:
598 if (showGrid)// && (zoom >= minZoomGrid))//&& !dc->IsPrinting())
601 printf(" Preparing to draw grid...\n");
602 // dc->SetBkMode(TRANSPARENT);
603 // dc->SelectObject(&penGrid);
604 dc->setBrush(Qt::NoBrush);
608 // dc->GetClipBox(&clip);
609 // clip = dc->viewport();
610 // clip = dc->window();
612 printf(" clip = %i, %i, %i, %i\n", clip.x(), clip.y(), clip.width(), clip.height());
614 clip.setLeft(clip.left() - (clip.left() % gridX));
615 clip.setBottom(clip.bottom() - (clip.bottom() % gridY + gridY));
617 if (clip.top() < -gridY)
618 clip.setTop(clip.top() + gridY);
620 QSize docSize(doc->getDocSize());
621 docSize.setHeight(docSize.height() * -1);
623 if (clip.right() > docSize.width())
624 clip.setRight(docSize.width());
626 if (clip.bottom() < docSize.height())
627 clip.setBottom(docSize.height());
629 printf(" clip = %i, %i, %i, %i; docSize = %i, %i\n", clip.left(), clip.right(), clip.bottom(), clip.top(), docSize.width(), docSize.height());
630 for(i=clip.left(); i<=clip.right(); i+=gridX)
632 dc->drawLine(i, 0, i, docSize.height());
635 // for(i=clip.bottom(); i<=clip.top(); i+=gridY)
636 for(i=-clip.bottom(); i<=clip.top(); i+=gridY)
638 // dc->drawLine(0, i, docSize.width(), i);
639 dc->drawLine(0, i, docSize.width(), i);
642 // dc->SetBkMode(OPAQUE);
644 dc->setBrush(Qt::NoBrush);
646 QSize docSize(doc->docSize);
648 for(int i=0; i<=docSize.width(); i+=gridX)
649 dc->drawLine(i, 0, i, docSize.height());
651 for(int i=0; i<=docSize.height(); i+=gridY)
652 dc->drawLine(0, i, docSize.width(), i);
656 // Draw the room connections
657 int i = doc->edge.size();
658 EdgeConstItr edge = doc->edge.end() - 1;
659 RoomConstItr room = doc->room.v.begin();
666 doc->getEdgePoints(*edge, start, end);
668 // Offset the edge if we're dragging it...
669 if (opInProgress >= gmoDrag)
671 if (selected[edge->room1])
674 if ((selected[edge->room1] && (edge->type1 & etNoRoom2))
675 || selected[edge->room2])
679 if (edge->type1 & etObstructed)
680 dc->setPen(hoveredEdge == i ? penEdgeDashedHover : penEdgeDashed);
682 dc->setPen(hoveredEdge == i ? penEdgeHover : penEdge);
684 dc->drawLine(start, end);
686 if (edge->type1 & etOneWay)
689 dc->setBrush(Qt::black);
690 DrawArrowhead(dc, end, start);
693 else if (edge->type1 & etNoExit)
695 DrawNoExit(dc, end, start);
697 else if (edge->type1 & etLoopBack)
699 const double angle = Angle(start - end);
700 const double orthoAngle = angle + QTR_TAU;
701 const double size = 5.0;
703 QPointF ortho(cos(orthoAngle), sin(orthoAngle));
704 QPointF unit = UnitVector(end - start);
706 QPointF p1 = start + (ortho * 6.0 * size);
707 QPointF p2 = end + (ortho * 6.0 * size);
708 QPointF p3 = end + (ortho * 3.0 * size);
709 p3.rx() -= 3.0 * size;
710 p3.ry() -= 3.0 * size;
711 dc->drawLine(p1, p2);
712 QRectF r(p3, QSizeF(6.0 * size, 6.0 * size));
713 // 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. :-/
714 QPoint funnyStart(start.x(), start.y() * -1), funnyEnd(end.x(), end.y() * -1);
715 double funnyAngle = Angle(funnyStart - funnyEnd);
716 dc->drawArc(r, (int)((funnyAngle + QTR_TAU) * RADIANS_TO_DEGREES) * 16, 180 * 16);
718 dc->setBrush(Qt::black);
719 DrawArrowhead(dc, p1, p2);
725 if (edge->type1 & etDirection)
727 QPoint ex = QPoint(0, 0);
728 double angle = Angle(end - start);
730 // Adjust text position if it runs into an edge
731 if (((edge->end1 == rcNNW || edge->end1 == rcN || edge->end1 == rcNNE) && (angle > THREE_QTR_TAU && angle < TAU))
732 || ((edge->end1 == rcSSW || edge->end1 == rcS || edge->end1 == rcSSE) && (angle > QTR_TAU && angle < HALF_TAU))
733 || ((edge->end1 == rcE) && (angle > 0 && angle < QTR_TAU))
734 || ((edge->end1 == rcW) && (angle > HALF_TAU && angle < THREE_QTR_TAU)))
736 ex = edgLblOffsetEx[edge->end1];
739 const QPoint & p = room[edge->room1]->pos;
740 elBuf[0] = edgeLabel[(edge->type1 & etDirection) - 1];
741 dc->drawText(p + edgLblOffset[edge->end1] + ex, elBuf);
744 // Make sure there's an actual room #2 for this edge...
745 if (!(edge->type1 & etNoRoom2) && (edge->type2 & etDirection))
747 QPoint ex = QPoint(0, 0);
748 double angle = Angle(start - end);
750 // Adjust text position if it runs into an edge
751 if (((edge->end2 == rcNNW || edge->end2 == rcN || edge->end2 == rcNNE) && (angle > THREE_QTR_TAU && angle < TAU))
752 || ((edge->end2 == rcSSW || edge->end2 == rcS || edge->end2 == rcSSE) && (angle > QTR_TAU && angle < HALF_TAU))
753 || ((edge->end2 == rcE) && (angle > 0 && angle < QTR_TAU))
754 || ((edge->end2 == rcW) && (angle > HALF_TAU && angle < THREE_QTR_TAU)))
756 ex = edgLblOffsetEx[edge->end2];
759 const QPoint & p = room[edge->room2]->pos;
760 elBuf[0] = edgeLabel[(edge->type2 & etDirection) - 1];
761 dc->drawText(p + edgLblOffset[edge->end2] + ex, elBuf);
769 bool focused = false;
772 for(const int count=doc->room.size(); i<count; i++, room++)
774 QRect r = (*room)->getRect();
775 QPoint cp = (*room)->GetCenter();
777 // Translate room if dragging operation in progress
778 if ((opInProgress >= gmoDrag) && selected[i])// && !(*room)->isCorner())
780 r.translate(p2Tmp.x(), p2Tmp.y());
784 // if (!dc->RectVisible((*room)->getRect(r)))
785 // if (!r.intersects(dc->viewport()))
788 const bool selectedI = (selected[i] != 0);
790 if (selectedI != focused)
793 bool hovered = r.contains(mouse);
794 RoomCorner rc = rcNone;
797 rc = doc->CornerHit(mouse - r.topLeft());
799 dc->setPen(hovered ? penEdgeHover : penRoom);
801 dc->setBrush(focused ? focusBrush : Qt::white);
803 bool needOpaqueText = false;
805 if ((*room)->flags & rfBorder)
811 dc->setBrush(focusBrush);
814 else // Room has no border and is not focused
815 needOpaqueText = !(*room)->name.empty();
817 if ((*room)->isCorner() && (focused || showCorners))
819 QRect centerRect(cp - QPoint(20, 20), QSize(40, 40));
821 dc->setPen(Qt::black);
822 dc->setBrush(Qt::black);
823 dc->drawEllipse(centerRect);
827 // dc->SetBkMode(TRANSPARENT);
829 if ((rc >= 0) && (rc <= 11) && ((*room)->isCorner() == false))
831 QRect rCrnr = cornerRect[rc].translated(r.topLeft());
832 // rCrnr.translate(r.topLeft());
834 dc->setPen(Qt::NoPen);
835 dc->setBrush(Qt::green);
840 if (!(*room)->note.empty())
842 QPoint noteOffset(roomWidth - 29, 57);
845 dc->setPen(QColor(0xFF, 0x00, 0x00));
846 dc->drawText(r.topLeft() + noteOffset, "*");
850 // if (needOpaqueText)
851 // dc->SetBkMode(OPAQUE);
853 // Shrink the rect left/right margins just a bit
854 QMargins margin(20, 0, 20, 0); // LTRB
855 r = r.marginsRemoved(margin);
857 //dc->setPen(Qt::blue);
861 dc->drawText(r, Qt::AlignCenter | Qt::TextWordWrap, (*room)->name.c_str());
863 // dc->SetBkMode(OPAQUE);
866 // JLH: Draw special stuffs (crap that was stuffed into OnMouseDown & OnMouseMove & the like...
867 if (opInProgress == gmoSelectBox)
869 dc->setPen(QPen(QColor(0x00, 0xFF, 0x00, 0xFF)));
870 dc->setBrush(QBrush(QColor(0x00, 0xFF, 0x00, 0x64)));
873 else if (opInProgress == gmoAddEdge)
876 dc->drawLine(p1Tmp, p2Tmp);
880 void MapView::keyPressEvent(QKeyEvent * event)
882 bool oldShift = shiftDown;
883 bool oldCtrl = ctrlDown;
884 bool oldAlt = altDown;
886 if (event->key() == Qt::Key_Shift)
888 else if (event->key() == Qt::Key_Control)
890 else if (event->key() == Qt::Key_Alt)
893 double oldZoom = zoom;
895 if (event->key() == Qt::Key_0)
897 else if (event->key() == Qt::Key_9)
899 else if (event->key() == Qt::Key_8)
901 else if (event->key() == Qt::Key_7)
903 else if (event->key() == Qt::Key_6)
905 else if (event->key() == Qt::Key_5)
907 else if (event->key() == Qt::Key_4)
909 else if (event->key() == Qt::Key_3)
911 else if (event->key() == Qt::Key_2)
913 else if (event->key() == Qt::Key_1)
920 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
923 ToolHandler(ToolKeyDown, Point(0, 0));
929 if (oldAlt != altDown)
932 setCursor(Qt::SizeAllCursor);
933 // oldPoint = oldScrollPoint;
937 if (select.size() > 0)
939 if (event->key() == Qt::Key_Up)
941 TranslateObjects(select, Point(0, +1.0));
944 else if (event->key() == Qt::Key_Down)
946 TranslateObjects(select, Point(0, -1.0));
949 else if (event->key() == Qt::Key_Right)
951 TranslateObjects(select, Point(+1.0, 0));
954 else if (event->key() == Qt::Key_Left)
956 TranslateObjects(select, Point(-1.0, 0));
963 void MapView::keyReleaseEvent(QKeyEvent * event)
965 bool oldShift = shiftDown;
966 bool oldCtrl = ctrlDown;
967 bool oldAlt = altDown;
969 if (event->key() == Qt::Key_Shift)
971 else if (event->key() == Qt::Key_Control)
973 else if (event->key() == Qt::Key_Alt)
977 if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
980 ToolHandler(ToolKeyUp, Point(0, 0));
986 if (oldAlt != altDown)
989 setCursor(Qt::ArrowCursor);
994 /////////////////////////////////////////////////////////////////////////////
996 //--------------------------------------------------------------------
997 // Record which page we're printing, if any:
1002 void MapView::OnPrepareDC(QPainter * dc, CPrintInfo * pInfo)
1004 CScrollZoomView::OnPrepareDC(dc, pInfo);
1007 printingPage = pInfo->m_nCurPage;
1012 bool MapView::OnPreparePrinting(CPrintInfo * pInfo)
1014 // Require registration before printing map with more than 10 rooms:
1015 MapDoc * const doc = GetDocument();
1018 if (doc->needRepaginate())
1022 switch (d.DoModal())
1032 pInfo->SetMaxPage(doc->page.size());
1033 return DoPreparePrinting(pInfo);
1036 void MapView::OnBeginPrinting(QPainter * /*pDC*/, CPrintInfo * /*pInfo*/)
1038 // Don't show selection or corners while printing:
1039 // We do this here instead of OnPreparePrinting, because OnEndPrinting
1040 // is only guaranteed to be called if OnBeginPrinting is.
1043 showCorners = false;
1046 void MapView::OnEndPrinting(QPainter* /*pDC*/, CPrintInfo* /*pInfo*/)
1048 if (GetDocument()->locked)
1049 OnUpdate(NULL, dupNavigationMode, NULL); // Select a room
1054 // CMapView miscellaneous
1059 // point: The point where the mouse was clicked (in logical coordinates)
1061 void MapView::addRoom(QPoint & point)
1063 QSize docSize(doc->docSize);
1065 if (doc->locked || (point.x() > docSize.width()) || (point.y() > docSize.height()))
1067 // MessageBeep(MB_ICONASTERISK);
1071 point.rx() -= point.x() % gridX;
1072 point.ry() -= point.y() % gridY;
1076 else if (point.x() + roomWidth > docSize.width())
1077 point.setX(docSize.width() - roomWidth);
1081 else if (point.y() + roomHeight > docSize.height())
1082 point.setY(docSize.height() - roomHeight);
1085 const bool wasModified = doc->isDirty;
1086 const int rNum = doc->addRoom(point);
1091 selectDone(); // FIXME disable floating comments
1092 editProperties(epuAddRoom, wasModified);
1096 // Make sure we aren't dragging a room off the edge:
1099 // p: The proposed offset for the selected rooms
1102 // rTmp: Rectangle enclosing the original positions of the selected rooms
1105 // p: The corrected (if necessary) offset for selected rooms
1107 void MapView::adjustOffset(QPoint & p) const
1109 const QSize size(doc->docSize);
1111 if (p.x() + rTmp.left() < 0)
1112 p.rx() = -rTmp.left();
1114 if (p.x() + rTmp.right() > size.width())
1115 p.rx() = size.width() - rTmp.right();
1117 if (p.y() + rTmp.top() < 0)
1118 p.ry() = -rTmp.top();
1120 if (p.y() + rTmp.bottom() > size.height())
1121 p.ry() = size.height() - rTmp.bottom();
1125 // Deselect all rooms:
1128 // update: TRUE means the selected rooms should be redrawn
1130 void MapView::clearSelection(bool update/* = true*/)
1132 if (doc->room.size() != selected.size())
1133 selected.resize(doc->room.size(), 0);
1135 if (update && numSelected)
1141 RoomConstItr room = doc->room.getVector().begin();
1143 for(int i=doc->room.size()-1; numSelected && i>=0; --i)
1148 room[i]->getRect(r);
1159 for(ByteItr b=selected.begin(); b!=selected.end(); ++b)
1162 deselectPages(update);
1166 // Compute a rectangle enclosing all selected rooms:
1175 // r: A rectangle enclosing all selected rooms
1177 void MapView::computeSelectedRect(QRect & r) const
1179 // r.SetRectEmpty();
1184 RoomConstItr room = doc->room.getVector().begin();
1186 for(int i=doc->room.size()-1; i>=0; i--)
1189 r |= room[i]->getRect();
1193 if (numSelectedPages)
1195 for(int i=doc->page.size()-1; i>=0; i--)
1197 if (selectedPage[i])
1198 r |= doc->getPageRect(i);
1207 // n: The page number to be deselected
1208 // update: TRUE means the page should be redrawn if necessary
1210 // Output Variables:
1215 void MapView::deselectPage(int n, bool update/* = true*/)
1217 ASSERT((n >= 0) && (n < doc->page.size()));
1219 if (selectedPage[n])
1221 selectedPage[n] = false;
1223 if ((--numSelectedPages == 0) && (numSelected == 1))
1225 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1226 ; // Find the selected room
1237 doc->getPageRect(n, r);
1247 // Deselect all pages:
1250 // update: TRUE means the selected pages should be redrawn
1252 // Output Variables:
1257 void MapView::deselectPages(bool update/* = true*/)
1259 ASSERT(selectedPage.size() == doc->page.size());
1261 if (update && numSelectedPages)
1264 // CClientDC dc(this);
1265 // OnPrepareDC(&dc);
1267 for(int i=doc->page.size()-1; numSelectedPages&&i>=0; i--)
1269 if (selectedPage[i])
1272 r = doc->getPageRect(i);
1276 // make sure it disappears
1277 r.setBottom(r.bottom() + penPageWidth);
1278 r.setLeft(r.left() - penPageWidth);
1282 // r.NormalizeRect();
1283 // InvalidateRect(r);
1288 numSelectedPages = 0;
1290 for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
1293 if (numSelected == 1)
1295 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1296 ; // Find the selected room
1306 // n: The room number to be deselected
1307 // update: TRUE means the room should be redrawn if necessary
1309 // Output Variables:
1314 void MapView::deselectRoom(RoomNum n, bool update/* = true*/)
1316 ASSERT((n >= 0) && (n < doc->room.size()));
1318 if (doc->room.size() != selected.size())
1319 selected.resize(doc->room.size(), 0);
1323 selected[n] = false;
1325 if ((--numSelected == 1) && !numSelectedPages)
1327 for (selectedOne=0; !selected[selectedOne]; selectedOne++)
1328 ; // Find the selected room
1339 doc->room[n].getRect(r);
1349 // Update the room comments dialog after the selection changes:
1351 void MapView::selectDone()
1353 // static_cast<CMapApp *>(AfxGetApp())->setComment(this, (selectedOne >= 0 ? &(GetDocument()->getRoom(selectedOne)) : NULL));
1360 // n: The page number to select
1361 // update: TRUE means the selected page should be redrawn if necessary
1363 // Output Variables:
1368 void MapView::selectPage(int n, bool update/* = true*/)
1370 if (!selectedPage[n])
1372 selectedPage[n] = true;
1382 doc->getPageRect(n, r);
1395 // n: The room number to select
1396 // update: TRUE means the selected room should be redrawn if necessary
1398 // Output Variables:
1403 void MapView::selectRoom(RoomNum n, bool update/*= true*/)
1405 if (doc->room.size() != selected.size())
1406 selected.resize(doc->room.size(), 0);
1412 if ((++numSelected == 1) && !numSelectedPages)
1423 doc->room[n].getRect(r);
1432 void MapView::selectOnlyRoom(RoomNum n)
1434 if (selectedOne != n)
1443 // Make sure that a room is visible:
1446 // n: The number of the room to be made visible
1448 void MapView::makeRoomVisible(RoomNum n)
1452 GetClientRect(&clientRect);
1453 QSize viewSize(clientRect.right, clientRect.bottom);
1455 CClientDC cdc(this);
1457 cdc.DPtoLP(&viewSize);
1459 const QSize docSize(doc->getDocSize());
1460 const QPoint curPos(getScrollPosition());
1462 doc->room[n].getRect(roomRect);
1464 QPoint newPos(curPos);
1466 if (roomRect.left - curPos.x < gridX)
1468 newPos.x -= curPos.x - roomRect.left + gridX;
1473 else if (roomRect.right - curPos.x + gridX > viewSize.cx)
1475 newPos.x += roomRect.right + gridX - viewSize.cx - curPos.x;
1477 if (newPos.x + viewSize.cx > docSize.cx)
1478 newPos.x = docSize.cx - viewSize.cx;
1481 if (curPos.y - roomRect.bottom < gridY)
1483 newPos.y += roomRect.bottom + gridY - curPos.y;
1488 else if (curPos.y - roomRect.top + gridY > viewSize.cy)
1490 newPos.y += roomRect.top + viewSize.cy - curPos.y - gridY;
1492 if (viewSize.cy - newPos.y > docSize.cy)
1493 newPos.y = viewSize.cy - docSize.cy;
1496 if (newPos != curPos)
1498 scrollToPosition(newPos);
1499 // Must adjust the room position because the DC won't be updated:
1500 roomRect.OffsetRect(curPos.x - newPos.x, curPos.y - newPos.y);
1503 // Make sure the floating comments dialog isn't in the way:
1504 cdc.LPtoDP(&roomRect);
1505 ClientToScreen(&roomRect);
1506 roomRect.NormalizeRect();
1507 static_cast<CMapApp *>(AfxGetApp())->adjustCommentPos(roomRect);
1512 // Paste a string to the clipboard:
1515 // text: The string to paste
1518 // True: String was successfully pasted
1519 // False: Could not paste (clipboard unavailable or out of memory)
1521 bool MapView::pasteClipboard(const string & text)
1524 if (!OpenClipboard())
1529 // Allocate a global memory object for the text
1530 HGLOBAL mem = GlobalAlloc(GMEM_DDESHARE, text.length() + sizeof(TCHAR));
1538 // Lock the handle and copy the text to the buffer
1540 strcpy(LPTSTR(GlobalLock(mem)), text.c_str());
1543 SetClipboardData(CFtrEXT, mem); // Place the handle on the clipboard
1551 // Update the selected room's comment from the floating dialog:
1554 // comment: The new room comment (must not be NULL)
1556 void MapView::setRoomNote(const char * comment)
1558 ASSERT(selectedOne >= 0);
1560 if (doc->room[selectedOne].note != comment)
1562 doc->setUndoData(new UndoRoomInfo(*doc, selectedOne));
1563 doc->setRoomNote(selectedOne, comment);
1567 int MapView::FindHoveredEdge(void)
1569 for(int i=0; i<doc->edge.size(); i++)
1573 doc->getEdgePoints(doc->edge[i], ep1, ep2);
1575 QPointF lineSegment = ep2 - ep1;
1576 QPointF v1 = mouse - ep1;
1577 QPointF v2 = mouse - ep2;
1578 double t = ParameterOfLineAndPoint(ep1, ep2, mouse);
1582 distance = Magnitude(v1);
1584 distance = Magnitude(v2);
1586 // distance = ?Det?(ls, v1) / |ls|
1587 distance = fabs(Determinant(lineSegment, v1)
1588 / Magnitude(lineSegment));
1590 if (distance < 20.0)
1594 // 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);
1625 // Zoom the display:
1628 // newZoom: The new zoom factor (in percent)
1630 void MapView::zoomTo(short newZoom)
1632 if (opInProgress == gmoAddCorner)
1633 opInProgress = gmoNone;
1635 if (newZoom == zoom)
1638 if (opInProgress || scrollDrag || (newZoom < minZoom) || (newZoom > maxZoom))
1639 ;//MessageBeep(MB_OK);
1642 // setDisplayZoom(zoom = newZoom);
1644 if (selectedOne >= 0)
1645 makeRoomVisible(selectedOne);
1648 // GetParentFrame()->OnUpdateFrameTitle(TRUE);
1654 //---------------------------------------------------------------------------
1655 // Keyboard navigation:
1658 // corner: The direction to move
1659 // toggleStubs: TRUE means adding an existing stub should delete it
1662 // edgeTmp: If type1 or type2 is nonzero, use that for a new edge
1663 // selectedOne: Must be a valid room number
1665 void MapView::navigate(RoomCorner corner, bool toggleStubs/* = true*/)
1667 const short roomXoffset[] =
1669 0, 0, gridX + roomWidth, -(gridX + roomWidth),
1670 gridX + roomWidth, -(gridX + roomWidth), gridX + roomWidth, -(gridX + roomWidth)
1671 }; // FIXME add NNE, etc.
1673 const short roomYoffset[] =
1675 gridY + roomHeight, -(gridY + roomHeight), 0, 0,
1676 gridY + roomHeight, gridY + roomHeight, -(gridY + roomHeight), -(gridY + roomHeight)
1677 }; // FIXME add NNE, etc.
1681 ASSERT(selectedOne >= 0);
1683 if (doc->room[selectedOne].isCorner())
1685 // MessageBeep(MB_ICONASTERISK);
1689 // Look for an existing connection:
1690 int eNum = doc->findEdge(selectedOne, corner, e);
1692 if ((eNum >= 0) && !(e.type2 & etUnexplored))
1694 // Found existing connection, and 2nd end is not explored
1695 if (toggleStubs && (e.type2 & etNoExit))
1697 if (edgeTmp.type1 & etNoExit)
1699 doc->setUndoData(new UndoDelete(doc->isDirty, doc->edge[eNum]));
1703 doc->setUndoData(new UndoChangeEdge(doc->isDirty, doc->edge[eNum]));
1704 edgeTmp.room1 = selectedOne;
1705 edgeTmp.end1 = corner;
1706 doc->addEdge(edgeTmp);
1709 doc->deleteEdge(eNum);
1713 if (edgeTmp.type1 || edgeTmp.type2 || (e.type1 & etOneWay)
1714 || (e.type2 & etNoRoom2))
1716 // We were trying to add connection
1717 // MessageBeep(MB_ICONASTERISK);
1721 if (e.end1 == rcCorner)
1723 int r = doc->findOtherEnd(eNum);
1729 // MessageBeep(MB_ICONASTERISK);
1732 } // end if this connection leads to a corner
1734 selectOnlyRoom(e.room1);
1735 makeRoomVisible(e.room1);
1736 eNum = -1; // Don't delete this edge
1738 else // eNum < 0 || e.type2 & etUnexplored
1740 // Try to add a new connection
1741 if (doc->locked || (corner >= rcNNE))
1743 // MessageBeep(MB_ICONASTERISK);
1747 const bool wasModified = doc->isDirty;
1750 // If adding stub where there's already a stub
1751 if ((eNum >= 0) && (e.type2 & etUnexplored)
1752 && (edgeTmp.type1 & etUnexplored))
1756 // Remove stub connection:
1757 doc->setUndoData(new UndoDelete(wasModified, doc->edge[eNum]));
1758 doc->deleteEdge(eNum);
1761 // MessageBeep(MB_ICONASTERISK);
1766 e.room1 = selectedOne;
1769 if (edgeTmp.type1 || edgeTmp.type2)
1771 e.type1 = edgeTmp.type1;
1772 e.type2 = edgeTmp.type2;
1777 EdgeVec deletedEdges;
1779 // If there's a room #2 connected to this corner of the room...
1780 if (!(e.type1 & etNoRoom2))
1782 QPoint pos(doc->room[selectedOne].pos);
1783 pos.rx() += roomXoffset[corner];
1784 pos.ry() += roomYoffset[corner];
1786 int room = doc->findRoom(pos);
1790 // We're off the grid
1791 // MessageBeep(MB_ICONASTERISK);
1796 // We need to add a new room
1797 room = doc->addRoom(pos);
1802 // Existing room, check for existing connection
1804 int oeNum = doc->findEdge(room, oppositeCorner[corner], oEdge);
1807 { // There is an existing connection
1808 if (oEdge.type2 & etUnexplored)
1810 deletedEdges.push_back(doc->edge[oeNum]);
1811 doc->deleteEdge(oeNum);
1814 eNum--; // The edge number might have changed
1817 room = -1; // Room has a non-stub connection there
1823 // MessageBeep(MB_ICONASTERISK);
1827 selectOnlyRoom(room);
1828 makeRoomVisible(room);
1831 e.end2 = oppositeCorner[corner];
1836 deletedEdges.push_back(doc->edge[eNum]);
1837 doc->deleteEdge(eNum);
1846 if (gueApp()->editAfterAdd()) //editAfterAdd() returns autoEdit
1847 editProperties(epuAddRoomEdge, wasModified, (deletedEdges.size() ? &deletedEdges : NULL));
1848 else if (deletedEdges.size())
1849 doc->setUndoData(new UndoChanges(wasModified, selectedOne, 1, deletedEdges));
1851 doc->setUndoData(new UndoAdd(wasModified, selectedOne, 1));
1853 else if (deletedEdges.size())
1855 if (deletedEdges.size() == 1)
1856 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
1858 doc->setUndoData(new UndoChanges(wasModified, -1, 1, deletedEdges));
1861 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
1867 // Fill in the edge type from the navigation box:
1869 // If the text in the navigation bar is up, down, in, or out, then set
1870 // the edge type appropriately. Otherwise, set the edge type to normal.
1871 // Always clears the navigation bar text.
1873 // Can only create up-down and in-out passages.
1876 // e.type1 and e.type2
1878 void MapView::setEdgeType(MapEdge & e)
1880 e.type1 = e.type2 = etNormal;
1882 RoomCorner corner1, corner2;
1883 char initial, separator;
1884 getNavText(initial, corner1, separator, corner2);
1886 if (initial && initial != '%' && initial != '>')
1888 // MessageBeep(MB_ICONASTERISK);
1896 case rcUp: e.type1 = etUp; break;
1897 case rcDown: e.type1 = etDown; break;
1898 case rcIn: e.type1 = etIn; break;
1899 case rcOut: e.type1 = etOut; break;
1902 if (separator == '-')
1906 case rcUp: e.type2 = etUp; break;
1907 case rcDown: e.type2 = etDown; break;
1908 case rcIn: e.type2 = etIn; break;
1909 case rcOut: e.type2 = etOut; break;
1913 else // standard edge
1917 case rcUp: e.type1 = etUp; e.type2 = etDown; break;
1918 case rcDown: e.type1 = etDown; e.type2 = etUp; break;
1919 case rcIn: e.type1 = etIn; e.type2 = etOut; break;
1920 case rcOut: e.type1 = etOut; e.type2 = etIn; break;
1925 e.type1 |= etObstructed;
1927 if (initial == '>' || separator == '>')
1928 e.type1 |= etOneWay;
1930 if (initial == '?' || separator == '?')
1932 e.type1 &= ~etSpecial;
1933 e.type1 |= etUnexplored;
1937 else if (initial == '!' || separator == '!')
1939 e.type1 &= ~etSpecial;
1940 e.type1 |= etNoExit;
1947 // Start typing in the navigation bar:
1949 // Stuffs the given char in the navigation bar and sets focus to there.
1952 // c: The character to put in the navigation bar.
1954 void MapView::setNavText(char c)
1957 CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
1960 nb.SetDlgItemText(IDC_NAV_DIR, text);
1962 CEdit * dir = static_cast<CEdit *>(nb.GetDlgItem(IDC_NAV_DIR));
1969 void MapView::OnChar(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
1971 if (((c >= 'A') && (c <= 'Z'))
1972 || ((c >= 'a') && (c <= 'z'))
1973 || (c == '-') || (c == '!') || (c == '?') || (c == '/')
1974 || (c == '>') || (c == '.') || (c == '~') || (c == '`')
1981 // Parse the text in the navigation bar:
1984 // mustBeMove: (default FALSE)
1985 // If TRUE, beep and don't clear text if not a direction.
1986 // If FALSE, clear text and don't beep even if it's meaningless.
1990 // The non-alphabetic character beginning the text (0 if none)
1992 // The direction indicated by the first direction word
1993 // rcNone if no direction words
1995 // The non-alphabetic character following the first direction word
1997 // Must be either '-', '>', or '?'
1999 // The direction indicated by the second direction word
2000 // rcNone if no second direction
2002 void MapView::getNavText(char & initial, RoomCorner & dir1, char & separator, RoomCorner & dir2, bool mustBeMove)
2005 CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
2008 nb.GetDlgItemText(IDC_NAV_DIR, text);
2011 dir1 = dir2 = rcNone;
2012 initial = separator = '\0';
2014 if (!text.IsEmpty() && (text[0] < 'a' || text[0] > 'z'))
2025 else if (!text.IsEmpty() && (text[0] == '-' || text[0] == '>'))
2027 separator = text[0];
2032 dir1 = parseDirection(text);
2034 if (!text.IsEmpty() && (text[0]=='-' || text[0]=='>' || text[0]=='?'))
2036 separator = text[0];
2042 dir2 = parseDirection(text);
2044 if (mustBeMove && (dir1 == rcNone || separator == '-' || ((initial || separator) && (dir1 >= rcNumCorners))))
2045 MessageBeep(MB_ICONASTERISK);
2048 nb.SetDlgItemText(IDC_NAV_DIR, tr(""));
2056 // Handle the Go button in the navigation bar:
2058 void MapView::OnNavGo()
2060 if (selectedOne < 0)
2062 MessageBeep(MB_ICONASTERISK);
2066 RoomCorner corner, junk2;
2067 char initial, separator;
2068 getNavText(initial, corner, separator, junk2, true);
2070 if (separator == '-' || junk2 != rcNone)
2072 MessageBeep(MB_ICONASTERISK);
2076 if (corner != rcNone)
2078 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2081 edgeTmp.type1 = etObstructed;
2083 if (initial == '>' || separator == '>')
2084 edgeTmp.type1 |= etOneWay;
2086 if (initial == '?' || separator == '?')
2087 edgeTmp.type1 = etUnexplored;
2088 else if (initial == '!' || separator == '!')
2089 edgeTmp.type1 = etNoExit;
2096 // Handle the direction keys on the numeric keypad:
2099 // cmd: Which command was executed
2101 void MapView::OnNavGoDir(unsigned int cmd)
2103 ASSERT(cmd >= ID_NAV_GO_NORTH && cmd <= ID_NAV_STUB2_OUT);
2105 if (selectedOne >= 0)
2108 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2109 bool toggleStubs = false;
2111 if (cmd >= ID_NAV_STUB1_NORTH)
2113 if (cmd >= ID_NAV_STUB2_NORTH)
2116 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etNoExit : etUnexplored);
2117 cmd -= ID_NAV_STUB2_NORTH - ID_NAV_GO_NORTH;
2122 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etUnexplored : etNoExit);
2123 cmd -= ID_NAV_STUB1_NORTH - ID_NAV_GO_NORTH;
2129 navigate(RoomCorner(cmd - ID_NAV_GO_NORTH), toggleStubs);
2132 MessageBeep(MB_ICONASTERISK);
2136 // CMapView message handlers
2137 //---------------------------------------------------------------------------
2138 // Update the comments dialog when the view is activated:
2140 void MapView::OnActivateView(bool bActivate, CView * pActivateView, CView * pDeactiveView)
2143 selectDone(); // Update comments dialog
2145 CScrollZoomView::OnActivateView(bActivate, pActivateView, pDeactiveView);
2149 // 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...]
2150 //void MapView::OnEditAddCorner()
2151 void MapView::HandleAddCorner(void)
2153 //printf("MapView::HandleAddCorner... (iTmp=%i, opInProgress=%i)\n", iTmp, opInProgress);
2154 ASSERT(opInProgress == gmoAddCorner);
2155 ASSERT(selectedOne >= 0);
2157 int room = doc->addCorner(selectedOne, iTmp);
2160 selectOnlyRoom(room);
2162 // MessageBeep(MB_ICONASTERISK);
2164 opInProgress = gmoNone;
2168 void MapView::HandleAddUnexplored(void)
2170 EdgeVec deletedEdges;
2171 const bool wasModified = doc->isDirty;
2174 edgeTmp.room1 = roomClicked;
2175 edgeTmp.end1 = cornerClicked;
2176 // This is from MapView::setEdgeType()
2177 edgeTmp.type1 &= ~etSpecial;
2178 edgeTmp.type1 |= etUnexplored;
2180 edgeTmp.end2 = rcSW;
2182 // Look for an existing connection:
2183 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2184 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2188 deletedEdges.push_back(doc->edge[eNum]);
2189 doc->deleteEdge(eNum);
2192 doc->addEdge(edgeTmp);
2194 if (deletedEdges.size())
2195 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2197 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2199 opInProgress = gmoNone;
2203 void MapView::HandleAddLoopBack(void)
2205 EdgeVec deletedEdges;
2206 const bool wasModified = doc->isDirty;
2209 edgeTmp.room1 = roomClicked;
2210 edgeTmp.end1 = cornerClicked;
2211 // This is from MapView::setEdgeType()
2212 edgeTmp.type1 &= ~etSpecial;
2213 edgeTmp.type1 |= etLoopBack;
2215 edgeTmp.end2 = rcSW;
2217 // Look for an existing connection:
2218 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2219 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2223 deletedEdges.push_back(doc->edge[eNum]);
2224 doc->deleteEdge(eNum);
2227 doc->addEdge(edgeTmp);
2229 if (deletedEdges.size())
2230 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2232 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2234 opInProgress = gmoNone;
2238 void MapView::HandleAddNoExit(void)
2240 EdgeVec deletedEdges;
2241 const bool wasModified = doc->isDirty;
2244 edgeTmp.room1 = roomClicked;
2245 edgeTmp.end1 = cornerClicked;
2246 // This is from MapView::setEdgeType()
2247 edgeTmp.type1 &= ~etSpecial;
2248 edgeTmp.type1 |= etNoExit;
2250 edgeTmp.end2 = rcSW;
2252 // Look for an existing connection:
2253 // int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2254 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2258 deletedEdges.push_back(doc->edge[eNum]);
2259 doc->deleteEdge(eNum);
2262 doc->addEdge(edgeTmp);
2264 if (deletedEdges.size())
2265 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2267 doc->setUndoData(new UndoAdd(wasModified)); // Undo new edge
2269 opInProgress = gmoNone;
2273 void MapView::SetEdgeDirection(EdgeType et)
2277 // Look for an existing connection:
2278 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2282 // Is the room we clicked on room1 of this edge?
2283 if (doc->edge[eNum].room1 == roomClicked)
2285 doc->edge[eNum].type1 &= ~etDirection;
2286 doc->edge[eNum].type1 |= et;
2290 doc->edge[eNum].type2 &= ~etDirection;
2291 doc->edge[eNum].type2 |= et;
2295 opInProgress = gmoNone;
2299 void MapView::HandleAddUp(void)
2301 SetEdgeDirection(etUp);
2304 void MapView::HandleAddDown(void)
2306 SetEdgeDirection(etDown);
2309 void MapView::HandleAddIn(void)
2311 SetEdgeDirection(etIn);
2314 void MapView::HandleAddOut(void)
2316 SetEdgeDirection(etOut);
2319 void MapView::SetEdges(int room, int edgeNum, EdgeType eType)
2322 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.
2325 1) Figure out which side of the Edge has the room we're starting with.
2326 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.
2327 3) Once the Edge is found, check to see if it's an rfCorner.
2331 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...
2332 Which means you could do away with type2, only it uses that to distinguish the Up/Down/In/Out annotations put on map edges...
2335 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.
2337 // Go thru from current room to all connected "corner" rooms
2340 // Check for the unlikely case that we got passed bad info
2341 if (doc->edge[edgeNum].HasRoom(room) == false)
2344 if (doc->edge[edgeNum].room2 == room)
2345 doc->edge[edgeNum].Swap();
2347 if (((eType == etOneWay) && (doc->edge[edgeNum].end2 != rcCorner))
2348 || (eType != etOneWay))
2349 doc->edge[edgeNum].type1 |= eType;
2351 if (doc->edge[edgeNum].end2 != rcCorner)
2354 // Find next Edge... :-/
2355 room = doc->edge[edgeNum].room2;
2357 for(int i=0; i<doc->edge.size(); i++)
2359 // Exclude the edge we just came from...
2363 if (doc->edge[i].HasRoom(room))
2372 void MapView::ClearEdges(int room, int edgeNum, EdgeType eType)
2374 // Go thru from current room to all connected "corner" rooms
2377 // Check for the unlikely case that we got passed bad info
2378 if (doc->edge[edgeNum].HasRoom(room) == false)
2381 if (doc->edge[edgeNum].room2 == room)
2382 doc->edge[edgeNum].Swap();
2384 doc->edge[edgeNum].type1 &= ~eType;
2386 if (doc->edge[edgeNum].end2 != rcCorner)
2389 // Find next Edge... :-/
2390 room = doc->edge[edgeNum].room2;
2392 for(int i=0; i<doc->edge.size(); i++)
2394 // Exclude the edge we just came from...
2398 if (doc->edge[i].HasRoom(room))
2407 void MapView::HandleAddOneWay(void)
2411 // Look for an existing connection:
2412 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2415 SetEdges(roomClicked, eNum, etOneWay);
2417 opInProgress = gmoNone;
2421 void MapView::HandleClearOneWay(void)
2425 // Look for an existing connection:
2426 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2429 ClearEdges(roomClicked, eNum, etOneWay);
2431 opInProgress = gmoNone;
2435 void MapView::HandleAddRestricted(void)
2439 // Look for an existing connection:
2440 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2443 SetEdges(roomClicked, eNum, etObstructed);
2445 opInProgress = gmoNone;
2449 void MapView::HandleClearRestricted(void)
2453 // Look for an existing connection:
2454 int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2457 ClearEdges(roomClicked, eNum, etObstructed);
2459 opInProgress = gmoNone;
2463 //void MapView::OnEditAddRoom()
2464 void MapView::HandleAddRoom(void)
2467 MapDoc * const doc = GetDocument();
2470 const int p = doc->page.size();
2475 page.pos = scrollDragStart;
2476 page.pos.x -= page.pos.x % gridX;
2477 page.pos.y -= page.pos.y % gridY;
2478 doc->setUndoData(new UndoAdd(doc->isDirty, p));
2480 doc->addPage(p, page);
2485 QPoint roomPt(scrollDragStart - offset);
2490 void MapView::HandleMapProperties(void)
2494 dlg.title.setText(doc->name.c_str());
2495 dlg.comment.setPlainText(doc->note.c_str());
2496 dlg.navMode.setChecked(doc->locked);
2497 dlg.displayGrid.setChecked(showGrid);
2498 dlg.showCorners.setChecked(showCorners);
2499 dlg.showPages.setChecked(showPages);
2501 if (dlg.exec() == false)
2504 doc->setName(dlg.title.text().toUtf8().data());
2505 doc->setNote(dlg.comment.document()->toPlainText().toUtf8().data());
2506 doc->locked = dlg.navMode.isChecked();
2507 showGrid = dlg.displayGrid.isChecked();
2508 showCorners = dlg.showCorners.isChecked();
2509 showPages = dlg.showPages.isChecked();
2511 if (dlg.title.text().isEmpty() == false)
2512 setWindowTitle(dlg.title.text());
2517 //void MapView::OnEditClear()
2518 void MapView::HandleDelete(void)
2525 //--------------------------------------------------------------------
2526 void MapView::OnEditAddPage()
2528 MapDoc * const doc = GetDocument();
2531 const int p = doc->page.size();
2536 page.pos = scrollDragStart;
2537 page.pos.x -= page.pos.x % gridX;
2538 page.pos.y -= page.pos.y % gridY;
2539 doc->setUndoData(new UndoAdd(doc->isDirty, p));
2541 doc->addPage(p, page);
2550 // removeCorner: (default true)
2551 // When TRUE, if a single corner is selected, remove it but leave
2552 // the connection. When FALSE, the connection is always removed.
2554 void MapView::deleteSelection(bool removeCorner/* = true*/)
2556 if (numSelected || numSelectedPages)
2558 if (doc->locked || (numSelectedPages == doc->page.size()))
2560 // MessageBeep(MB_ICONASTERISK);
2564 if (removeCorner && (selectedOne >= 0)
2565 && doc->room[selectedOne].isCorner())
2567 // Replace this corner with a single edge:
2568 doc->deleteCorner(selectedOne);
2569 clearSelection(false);
2575 // Select all corners if one corner or one end is selected:
2576 const EdgeVec & edge = doc->edge;
2577 int wereSelected = 0;
2579 while (wereSelected != numSelected)
2581 wereSelected = numSelected;
2583 for(EdgeConstItr e=edge.begin(); e!=edge.end(); ++e)
2585 if ((e->end1 == rcCorner) && selected[e->room2] && !selected[e->room1])
2586 selectRoom(e->room1, false);
2587 else if ((e->end2==rcCorner) && selected[e->room1] && !selected[e->room2])
2588 selectRoom(e->room2, false);
2593 UndoDelete * undoRec = new UndoDelete(*doc, numSelected, selected, numSelectedPages, selectedPage);
2594 opInProgress = gmoDeleting; // Don't clear selection after first room
2595 int pos = numSelected;
2597 for(int i=doc->room.size()-1; i>=0; i--)
2600 undoRec->addRoom(--pos, i, doc->extractRoom(i));
2603 for(int i=doc->page.size()-1; i>=0; i--)
2605 if (selectedPage[i])
2609 selectedPage.resize(doc->page.size(), 0);
2610 opInProgress = gmoNone;
2611 doc->setUndoData(undoRec);
2613 clearSelection(false);
2619 //--------------------------------------------------------------------
2620 // Cut, Copy & Paste:
2622 void MapView::OnEditCopy()
2624 if (numSelected || numSelectedPages)
2626 MapDoc * doc = GetDocument();
2629 // Deselect all corner rooms:
2630 for(int i=doc->room.size()-1; i>=0; --i)
2632 if (selected[i] && doc->room[i].isCorner())
2636 if (!numSelected && !numSelectedPages)
2639 MessageBeep(MB_ICONASTERISK);
2641 } // end if only corners were selected
2643 // Select corners between selected rooms:
2644 const EdgeVec& edges = doc->edge;
2646 for (EdgeConstItr e = edges.begin(); e != edges.end(); ++e)
2648 if ((selected[e->room1] && (e->end2 == rcCorner)
2649 && (e->end1 != rcCorner) && !selected[e->room2])
2650 || (selected[e->room2] && (e->end1 == rcCorner)
2651 && (e->end2 != rcCorner) && !selected[e->room1]))
2653 RoomNumVec cornerRooms;
2654 cornerRooms.reserve(16); // Should be plenty
2655 int otherEnd = doc->findOtherEnd(e, &cornerRooms);
2657 if ((otherEnd >= 0) && selected[otherEnd])
2659 for(RNConstItr rn=cornerRooms.begin(); rn!=cornerRooms.end(); ++rn)
2662 } // end if edge connects selected room to unselected corner
2667 // Copy selected rooms and pages:
2668 gueApp()->setClipboard(new RoomScrap(*doc, numSelected, selected, numSelectedPages, selectedPage));
2672 void MapView::OnEditCut()
2678 void MapView::OnEditPaste()
2680 const RoomScrap * scrap = static_cast<CMapApp *>(AfxGetApp())->getClipboard();
2684 MapDoc * doc = GetDocument();
2687 int i = doc->room.size();
2689 doc->setUndoData(scrap->paste(*doc));
2690 const int numRooms = doc->room.size();
2695 clearSelection(true);
2697 while (i < numRooms)
2698 selectRoom(i++, true);
2704 void MapView::OnEditPaginate()
2707 GetDocument()->layoutPages();
2712 // Handle Edit Properties:
2714 void MapView::HandleRoomProperties(void)
2716 // editProperties((cmd == ID_EDIT_MAP_PROPERTIES) ? epuMapInfo : epuRoomInfo);
2717 editProperties(epuRoomInfo);
2721 // Bring up the Properties dialog:
2725 // The type of action we should create an undo record for
2726 // epuRoomInfo: Just changing room info
2727 // epuAddRoom: Adding a new room
2728 // epuAddRoomEdge: Adding a new room and a new edge
2730 // The modification state of the document
2731 // (Necessary only if undoType is not epuRoomInfo)
2732 // edges: (default NULL)
2733 // A pointer to the edges that were deleted before adding this room
2734 // NULL if no edges were deleted
2735 // Must be NULL unless undoType is epuAddRoomEdge
2736 // The vector is emptied but not deleted
2738 void MapView::editProperties(EditPropUndo undoType, bool wasModified/*= false*/, EdgeVec * edges/*= NULL*/)
2740 bool forceMap = false;
2742 if (undoType == epuMapInfo)
2744 undoType = epuRoomInfo;
2748 // CPropertySheet dlg(tr("Properties"));
2749 RoomDialog dlg(this);
2750 // CRoomPropPage room;
2751 UndoRoomInfo * undoRoom = NULL;
2752 const bool roomTab = (selectedOne >= 0) && !doc->room[selectedOne].isCorner();
2754 // CMapPropPage map;
2755 // Strcpy(map.name, doc->getName());
2756 // Strcpy(map.note, doc->getNote());
2757 // map.corners = showCorners;
2758 // map.locked = doc->locked;
2759 // map.grid = showGrid;
2760 // map.pages = showPages;
2761 // dlg.AddPage(&map);
2765 // VERIFY(undoRoom = new UndoRoomInfo(*doc, selectedOne));
2766 undoRoom = new UndoRoomInfo(*doc, selectedOne);
2767 // room.border = bool((undoRoom->getRoom().flags & rfBorder) != 0);
2768 // Strcpy(room.name, undoRoom->getRoom().name);
2769 // Strcpy(room.note, undoRoom->getRoom().note);
2770 // dlg.AddPage(&room);
2771 // static_cast<CMapApp *>(AfxGetApp())->setComment(this, NULL, false);
2772 dlg.name.setText(undoRoom->room.name.c_str());
2773 dlg.comment.insertPlainText(undoRoom->room.note.c_str());
2774 dlg.border.setChecked(bool((undoRoom->room.flags & rfBorder) != 0));
2777 // dlg.SetActivePage(&room);
2780 // if (dlg.DoModal() == IDOK)
2785 bool changed = false;
2787 // if (room.border != (0 != (undoRoom->getRoom().flags & rfBorder)))
2788 if (dlg.border.isChecked() != (0 != (undoRoom->room.flags & rfBorder)))
2790 // doc->setRoomFlags(selectedOne, (room.border ? rfBorder : 0), rfBorder);
2791 doc->setRoomFlags(selectedOne, (dlg.border.isChecked() ? rfBorder : 0), rfBorder);
2795 // if (room.name != undoRoom->getRoom().name.c_str())
2796 if (dlg.name.text().toUtf8().data() != undoRoom->room.name.c_str())
2798 // if (room.name.Find('\n') < 0)
2799 // wrapRoomName(room.name);
2801 doc->setRoomName(selectedOne, dlg.name.text().toUtf8().data());
2805 // room.note.TrimRight();
2807 if (dlg.comment.document()->toPlainText().toUtf8().data() != undoRoom->room.note.c_str())
2809 // doc->setRoomNote(selectedOne, room.note);
2810 doc->setRoomNote(selectedOne, dlg.comment.document()->toPlainText().toUtf8().data());
2814 if (changed && (undoType == epuRoomInfo))
2816 doc->setUndoData(undoRoom);
2817 undoRoom = NULL; // undoRoom now belongs to the document
2821 if (undoType != epuRoomInfo)
2823 ASSERT(selectedOne >= 0);
2826 doc->setUndoData(new UndoChanges(wasModified, selectedOne, 1, *edges));
2828 doc->setUndoData(new UndoAdd(wasModified, selectedOne, (undoType == epuAddRoomEdge ? 1 : 0)));
2831 // doc->setName(map.name);
2832 // doc->setNote(map.note);
2834 // if (doc->locked != bool(map.locked))
2835 // doc->OnNavigationMode();
2837 /* if ((showCorners != bool(map.corners))
2838 || (showGrid != bool(map.grid))
2839 || (showPages != bool(map.pages)))
2841 showCorners = map.corners != 0;
2842 showGrid = map.grid != 0;
2843 showPages = map.pages != 0;
2846 deselectPages(false);
2848 // InvalidateRect(NULL);
2851 else if (undoType != epuRoomInfo)
2853 // Cancel adding the room:
2854 ASSERT(selectedOne >= 0);
2855 const RoomNum roomNum = selectedOne;
2858 if (undoType == epuAddRoomEdge)
2860 const MapEdge & edge = doc->edge[doc->edge.size() - 1];
2861 selectOnlyRoom(edge.room1 == roomNum ? edge.room2 : edge.room1);
2864 opInProgress = gmoDeleting; // Don't clear selection
2865 doc->deleteRoom(roomNum);
2866 opInProgress = gmoNone;
2870 doc->addEdges(edges->size());
2872 for(EdgeConstItr e=edges->begin(); e!=edges->end(); e++)
2876 doc->isDirty = wasModified;
2886 // Select all rooms:
2888 void MapView::OnEditSelectAll()
2890 for(int i=doc->room.size()-1; i>=0; i--)
2896 //---------------------------------------------------------------------------
2897 // Select connected rooms:
2899 void MapView::OnEditSelectConnected()
2901 const MapDoc * doc = GetDocument();
2904 const int numEdges = doc->getEdgeCount();
2905 const EdgeVec & edge = doc->edge;
2907 int wasSelected = 0;
2909 while (wasSelected != numSelected)
2911 wasSelected = numSelected;
2913 for(int i=0; i<numEdges; i++)
2915 if ((edge[i].type1 & etNoRoom2) == 0)
2917 if (selected[edge[i].room1])
2918 selectRoom(edge[i].room2);
2919 else if (selected[edge[i].room2])
2920 selectRoom(edge[i].room1);
2921 } // end if edge connects two rooms
2923 } // end while more rooms were selected on this pass
2928 void MapView::OnInitialUpdate()
2930 const MapDoc * doc = GetDocument();
2933 selectedPage.resize(doc->page.size(), 0);
2934 clearSelection(false);
2936 // Set the document size:
2937 init(4 * gridX, doc->getDocSize(), QSize(gridX, gridY));
2938 // CClientDC dc(this);
2939 // OnPrepareDC(&dc);
2941 memset(&lf, 0, sizeof(lf));
2943 strcpy(lf.lfFaceName, "Arial");
2944 font.CreateFontIndirect(&lf);
2947 OnUpdate(NULL, dupNavigationMode, NULL);
2949 CScrollZoomView::OnInitialUpdate();
2950 setScrollBars(); // Now fix the scroll bars
2953 void MapView::OnKeyDown(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
2955 bool ctrl = (GetKeyState(VK_CONTROL) < 0);
2959 case VK_LEFT: OnHScroll(ctrl ? SB_PAGELEFT : SB_LINELEFT, 0, NULL); break;
2960 case VK_RIGHT: OnHScroll(ctrl ? SB_PAGERIGHT : SB_LINERIGHT, 0, NULL); break;
2961 case VK_UP: OnVScroll(ctrl ? SB_PAGEUP : SB_LINEUP, 0, NULL); break;
2962 case VK_DOWN: OnVScroll(ctrl ? SB_PAGEDOWN : SB_LINEDOWN, 0, NULL); break;
2963 case VK_PRIOR: OnVScroll(ctrl ? SBtrOP : SB_PAGEUP, 0, NULL); break;
2964 case VK_NEXT: OnVScroll(ctrl ? SB_BOTTOM : SB_PAGEDOWN, 0, NULL); break;
2965 case VKtrAB: gueApp()->editComment(); break;
2967 if ((GetKeyState(VK_SHIFT) >= 0) && (c >= '0') && (c <= '9'))
2969 if (!ctrl && (c < '2'))
2971 else if (ctrl && (c == '0'))
2974 zoomTo(10 * (c - '0') + (ctrl ? 100 : 0));
2975 } // end if number key (not shifted)
2982 void MapView::mouseDoubleClickEvent(QMouseEvent * event)
2983 //void MapView::OnLButtonDblClk(unsigned int nFlags, QPoint point)
2985 if (scrollDrag || scrollDragTimer)
2988 // QPoint point = event->pos() * 5;
2989 QPointF ptf = event->localPos() * (100.0 / zoom);
2990 QPoint point = ptf.toPoint();
2991 // QPoint point = event->pos() * (100.0 / zoom);
2992 int nFlags = event->modifiers();
2997 opInProgress = gmoNone;
2999 // if (GetCapture() == this)
3000 // ReleaseCapture();
3002 // CClientDC dc(this);
3003 // OnPrepareDC(&dc);
3004 // dc.DPtoLP(&point);
3006 const int i = doc->roomHit(point);
3007 //printf("MapView::mouseDoubleClickEvent: roomHit=%i\n", i);
3009 if ((i >= 0) && (doc->room[i].isCorner() == false))
3012 editProperties(epuRoomInfo);
3018 /* N.B.: Handles RButton & MButton too */
3019 void MapView::mousePressEvent(QMouseEvent * event)
3020 //void MapView::OnLButtonDown(unsigned int nFlags, QPoint point)
3022 nFlags = MK_CONTROL, MK_SHIFT, MK_{LMR}BUTTON
3025 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3026 // QPoint point = event->pos() * 5;
3027 QPointF ptf = event->localPos() * (100.0 / zoom);
3028 QPoint point = ptf.toPoint();
3029 // QPoint point = event->pos() * (100 / zoom);
3030 int nFlags = event->modifiers();
3031 mouseDown = true;//will prolly have to separate this out to L, M & R :-P
3033 Qt::NoModifier, Qt::ShiftModifier, Qt::ControlModifier, Qt::AltModifier, etc.
3036 // Scrolling support (save this for now)...
3038 offsetScrollDragStart = offset;
3039 scrollDragStart = point + offset;
3041 if (event->button() == Qt::LeftButton)
3043 if (scrollDrag || scrollDragTimer)
3046 // CClientDC dc(this);
3047 // OnPrepareDC(&dc);
3048 // dc.DPtoLP(&point);
3051 int room = doc->roomHit(point, &corner);
3056 return; // Must have 1 room selected during navigation
3058 if (showPages && ((room = doc->pageBorderHit(point)) >= 0))
3062 // if (nFlags & MK_CONTROL)
3063 if (nFlags & Qt::ControlModifier)
3065 opInProgress = gmoControlDown;
3066 bTmp = selectedPage[room];
3067 b2Tmp = true; // In page
3071 // if (nFlags & MK_SHIFT)
3072 if (nFlags & Qt::ShiftModifier)
3073 opInProgress = gmoShiftDown;
3075 opInProgress = gmoNone;
3086 p1Tmp.rx() = point.x() / gridX;
3087 p1Tmp.ry() = point.y() / gridY;
3088 p2Tmp = doc->page[room].pos;
3094 opInProgress = gmoSelectBox;
3096 // if (nFlags & MK_CONTROL)
3097 if (nFlags & Qt::ControlModifier)
3099 // If Ctrl pressed, don't clear selection:
3100 for(ByteItr b=selected.begin(); b!=selected.end(); b++)
3103 *b = 2; // Mark the rooms that should stay selected
3106 for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
3109 *b = 2; // Mark the pages that should stay selected
3120 // rTmp.left = rTmp.right = point.x;
3121 // rTmp.bottom = rTmp.top = point.y;
3122 rTmp.setCoords(point.x(), point.y(), point.x(), point.y());
3127 else if (doc->locked)
3130 ASSERT(selectedOne >= 0);
3132 if ((room != selectedOne) && !doc->room[room].isCorner())
3135 doc->shortestPath(path, selectedOne, room);
3138 path = tr("Sorry, I don't know how to get there.");
3139 else if (path.find('.') != String::npos)
3141 const CMapApp * app = static_cast<CMapApp *>(AfxGetApp());
3143 if (app->navigationCopy())
3145 if (app->navigationCRLF())
3148 pasteClipboard(path);
3151 // end else if path has multiple steps
3153 static_cast<CMainFrame *>(AfxGetMainWnd())->setStatusBar(path.c_str());
3154 selectOnlyRoom(room);
3156 // end if moving to new room in navigation mode
3159 // else if (nFlags & MK_CONTROL)
3160 else if (nFlags & Qt::ControlModifier)
3163 opInProgress = gmoControlDown;
3164 ASSERT(room < selected.size());
3165 bTmp = selected[room];
3166 b2Tmp = false; // In room
3170 p1Tmp.rx() = point.x() / gridX;
3171 p1Tmp.ry() = point.y() / gridY;
3174 // else if (nFlags & MK_SHIFT)
3175 else if (nFlags & Qt::ShiftModifier)
3178 opInProgress = gmoShiftDown;
3179 selectOnlyRoom(room);
3180 iTmp = -1; // Not a page
3181 p1Tmp.rx() = point.x() / gridX;
3182 p1Tmp.ry() = point.y() / gridY;
3183 p2Tmp = doc->room[room].pos;
3187 selectOnlyRoom(room);
3189 if (!(doc->room[room].isCorner()))
3190 emit(RoomClicked(doc, room));
3192 if (doc->room[room].isCorner())
3195 opInProgress = gmoControlDown;
3196 bTmp = b2Tmp = false; // Leave the corner selected; in room
3197 p1Tmp.rx() = point.x() / gridX;
3198 p1Tmp.ry() = point.y() / gridY;
3200 else if (corner != rcNone)
3203 opInProgress = gmoAddEdge;
3204 bTmp = doc->isDirty;
3205 iTmp = doc->findEdge(room, corner, edgeTmp);
3209 opInProgress = gmoChangeEdge;
3210 rTmp.setCoords(point.x() - 29, point.y() - 29, point.x() + 29, point.y() + 29);
3211 // rTmp.InflateRect(29, 29);
3214 if ((iTmp < 0) || (edgeTmp.type2 & etNoRoom2))
3216 edgeTmp.room1 = room;
3217 edgeTmp.end1 = corner;
3218 // This is setting edge types when it shouldn't... So we're commenting this out for now.
3219 // setEdgeType(edgeTmp);
3223 edgeTmp.end2 = rcSW;
3224 doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3227 // OnPaint(); // Update the view immediately
3230 if (opInProgress == gmoAddEdge)
3235 if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3238 QPen * oldPen = dc.SelectObject(&penEdge);
3241 dc.SelectObject(oldPen);
3249 else if (event->button() == Qt::RightButton)
3251 //void MapView::OnRButtonDown(unsigned int nFlags, QPoint point)
3253 /* if (opInProgress == gmoAddCorner)
3254 opInProgress = gmoNone;
3256 // CClientDC dc(this);
3257 // OnPrepareDC(&dc);
3258 // dc.DPtoLP(&point);
3260 scrollDragStart = point;
3266 // ::SetCursor(handCursor);
3269 // scrollDragTimer = SetTimer(menuTimer, clickTicks, NULL);
3276 bool MapView::calcScrollPosition(QPoint & pt) const
3286 // VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_HORZ, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3288 // if (pt.x + si.nPage > si.nMax + scrollStep.cx)
3289 // pt.x = si.nMax - si.nPage + scrollStep.cx;
3291 // VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_VERT, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3293 // if (-pt.y + si.nPage > si.nMax + scrollStep.cy)
3294 // pt.y = si.nPage - si.nMax - scrollStep.cy;
3296 // pt.x -= pt.x % scrollStep.cx;
3297 // pt.y -= pt.y % scrollStep.cy;
3299 return offset != pt;
3304 void MapView::scrollToPosition(const QPoint & pt)
3306 QPoint newOffset(pt);
3308 if (!calcScrollPosition(newOffset))
3309 return; // Didn't scroll
3311 // QSize delta(newOffset - offset);
3314 // CClientDC dc(this);
3315 // OnPrepareDC(&dc);
3316 // dc.LPtoDP(&delta);
3318 // SetScrollPos(SB_VERT, -offset.y);
3319 // SetScrollPos(SB_HORZ, offset.x);
3320 // ScrollWindow(-delta.cx, delta.cy);
3324 void MapView::mouseMoveEvent(QMouseEvent * event)
3325 //void MapView::OnMouseMove(unsigned int nFlags, QPoint point)
3327 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3328 // QPoint point = event->pos() * 5;
3329 QPointF ptf = event->localPos() * (100.0 / zoom);
3330 QPoint point = ptf.toPoint();
3331 // QPoint point = event->pos() * (100 / zoom);
3332 int nFlags = event->modifiers();
3337 // Location where the mouse is currently pointing
3340 // Bail if we're just mousing around...
3341 if (mouseDown == false)
3343 hoveredEdge = FindHoveredEdge();
3349 /* if (scrollDragTimer)
3351 // KillTimer(scrollDragTimer);
3352 scrollDragTimer = 0;
3355 // ::SetCursor(handCursor);
3358 // if (GetCapture() != this)
3361 // CClientDC dc(this);
3362 // OnPrepareDC(&dc);
3363 // dc.DPtoLP(&point);
3370 // p.Offset(scrollDragStart - point);
3371 p += (scrollDragStart - point);
3373 if (!calcScrollPosition(p)
3374 )// || PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3377 if ((opInProgress == gmoAddEdge) || (opInProgress == gmoSelectBox)
3378 || (opInProgress >= gmoDrag))
3381 // We must handle these specially to avoid display artifacts:
3383 CGdiObject * old = NULL;
3387 switch (opInProgress)
3390 if (!pen.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0 ,0)))
3393 old = dc.SelectObject(&pen);
3399 old = dc.SelectStockObject(HOLLOW_BRUSH);
3405 break; // Must be gmoDrag
3409 scrollToPosition(p);
3411 // OnPrepareDC(&dc); // Update with new offset
3414 switch (opInProgress)
3416 case gmoAddEdge: dc.MoveTo(p1Tmp); dc.LineTo(p2Tmp); break;
3417 case gmoSelectBox: dc.Rectangle(rTmp); break;
3418 default: /* drag*/ drawSelected(&dc); break;
3422 dc.SelectObject(old);
3426 scrollToPosition(p);
3428 QPoint delta = (point + offset) - scrollDragStart;
3429 offset = offsetScrollDragStart + delta;
3435 if (opInProgress == gmoChangeEdge)
3437 // if (rTmp.PtInRect(point))
3438 if (rTmp.contains(point))
3441 // e2Tmp = doc->getEdge(iTmp); // Save the current edge data
3442 e2Tmp = doc->edge[iTmp]; // Save the current edge data
3443 doc->deleteEdge(iTmp); // Delete the old edge
3444 // OnPaint(); // Update the view immediately
3445 opInProgress = gmoAddEdge; // Now fall through to gmoAddEdge
3448 if (opInProgress == gmoAddEdge)
3450 // dc.SetROP2(R2_NOT);
3453 // if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0))) // PORT
3456 // QPen * oldPen = dc.SelectObject(&penEdge);
3457 // dc.MoveTo(p1Tmp);
3458 // dc.LineTo(p2Tmp);
3461 int room = doc->roomHit(point, &corner);
3463 if (corner != rcNone && room != edgeTmp.room1)
3465 edgeTmp.room2 = room;
3466 edgeTmp.end2 = corner;
3467 doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3474 // dc.MoveTo(p1Tmp);
3475 // dc.LineTo(p2Tmp);
3476 // dc.SelectObject(oldPen);
3478 else if (opInProgress == gmoSelectBox)
3483 // dc.SetROP2(R2_NOT);
3484 // QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3485 // dc.Rectangle(rTmp);
3487 rTmp = QRect(p1Tmp, point).normalized();
3488 // rTmp.NormalizeRect();
3489 RoomConstItr room = doc->room.getVector().begin();
3490 // QRect intersect, r;
3492 for(int i=doc->room.size()-1; i>=0; i--)
3494 if (selected[i] == 2)
3495 continue; // Room was already selected before selection box
3498 room[i]->getRect(r);
3499 // intersect.IntersectRect(rTmp, r);
3500 intersect = rTmp & r;
3502 QRect r = room[i]->getRect();
3503 QRect intersect = rTmp & r;
3506 if ((selected[i] != 0) != bool(r == intersect))
3508 selected[i] = !selected[i];
3509 numSelected += (selected[i] ? 1 : -1);
3510 // r.DeflateRect(5, 5);
3512 // r.NormalizeRect();
3513 // InvalidateRect(&r);
3519 for(int i=doc->page.size()-1; i>=0; i--)
3521 if (selectedPage[i] == 2)
3522 continue; // Page was already selected before selection box
3525 doc->getPageRect(i, r);
3526 // intersect.IntersectRect(rTmp, r);
3527 intersect = rTmp & r;
3529 QRect r = doc->getPageRect(i);
3530 QRect intersect = rTmp & r;
3533 if ((selectedPage[i] != 0) != bool(r == intersect))
3535 selectedPage[i] = !selectedPage[i];
3536 numSelectedPages += (selectedPage[i] ? 1 : -1);
3537 // r.DeflateRect(5, 5);
3539 // r.NormalizeRect();
3540 // InvalidateRect(&r);
3545 if ((numSelected == 1) && !numSelectedPages)
3547 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
3548 ; // Find the selected room
3555 // if (GetUpdateRect(NULL))
3558 // dc.Rectangle(rTmp);
3559 // dc.SelectObject(oldBrush);
3562 else if (opInProgress == gmoControlDown)
3564 p2Tmp.rx() = gridX * (point.x() / gridX - p1Tmp.x());
3565 p2Tmp.ry() = gridY * (point.y() / gridY - p1Tmp.y());
3567 if (p2Tmp.x() || p2Tmp.y())
3569 opInProgress = gmoControlDrag;
3570 computeSelectedRect(rTmp);
3571 // drawBackground(&dc);
3572 // dc.SetROP2(R2_NOT);
3573 // drawSelected(&dc);
3576 else if (opInProgress == gmoShiftDown)
3578 long ox = point.x() / gridX;
3579 long oy = point.y() / gridY;
3582 opInProgress = gmoShiftDragLeft;
3583 else if (ox > p1Tmp.x())
3584 opInProgress = gmoShiftDragRight;
3585 else if (oy > p1Tmp.y())
3586 opInProgress = gmoShiftDragUp;
3587 else if (oy < p1Tmp.y())
3588 opInProgress = gmoShiftDragDown;
3592 // if dragging a page
3595 ASSERT(!numSelected && (numSelectedPages == 1));
3596 rTmp = doc->getPageRect(iTmp);
3598 switch (opInProgress)
3600 case gmoShiftDragUp:
3601 p2Tmp.ry() = rTmp.top();
3603 case gmoShiftDragLeft:
3604 p2Tmp.rx() = rTmp.right() - roomWidth + gridX;
3609 RoomConstItr room = doc->room.getVector().begin();
3610 const int roomCount = doc->room.size();
3612 for(int i=0; i<roomCount; i++)
3614 if ((opInProgress == gmoShiftDragLeft && room[i]->pos.x() <= p2Tmp.x())
3615 || (opInProgress == gmoShiftDragRight && room[i]->pos.x() >= p2Tmp.x())
3616 || (opInProgress == gmoShiftDragUp && room[i]->pos.y() >= p2Tmp.y())
3617 || (opInProgress == gmoShiftDragDown && room[i]->pos.y() <= p2Tmp.y()))
3618 selectRoom(i, false);
3622 // shift-dragging a room
3623 if (opInProgress == gmoShiftDragLeft)
3624 p2Tmp.rx() += roomWidth;
3625 else if (opInProgress == gmoShiftDragDown)
3626 p2Tmp.ry() += roomHeight;
3630 // shift-dragging a page
3631 if (opInProgress == gmoShiftDragLeft)
3632 p2Tmp.rx() += gridX;
3633 else if (opInProgress == gmoShiftDragRight)
3634 p2Tmp.rx() -= gridX;
3635 else if (opInProgress == gmoShiftDragUp)
3636 p2Tmp.ry() -= gridY;
3637 else if (opInProgress == gmoShiftDragDown)
3638 p2Tmp.ry() += gridY;
3641 for(i=doc->page.size()-1; i>=0; --i)
3643 rTmp = doc->getPageRect(i);
3645 if ((opInProgress == gmoShiftDragLeft && rTmp.right() < p2Tmp.x())
3646 || (opInProgress == gmoShiftDragRight && rTmp.left() > p2Tmp.x())
3647 || (opInProgress == gmoShiftDragUp && rTmp.top() > p2Tmp.y())
3648 || (opInProgress == gmoShiftDragDown && rTmp.bottom() < p2Tmp.y()))
3649 selectPage(i, false);
3655 p2Tmp.rx() = gridX * (point.x() / gridX - p1Tmp.x());
3656 p2Tmp.ry() = gridY * (point.y() / gridY - p1Tmp.y());
3657 computeSelectedRect(rTmp);
3658 // drawBackground(&dc);
3659 // dc.SetROP2(R2_NOT);
3660 // drawSelected(&dc);
3662 // end else if gmoShiftDown
3663 else if (opInProgress >= gmoDrag)
3667 // if (PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3668 // return; // The mouse has already been moved again
3670 QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3679 // dc.SetROP2(R2_NOT);
3680 // drawSelected(&dc);
3682 // drawSelected(&dc);
3686 // Maybe have more discriminant updating... Maybe.
3690 //void MapView::OnLButtonUp(unsigned int nFlags, QPoint point)
3691 void MapView::mouseReleaseEvent(QMouseEvent * event)
3693 // We need to adjust the position to take into account the scale factor, which in this case, is 1/5:
3694 // QPoint point = event->pos() * 5;
3695 QPointF ptf = event->localPos() * (100.0 / zoom);
3696 QPoint point = ptf.toPoint();
3697 // QPoint point = event->pos() * (100 / zoom);
3698 int nFlags = event->modifiers();
3704 if (event->button() == Qt::LeftButton)
3706 // if (GetCapture() != this)
3708 // if (selectedOne >= 0)
3709 // makeRoomVisible(selectedOne);
3715 // ReleaseCapture();
3717 // CClientDC dc(this);
3718 // OnPrepareDC(&dc);
3719 // dc.DPtoLP(&point);
3721 if (opInProgress == gmoAddEdge)
3723 // Erase the rubber band:
3724 // dc.SetROP2(R2_NOT);
3727 // if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3730 // QPen * oldPen = dc.SelectObject(&penEdge);
3731 // dc.MoveTo(p1Tmp);
3732 // dc.LineTo(p2Tmp);
3733 // dc.SelectObject(oldPen);
3735 // Find out where the edge goes:
3737 int room = doc->roomHit(point, &corner);
3739 if (corner != rcNone && room != edgeTmp.room1)
3741 // The edge goes somewhere
3742 // If there's a stub on the other end, delete it:
3743 EdgeVec deletedEdges;
3745 int oldEdgeNum = doc->findEdge(room, corner, oldEdge);
3747 if (oldEdgeNum >= 0 && (oldEdge.type2 & etUnexplored))
3749 // deletedEdges.push_back(doc->getEdge(oldEdgeNum));
3750 deletedEdges.push_back(doc->edge[oldEdgeNum]);
3751 doc->deleteEdge(oldEdgeNum);
3754 // Add the new or changed edge:
3755 if (edgeTmp.type2 & etOneWay)
3757 edgeTmp.room2 = edgeTmp.room1;
3758 edgeTmp.end2 = edgeTmp.end1;
3759 edgeTmp.type1 = edgeTmp.type2 | (edgeTmp.type1 & etObstructed);
3760 edgeTmp.type2 = etNormal;
3761 edgeTmp.room1 = room;
3762 edgeTmp.end1 = corner;
3766 edgeTmp.room2 = room;
3767 edgeTmp.end2 = corner;
3771 deletedEdges.push_back(e2Tmp);
3773 if (deletedEdges.size())
3775 if (deletedEdges.size() == 1)
3776 doc->setUndoData(new UndoChangeEdge(bTmp, deletedEdges[0]));
3778 doc->setUndoData(new UndoChanges(bTmp, -1, 1, deletedEdges));
3781 doc->setUndoData(new UndoAdd(bTmp));
3783 doc->addEdge(edgeTmp);
3789 // We just deleted the old edge
3790 if ((e2Tmp.end1 == rcCorner)
3791 || (!(e2Tmp.type1 & etNoRoom2) && (e2Tmp.end2 == rcCorner)))
3793 doc->addEdge(e2Tmp); // Put it back temporarily
3794 selectOnlyRoom((e2Tmp.end1 == rcCorner) ? e2Tmp.room1 : e2Tmp.room2);
3795 deleteSelection(false); // Remove entire connection
3798 // not a connection to a corner
3799 doc->setUndoData(new UndoDelete(bTmp, e2Tmp));
3801 else if (selectedOne >= 0)
3802 makeRoomVisible(selectedOne); // We just clicked on a room corner
3804 // end if edge doesn't go anywhere
3806 else if (opInProgress == gmoChangeEdge)
3808 // We didn't change the edge
3809 if (selectedOne >= 0)
3810 makeRoomVisible(selectedOne);
3812 // else if (opInProgress == gmoSelectBox)
3814 // dc.SetROP2(R2_NOT);
3815 // QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3816 // dc.Rectangle(rTmp);
3817 // dc.SelectObject(oldBrush);
3819 else if (opInProgress == gmoControlDown)
3830 // end if room or page was already selected
3832 else if (opInProgress >= gmoDrag)
3834 QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3839 const QSize offset1(p.x(), p.y());
3840 doc->setUndoData(new UndoMove(doc->isDirty, offset1, numSelected, selected, numSelectedPages, selectedPage));
3842 for(int i=doc->room.size()-1; i>=0; i--)
3845 doc->moveRoom(i, offset1);
3848 for(int i=doc->page.size()-1; i>=0; i--)
3850 if (selectedPage[i])
3851 doc->movePage(i, offset1);
3855 // InvalidateRect(NULL);
3858 opInProgress = gmoNone;
3860 else if (event->button() == Qt::RightButton)
3862 //void MapView::OnRButtonUp(unsigned int nFlags, QPoint point)
3864 // if (scrollDragTimer)
3867 // KillTimer(scrollDragTimer);
3868 scrollDragTimer = 0;
3871 int room = doc->roomHit(point, &corner);
3872 cornerClicked = corner; // Bleah
3873 roomClicked = room; // Bleah
3877 if (showPages && (room = doc->pageBorderHit(point)) >= 0)
3884 mapContextMenu->popup(mapToGlobal(event->pos()));
3888 selectOnlyRoom(room);
3889 opInProgress = gmoNone;
3891 if ((corner != rcNone) && !doc->locked)
3893 iTmp = doc->findEdge(room, corner, edgeTmp);
3895 if ((iTmp >= 0) && !(edgeTmp.type2 & etNoRoom2))
3897 opInProgress = gmoAddCorner;
3898 addCornerAct->setEnabled(true);
3901 addCornerAct->setEnabled(false);
3904 roomContextMenu->popup(mapToGlobal(event->pos()));
3905 mouse = QPoint(0, 0);
3908 // dc.LPtoDP(&point);
3909 // ClientToScreen(&point);
3910 // pop->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
3911 // //opInProgress = gmoNone; // FIXME
3913 // else if (!scrollDrag || (GetCapture() != this))
3914 // CScrollZoomView::OnRButtonUp(nFlags, point);
3917 // if (opInProgress == gmoNone)
3918 // ReleaseCapture();
3920 // ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3922 // scrollDrag = false;
3931 int MapView::OnMouseActivate(CWnd * wnd, unsigned int hitTest, unsigned int message)
3933 const int result = CScrollZoomView::OnMouseActivate(wnd, hitTest, message);
3935 if ((result == MA_ACTIVATE) && GUEmapEatClicks)
3936 return MA_ACTIVATEANDEAT;
3941 bool MapView::OnMouseWheel(unsigned int nFlags, short zDelta, QPoint pt)
3943 if (nFlags == MK_CONTROL)
3945 OnViewZoom((zDelta > 0) ? ID_VIEW_ZOOM_IN : ID_VIEW_ZOOM_OUT);
3949 return CScrollZoomView::OnMouseWheel(nFlags, zDelta, pt);
3952 void MapView::OnSize(unsigned int nType, int cx, int cy)
3954 CScrollZoomView::OnSize(nType, cx, cy);
3956 if (initialized()) // Make sure view has been initialized
3960 void MapView::OnTimer(unsigned int idEvent)
3962 if (idEvent == menuTimer)
3964 // We've held the button down long enough, switch to dragging:
3966 KillTimer(scrollDragTimer);
3967 scrollDragTimer = 0;
3969 ::SetCursor(handCursor);
3972 CScrollZoomView::OnTimer(idEvent);
3976 // Update the view after the document changes:
3981 // A room has just been deleted
3983 // The number of pages has changed
3985 // A room comment was just changed
3986 // Do not invalidate the view unless pHint is not NULL.
3987 // dupNavigationMode:
3988 // We are entering navigation mode
3989 // Ignore pHint, do not invalidate the view, just make sure
3990 // exactly one room is selected and that it's visible.
3992 // NULL means invalidate the entire view (but see lHint)
3993 // Otherwise, it's a QRect* to the area to invalidate
3995 void MapView::OnUpdate(CView * pSender, LPARAM lHint, CObject * pHint)
3997 if (lHint == dupDeletedRoom && opInProgress != gmoDeleting && numSelected)
3999 // Clear selection unless this is the view doing the deleting:
4003 else if (lHint == dupPageCount && opInProgress != gmoDeleting)
4005 selectedPage.resize(GetDocument()->getPages().size(), 0);
4007 if (numSelectedPages)
4013 else if (lHint == dupRoomComment)
4015 // Update comment for this view, but don't override any other view:
4016 static_cast<CMapApp*>(AfxGetApp())->setComment(this,
4017 (selectedOne >= 0 ? &(GetDocument()->getRoom(selectedOne)) : NULL), false);
4020 return; // Don't invalidate entire view
4023 if (lHint == dupNavigationMode)
4025 const MapDoc * doc = GetDocument();
4027 const int numRooms = doc->room.size();
4029 if (selectedOne < 0 || doc->room[selectedOne].isCorner())
4032 makeRoomVisible(selectedOne);
4033 } // end if switching to navigation mode
4036 QRect rect(*reinterpret_cast<const QRect *>(pHint));
4040 rect.NormalizeRect();
4041 InvalidateRect(rect);
4043 else if (lHint != dupRoomComment)
4047 void MapView::OnUpdateEditAddCorner(CCmdUI* pCmdUI)
4049 pCmdUI->Enable(opInProgress == gmoAddCorner);
4052 void MapView::OnUpdateEditPaste(CCmdUI* pCmdUI)
4054 pCmdUI->Enable((static_cast<CMapApp*>(AfxGetApp())->getClipboard() != NULL)
4055 && !GetDocument()->locked);
4059 // Commands which require selected rooms and unlocked document:
4064 // Edit|Select connected
4066 void MapView::OnUpdateSelectedUnlocked(CCmdUI * pCmdUI)
4068 MapDoc * const doc = GetDocument();
4071 bool selected = numSelected;
4073 if ((pCmdUI->m_nID != ID_EDIT_SELECT_CONNECTED) && numSelectedPages)
4075 if ((pCmdUI->m_nID != ID_EDIT_COPY)
4076 && (doc->page.size() == numSelectedPages))
4077 selected = false; // Can't delete all pages
4082 pCmdUI->Enable(selected && !doc->locked);
4086 // Commands which require the document to be unlocked:
4090 void MapView::OnUpdateUnlocked(CCmdUI* pCmdUI)
4092 pCmdUI->Enable(!GetDocument()->locked);
4096 // Toggle the grid on and off:
4098 void MapView::OnViewGrid()
4100 showGrid = !showGrid;
4101 InvalidateRect(NULL);
4104 void MapView::OnUpdateViewGrid(CCmdUI * pCmdUI)
4106 pCmdUI->SetCheck(showGrid);
4109 void MapView::OnViewZoom(unsigned int cmd)
4111 ASSERT((cmd == ID_VIEW_ZOOM_IN) || (cmd == ID_VIEW_ZOOM_OUT));
4113 if (cmd == ID_VIEW_ZOOM_OUT)
4114 zoomTo(zoom % 10 ? zoom - zoom % 10 : zoom - 10);
4116 zoomTo(zoom + 10 - zoom % 10);
4119 void MapView::OnUpdateViewZoom(CCmdUI * pCmdUI)
4121 pCmdUI->Enable((pCmdUI->m_nID == ID_VIEW_ZOOM_IN)
4122 ? (zoom < maxZoom) : (zoom > minZoom));
4126 // Redraw the window:
4128 void MapView::OnWindowRefresh()
4130 InvalidateRect(NULL);
4133 /////////////////////////////////////////////////////////////////////////////
4134 /////////////////////////////////////////////////////////////////////////////
4135 // CRepaginateDlg dialog
4137 CRepaginateDlg::CRepaginateDlg(): CDialog(CRepaginateDlg::IDD, NULL)
4139 //{{AFX_DATA_INIT(CRepaginateDlg)
4143 BEGIN_MESSAGE_MAP(CRepaginateDlg, CDialog)
4144 //{{AFX_MSG_MAP(CRepaginateDlg)
4145 ON_BN_CLICKED(IDYES, OnYes)
4149 /////////////////////////////////////////////////////////////////////////////
4150 // CRepaginateDlg message handlers
4152 bool CRepaginateDlg::OnInitDialog()
4154 CDialog::OnInitDialog();
4156 SendDlgItemMessage(IDC_EXCLAMATION, STM_SETICON, (WPARAM) gueApp()->LoadStandardIcon(IDI_EXCLAMATION));
4157 MessageBeep(MB_ICONEXCLAMATION);
4159 return true; // return TRUE unless you set the focus to a control
4162 void CRepaginateDlg::OnYes()