]> Shamusworld >> Repos - guemap/blob - src/mapview.cpp
Initial commit of GUEmap v3.
[guemap] / src / mapview.cpp
1 //
2 // GUEmap
3 // (C) 1997-2007 Christopher J. Madsen
4 // (C) 2019 James Hammons
5 //
6 // GUEmap is licensed under either version 2 of the GPL, or (at your option)
7 // any later version.  See LICENSE file for details.
8 //
9 // mapview.cpp: implementation of the CMapView class
10 //
11
12 #include "mapview.h"
13
14 #include <math.h>
15 #include "mainwin.h"
16 #include "mapdialog.h"
17 #include "mapdoc.h"
18 #include "mathconstants.h"
19 //#include "properties.h"
20 #include "roomdialog.h"
21 #include "undo.h"
22
23
24 const int
25         penEdgeWidth = 9,
26         penPageWidth = 20,
27         penRoomWidth = 5,
28         maxZoom      = 200,
29         minZoom      = 20,
30         minZoomGrid  = 50;
31
32 const unsigned int
33         clickTicks   = 500,
34         menuTimer    = 1;
35
36 struct DirectionName
37 {
38         RoomCorner dir;
39         const char * name;
40 };
41
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" },
51         { rcNone, NULL }
52 };
53
54 static const char a2z[] = "abcdefghijklmnopqrstuvwxyz";
55
56
57 //
58 // Parse a direction word:
59 //
60 // Input:
61 //   text:  The string to be tested (must be in lowercase, may be empty)
62 //
63 // Output:
64 //   text:  Has the direction word and whitespace stripped from the beginning
65 //
66 // Returns:
67 //   The direction indicated by the text
68 //   rcNone if not a direction
69 //
70 RoomCorner parseDirection(string & text)
71 {
72 #if 0
73         const CString word(text.SpanIncluding(a2z));
74
75         if (!word.IsEmpty())
76         {
77                 for(int i=0; directions[i].dir != rcNone; i++)
78                 {
79                         if (word == directions[i].name)
80                         {
81                                 text = text.Mid(word.GetLength());
82                                 text.TrimLeft();
83                                 return directions[i].dir;
84                         }
85                 }
86         }
87 #endif
88
89         return rcNone;
90 }
91
92
93 #if 0
94 /////////////////////////////////////////////////////////////////////////////
95 // CRepaginateDlg dialog
96
97 class CRepaginateDlg : public CDialog
98 {
99         // Construction
100         public:
101                 CRepaginateDlg();   // standard constructor
102
103         // Dialog Data
104         //{{AFX_DATA(CRepaginateDlg)
105         enum { IDD = IDD_REPAGINATE };
106         //}}AFX_DATA
107
108         // Overrides
109         // ClassWizard generated virtual function overrides
110         //{{AFX_VIRTUAL(CRepaginateDlg)
111         //}}AFX_VIRTUAL
112
113         // Implementation
114         protected:
115                 // Generated message map functions
116                 //{{AFX_MSG(CRepaginateDlg)
117                 afx_msg void OnYes();
118                 virtual bool OnInitDialog();
119                 //}}AFX_MSG
120
121         DECLARE_MESSAGE_MAP();
122 };
123 #endif
124
125
126
127 /////////////////////////////////////////////////////////////////////////////
128 /////////////////////////////////////////////////////////////////////////////
129 // CMapView
130
131 #if 0
132 IMPLEMENT_DYNCREATE(CMapView, CScrollZoomView)
133
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)
156         ON_WM_CHAR()
157         ON_WM_KEYDOWN()
158         ON_WM_LBUTTONDBLCLK()
159         ON_WM_LBUTTONDOWN()
160         ON_WM_LBUTTONUP()
161         ON_WM_MOUSEMOVE()
162         ON_WM_MOUSEWHEEL()
163         ON_WM_RBUTTONDOWN()
164         ON_WM_RBUTTONUP()
165         ON_WM_SIZE()
166         ON_WMtrIMER()
167         //}}AFX_MSG_MAP
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)
178 END_MESSAGE_MAP();
179 #endif
180
181 //===========================================================================
182 // Class CMapView:
183 //
184 // Member Variables:
185 //   opInProgress:  Indicates what's happening while the mouse button is down
186 //     gmoAddCorner:
187 //       iTmp:     The edge that needs a corner
188 //     gmoAddEdge:
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
195 //     gmoChangeEdge:
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
200 //     gmoDeleting:
201 //       Used in OnEditClear to tell OnUpdate not to clear the selection
202 //     gmoSelectBox:
203 //       p1Tmp:  The fixed corner of the selection rectangle
204 //       p2Tmp:  The moving corner of the selection rectangle
205 //       rTmp:   The current selection rectangle
206 //     gmoControlDown:
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)
212 //     gmoShiftDown:
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)
217 //       p2Tmp:
218 //         The offset to the current grid cell (in logical coordinates)
219 //       rTmp:
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.
222 //   font:
223 //     The font used for room names
224 //   numSelected:
225 //     The number of rooms currently selected
226 //   numSelectedPages:
227 //     The number of pages currently selected
228 //   printingPage:
229 //     The number of the page being printed or previewed
230 //     0 means normal screen display
231 //   scrollDrag:
232 //     True if we are in the middle of a scroll-by-drag operation
233 //   scrollDragStart:
234 //     The point under the cursor when a scrollDrag started
235 //   selectedOne:
236 //     The selected room's number, if exactly 1 room is selected
237 //     -1 otherwise
238 //   selected:
239 //     selected[N] is non-zero iff room N is selected
240 //       2 means the room should stay selected during gmoSelectBox
241 //   selectedPage:
242 //     selectedPage[N] is non-zero iff page N is selected
243 //       2 means the page should stay selected during gmoSelectBox
244 //   zoom:
245 //     Zoom factor in percent
246 //
247
248 //
249 // CMapView construction/destruction
250 //
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)
257 {
258         doc = new MapDoc();
259
260         selectedPage.resize(doc->page.size(), 0);
261         clearSelection(false);
262
263         setFocusPolicy(Qt::StrongFocus);
264         // Make sure we get *ALL* mouse move events!
265         setMouseTracking(true);
266
267         // Set the document size:
268 //scrollzoom.cpp ->     init(4 * gridX, doc->getDocSize(), QSize(gridX, gridY));
269 //  CClientDC dc(this);
270 //  OnPrepareDC(&dc);
271 //      LOGFONT lf;
272 //      memset(&lf, 0, sizeof(lf));
273 //      lf.lfHeight = 62;
274 //      strcpy(lf.lfFaceName, "Arial");
275 //      font.CreateFontIndirect(&lf);
276
277 //      if (doc->locked)
278 //              OnUpdate(NULL, dupNavigationMode, NULL);
279
280 //      CScrollZoomView::OnInitialUpdate();
281 //      setScrollBars();              // Now fix the scroll bars
282
283         // Actions
284         deleteRoomAct = CreateAction("Delete", "Delete room", "Deletes selected rooms", QIcon(), QKeySequence(), false, this);
285         connect(deleteRoomAct, SIGNAL(triggered()), this, SLOT(HandleDelete()));
286
287         roomPropertiesAct = CreateAction("Properties...", "Properties", "Opens the properties dialog for this room", QIcon(), QKeySequence(), false, this);
288         connect(roomPropertiesAct, SIGNAL(triggered()), this, SLOT(HandleRoomProperties()));
289
290         mapPropertiesAct = CreateAction("Properties...", "Properties", "Opens the properties dialog for this map", QIcon(), QKeySequence(), false, this);
291         connect(mapPropertiesAct, SIGNAL(triggered()), this, SLOT(HandleMapProperties()));
292
293         selectConnectedAct = CreateAction("Select connected rooms", "Select connected", "Selects connected rooms", QIcon(), QKeySequence(), false, this);
294
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()));
297
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()));
300
301         addPageAct = CreateAction("Add Page", "Add Page", "Adds a page to the map", QIcon(), QKeySequence(), false, this);
302
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()));
305
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()));
308
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()));
311
312         addUpAct = CreateAction("Up", "Up", "Marks the selected corner as Up", QIcon(), QKeySequence(), false, this);
313         connect(addUpAct, SIGNAL(triggered()), this, SLOT(HandleAddUp()));
314
315         addDownAct = CreateAction("Down", "Down", "Marks the selected corner as Down", QIcon(), QKeySequence(), false, this);
316         connect(addDownAct, SIGNAL(triggered()), this, SLOT(HandleAddDown()));
317
318         addInAct = CreateAction("In", "In", "Marks the selected corner as In", QIcon(), QKeySequence(), false, this);
319         connect(addInAct, SIGNAL(triggered()), this, SLOT(HandleAddIn()));
320
321         addOutAct = CreateAction("Out", "Out", "Marks the selected corner as Out", QIcon(), QKeySequence(), false, this);
322         connect(addOutAct, SIGNAL(triggered()), this, SLOT(HandleAddOut()));
323
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()));
326
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()));
329
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()));
332
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()));
335
336         // Popup menus
337         roomContextMenu = new QMenu(this);
338         mapContextMenu = new QMenu(this);
339
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);
356
357         mapContextMenu->addAction(mapPropertiesAct);
358         mapContextMenu->addAction(addRoomAct);
359         mapContextMenu->addAction(addPageAct);
360         mapContextMenu->addAction(deleteRoomAct);
361 }
362
363
364 MapView::~MapView()
365 {
366         delete doc;
367 //      gueApp()->closingView(this);  // Remove any comment for this view
368 }
369
370
371 void MapView::DrawArrowhead(QPainter * painter, QPointF head, QPointF tail)
372 {
373         QPolygonF arrow;
374
375         const double angle = Angle(tail - head);
376         const double orthoAngle = angle + QTR_TAU;
377         const double size = 5.0;
378
379         QPointF ortho(cos(orthoAngle), sin(orthoAngle));
380         QPointF unit = UnitVector(head - tail);
381
382         QPointF p1 = head - (unit * 9.0 * size);
383         QPointF p2 = p1 + (ortho * 3.0 * size);
384         QPointF p3 = p1 - (ortho * 3.0 * size);
385
386         arrow << head << p2 << p3;
387         painter->drawPolygon(arrow);
388 }
389
390
391 void MapView::DrawNoExit(QPainter * painter, QPointF head, QPointF tail)
392 {
393         const double angle = Angle(tail - head);
394         const double orthoAngle = angle + QTR_TAU;
395         const double size = 5.0;
396
397         QPointF ortho(cos(orthoAngle), sin(orthoAngle));
398         QPointF unit = UnitVector(head - tail);
399
400         QPointF p2 = head + (ortho * 3.0 * size);
401         QPointF p3 = head - (ortho * 3.0 * size);
402
403         painter->drawLine(p2, p3);
404 }
405
406
407 //
408 // MapView drawing
409 //
410 void MapView::paintEvent(QPaintEvent * /*event*/)
411 {
412         const char edgeLabel[] = "UDIO";
413
414         const QPoint edgLblOffset[] = {
415                 // N, S, E, W
416                 { roomWidth / 2 + 24, -13 }, { roomWidth / 2 - 48, roomHeight + 52 },
417                 { roomWidth + 24, roomHeight / 2 + 57 }, { -48, roomHeight / 2 - 19 },
418                 // NE, NW, SE, SW
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 }
425         };
426
427         const int
428                 cantGoSide   = 19,
429                 cantGoAngleX = gridY / 6,
430                 cantGoAngleY = gridX / 6;
431 /*
432         rcN, rcS, rcE, rcW,
433         rcNE, rcNW, rcSE, rcSW,
434         rcNNE, rcNNW, rcSSE, rcSSW,
435 */
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 },
449         };
450
451         QPainter painter(this);
452         QPainter * dc = &painter;
453
454         painter.setRenderHint(QPainter::Antialiasing);
455
456 #if 0
457         dc->save();
458         dc->setFont(font);
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));
462         dc->restore();
463 #endif
464
465         QTransform t1;
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);
471 #if 0
472 //      QPainter painter(this);
473         painter.save();
474         painter.setPen(QPen(Qt::blue, 1, Qt::DashLine));
475         painter.drawRect(0, 0, 100, 100);
476
477         QTransform transform;
478         transform.translate(50, 50);
479         transform.rotate(45);
480         transform.scale(0.5, -1.0);
481         painter.setTransform(transform);
482
483         painter.setFont(QFont("Helvetica", 24));
484         painter.setPen(QPen(Qt::black, 1));
485         painter.drawText(20, 10, "QTransform");
486         painter.restore();
487 #endif
488
489 #if 0
490         QBrush focusBrush;
491         QPen penEdge;
492         QPen penGrid;
493         QPen penRoom;
494         LOGBRUSH lb;
495         lb.lbStyle = BS_SOLID;
496         lb.lbColor = RGB(0, 0, 0);
497
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)))
502                 return;
503
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);
508 #else
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);
516         dc->setFont(font);
517 #endif
518
519         if (printingPage)
520         {
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()));
526         }
527
528 #if 0
529         // Draw the page outlines:
530         if ((showPages || numSelectedPages) && !dc->IsPrinting())
531         {
532                 dc->SetBkMode(TRANSPARENT);
533                 QPen penPage;
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;
538                 TCHAR pageNum[4];
539                 int pNum = 1;
540                 dc->SelectStockObject(NULL_BRUSH);
541                 QBrush * const nullBrush = dc->GetCurrentBrush();
542                 bool focused = false;
543
544                 for(PageConstItr p=pageVec.begin(); p!=pageVec.end(); ++p, ++pNum)
545                 {
546                                 doc->getPageRect(*p, r);
547
548                         if (dc->RectVisible(r))
549                         {
550                                 const bool selectedI = selectedPage[pNum - 1] != 0;
551
552                                 if (!selectedI && !showPages)
553                                         continue; // Only show selected pages
554
555                                 if (selectedI != focused)
556                                 {
557                                         focused = selectedI;
558                                         dc->SelectObject(focused ? &focusBrush : nullBrush);
559                                 }
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);
566
567                                 dc->Rectangle(r.left + penPageWidth / 4, r.bottom - penPageWidth / 4, r.right, r.top);
568                                 ASSERT(pNum < 1000);
569                                 _itot(pNum, pageNum, 10);
570                                 const int pageNumL = strlen(pageNum);
571
572                                 for(i=r.left+3*gridX/2; i<r.right-gridX; i+=gridX)
573                                 {
574                                         dc->TextOut(i, r.bottom - gridY * 7 / 8, pageNum, pageNumL);
575                                         dc->TextOut(i, r.top + gridY / 4, pageNum, pageNumL);
576                                 }
577
578                                 for(i=r.bottom-gridY*15/8; i>r.top+gridY; i-=gridY)
579                                 {
580                                         dc->TextOut(r.left + gridX / 2 + penPageWidth / 4, i, pageNum, pageNumL);
581                                         dc->TextOut(r.right- gridX / 2 - penPageWidth / 4, i, pageNum, pageNumL);
582                                 }
583                         } // end if page visible
584                 } // end for pages
585
586                 dc->SetBkMode(OPAQUE);
587                 dc->SelectObject(whiteBrush);
588                 dc->SelectObject(&penEdge); // Before destroying penPage
589         }
590         else // not showing pages
591                 dc->SelectObject(&penEdge);
592 #else
593         dc->setPen(penEdge);
594 #endif
595
596         // Draw the background grid:
597         if (showGrid)// && (zoom >= minZoomGrid))//&& !dc->IsPrinting())
598         {
599 #if 0
600 printf("  Preparing to draw grid...\n");
601 //              dc->SetBkMode(TRANSPARENT);
602 //              dc->SelectObject(&penGrid);
603                 dc->setBrush(Qt::NoBrush);
604                 dc->setPen(penGrid);
605
606                 QRect clip;
607 //              dc->GetClipBox(&clip);
608 //              clip = dc->viewport();
609 //              clip = dc->window();
610                 clip = geometry();
611 printf("    clip = %i, %i, %i, %i\n", clip.x(), clip.y(), clip.width(), clip.height());
612
613                 clip.setLeft(clip.left() - (clip.left() % gridX));
614                 clip.setBottom(clip.bottom() - (clip.bottom() % gridY + gridY));
615
616                 if (clip.top() < -gridY)
617                         clip.setTop(clip.top() + gridY);
618
619                 QSize docSize(doc->getDocSize());
620                 docSize.setHeight(docSize.height() * -1);
621
622                 if (clip.right() > docSize.width())
623                         clip.setRight(docSize.width());
624
625                 if (clip.bottom() < docSize.height())
626                         clip.setBottom(docSize.height());
627
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)
630                 {
631                         dc->drawLine(i, 0, i, docSize.height());
632                 }
633
634 //              for(i=clip.bottom(); i<=clip.top(); i+=gridY)
635                 for(i=-clip.bottom(); i<=clip.top(); i+=gridY)
636                 {
637 //                      dc->drawLine(0, i, docSize.width(), i);
638                         dc->drawLine(0, i, docSize.width(), i);
639                 }
640
641 //              dc->SetBkMode(OPAQUE);
642 #else
643                 dc->setBrush(Qt::NoBrush);
644                 dc->setPen(penGrid);
645                 QSize docSize(doc->docSize);
646
647                 for(int i=0; i<=docSize.width(); i+=gridX)
648                         dc->drawLine(i, 0, i, docSize.height());
649
650                 for(int i=0; i<=docSize.height(); i+=gridY)
651                         dc->drawLine(0, i, docSize.width(), i);
652 #endif
653         }
654
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();
659         dc->setPen(penEdge);
660
661         while (i-- > 0)
662         {
663                 QPoint start, end;
664
665                 doc->getEdgePoints(*edge, start, end);
666
667                 // Offset the edge if we're dragging it...
668                 if (opInProgress >= gmoDrag)
669                 {
670                         if (selected[edge->room1])
671                                 start += p2Tmp;
672
673                         if ((selected[edge->room1] && (edge->type1 & etNoRoom2))
674                                 || selected[edge->room2])
675                                 end += p2Tmp;
676                 }
677
678                 if (edge->type1 & etObstructed)
679                         dc->setPen(hoveredEdge == i ? penEdgeDashedHover : penEdgeDashed);
680                 else
681                         dc->setPen(hoveredEdge == i ? penEdgeHover : penEdge);
682
683                 dc->drawLine(start, end);
684
685                 if (edge->type1 & etOneWay)
686                 {
687                         dc->save();
688                         dc->setBrush(Qt::black);
689                         DrawArrowhead(dc, end, start);
690                         dc->restore();
691                 }
692                 else if (edge->type1 & etNoExit)
693                 {
694                         DrawNoExit(dc, end, start);
695                 }
696                 else if (edge->type1 & etLoopBack)
697                 {
698                         const double angle = Angle(start - end);
699                         const double orthoAngle = angle + QTR_TAU;
700                         const double size = 5.0;
701
702                         QPointF ortho(cos(orthoAngle), sin(orthoAngle));
703                         QPointF unit = UnitVector(end - start);
704
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);
716                         dc->save();
717                         dc->setBrush(Qt::black);
718                         DrawArrowhead(dc, p1, p2);
719                         dc->restore();
720                 }
721
722                 char elBuf[2] = "?";
723
724                 if (edge->type1 & etDirection)
725                 {
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);
730                 }
731
732                 if (edge->type2 & etDirection)
733                 {
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);
738                 }
739
740                 edge--;
741         }
742
743         // Draw the rooms
744         dc->setPen(penRoom);
745         bool focused = false;
746         i = 0;
747
748         for(const int count=doc->room.size(); i<count; i++, room++)
749         {
750                 QRect r = (*room)->getRect();
751                 QPoint cp = (*room)->GetCenter();
752
753                 // Translate room if dragging operation in progress
754                 if ((opInProgress >= gmoDrag) && selected[i])// && !(*room)->isCorner())
755                 {
756                         r.translate(p2Tmp.x(), p2Tmp.y());
757                         cp += p2Tmp;
758                 }
759
760 //              if (!dc->RectVisible((*room)->getRect(r)))
761 //              if (!r.intersects(dc->viewport()))
762 //                      continue;
763
764                 const bool selectedI = (selected[i] != 0);
765
766                 if (selectedI != focused)
767                         focused = selectedI;
768
769                 bool hovered = r.contains(mouse);
770                 RoomCorner rc = rcNone;
771
772                 if (hovered)
773                         rc = doc->CornerHit(mouse - r.topLeft());
774
775                 dc->setPen(hovered ? penEdgeHover : penRoom);
776
777                 dc->setBrush(focused ? focusBrush : Qt::white);
778
779                 bool needOpaqueText = false;
780
781                 if ((*room)->flags & rfBorder)
782                 {
783                         dc->drawRect(r);
784                 }
785                 else if (focused)
786                 {
787                         dc->setBrush(focusBrush);
788                         dc->drawRect(r);
789                 }
790                 else // Room has no border and is not focused
791                         needOpaqueText = !(*room)->name.empty();
792
793                 if ((*room)->isCorner() && (focused || showCorners))
794                 {
795                         QRect centerRect(cp - QPoint(20, 20), QSize(40, 40));
796                         dc->save();
797                         dc->setPen(Qt::black);
798                         dc->setBrush(Qt::black);
799                         dc->drawEllipse(centerRect);
800                         dc->restore();
801                 }
802
803 //              dc->SetBkMode(TRANSPARENT);
804
805                 if ((rc >= 0) && (rc <= 11) && ((*room)->isCorner() == false))
806                 {
807                         QRect rCrnr = cornerRect[rc].translated(r.topLeft());
808 //                      rCrnr.translate(r.topLeft());
809                         dc->save();
810                         dc->setPen(Qt::NoPen);
811                         dc->setBrush(Qt::green);
812                         dc->drawRect(rCrnr);
813                         dc->restore();
814                 }
815
816                 if (!(*room)->note.empty())
817                 {
818                         QPoint noteOffset(roomWidth - 29, 57);
819
820                         dc->save();
821                         dc->setPen(QColor(0xFF, 0x00, 0x00));
822                         dc->drawText(r.topLeft() + noteOffset, "*");
823                         dc->restore();
824                 }
825
826 //              if (needOpaqueText)
827 //                      dc->SetBkMode(OPAQUE);
828
829                 // Shrink the rect left/right margins just a bit
830                 QMargins margin(20, 0, 20, 0); // LTRB
831                 r = r.marginsRemoved(margin);
832 //dc->save();
833 //dc->setPen(Qt::blue);
834 //dc->drawRect(r);
835 //dc->restore();
836
837                 dc->drawText(r, Qt::AlignCenter | Qt::TextWordWrap, (*room)->name.c_str());
838
839 //              dc->SetBkMode(OPAQUE);
840         }
841
842         // JLH: Draw special stuffs (crap that was stuffed into OnMouseDown & OnMouseMove & the like...
843         if (opInProgress == gmoSelectBox)
844         {
845                 dc->setPen(QPen(QColor(0x00, 0xFF, 0x00, 0xFF)));
846                 dc->setBrush(QBrush(QColor(0x00, 0xFF, 0x00, 0x64)));
847                 dc->drawRect(rTmp);
848         }
849         else if (opInProgress == gmoAddEdge)
850         {
851                 dc->setPen(penEdge);
852                 dc->drawLine(p1Tmp, p2Tmp);
853         }
854 }
855
856
857 void MapView::keyPressEvent(QKeyEvent * event)
858 {
859         bool oldShift = shiftDown;
860         bool oldCtrl = ctrlDown;
861         bool oldAlt = altDown;
862
863         if (event->key() == Qt::Key_Shift)
864                 shiftDown = true;
865         else if (event->key() == Qt::Key_Control)
866                 ctrlDown = true;
867         else if (event->key() == Qt::Key_Alt)
868                 altDown = true;
869
870         double oldZoom = zoom;
871
872         if (event->key() == Qt::Key_0)
873                 zoom = 100.0;
874         else if (event->key() == Qt::Key_9)
875                 zoom = 90.0;
876         else if (event->key() == Qt::Key_8)
877                 zoom = 80.0;
878         else if (event->key() == Qt::Key_7)
879                 zoom = 70.0;
880         else if (event->key() == Qt::Key_6)
881                 zoom = 60.0;
882         else if (event->key() == Qt::Key_5)
883                 zoom = 50.0;
884         else if (event->key() == Qt::Key_4)
885                 zoom = 40.0;
886         else if (event->key() == Qt::Key_3)
887                 zoom = 30.0;
888         else if (event->key() == Qt::Key_2)
889                 zoom = 20.0;
890         else if (event->key() == Qt::Key_1)
891                 zoom = 10.0;
892
893         if (zoom != oldZoom)
894                 update();
895
896 #if 0
897         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
898         {
899                 if (Global::tool)
900                         ToolHandler(ToolKeyDown, Point(0, 0));
901
902                 update();
903         }
904 #endif
905
906         if (oldAlt != altDown)
907         {
908                 scrollDrag = true;
909                 setCursor(Qt::SizeAllCursor);
910 //              oldPoint = oldScrollPoint;
911         }
912
913 #if 0
914         if (select.size() > 0)
915         {
916                 if (event->key() == Qt::Key_Up)
917                 {
918                         TranslateObjects(select, Point(0, +1.0));
919                         update();
920                 }
921                 else if (event->key() == Qt::Key_Down)
922                 {
923                         TranslateObjects(select, Point(0, -1.0));
924                         update();
925                 }
926                 else if (event->key() == Qt::Key_Right)
927                 {
928                         TranslateObjects(select, Point(+1.0, 0));
929                         update();
930                 }
931                 else if (event->key() == Qt::Key_Left)
932                 {
933                         TranslateObjects(select, Point(-1.0, 0));
934                         update();
935                 }
936         }
937 #endif
938 }
939
940
941 void MapView::keyReleaseEvent(QKeyEvent * event)
942 {
943         bool oldShift = shiftDown;
944         bool oldCtrl = ctrlDown;
945         bool oldAlt = altDown;
946
947         if (event->key() == Qt::Key_Shift)
948                 shiftDown = false;
949         else if (event->key() == Qt::Key_Control)
950                 ctrlDown = false;
951         else if (event->key() == Qt::Key_Alt)
952                 altDown = false;
953
954 #if 0
955         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
956         {
957                 if (Global::tool)
958                         ToolHandler(ToolKeyUp, Point(0, 0));
959
960                 update();
961         }
962 #endif
963
964         if (oldAlt != altDown)
965         {
966                 scrollDrag = false;
967                 setCursor(Qt::ArrowCursor);
968         }
969 }
970
971
972 #if 0
973 /////////////////////////////////////////////////////////////////////////////
974 // CMapView printing
975 //--------------------------------------------------------------------
976 // Record which page we're printing, if any:
977 //
978 // Output Variables:
979 //   printingPage
980
981 void MapView::OnPrepareDC(QPainter * dc, CPrintInfo * pInfo)
982 {
983         CScrollZoomView::OnPrepareDC(dc, pInfo);
984
985         if (pInfo)
986                 printingPage = pInfo->m_nCurPage;
987         else
988                 printingPage = 0;
989 }
990
991
992 bool MapView::OnPreparePrinting(CPrintInfo * pInfo)
993 {
994         // Require registration before printing map with more than 10 rooms:
995         MapDoc * const doc = GetDocument();
996         ASSERT_VALID(doc);
997
998         if (doc->needRepaginate())
999         {
1000                 CRepaginateDlg  d;
1001
1002                 switch (d.DoModal())
1003                 {
1004                 case IDCANCEL:
1005                         return false;
1006                 case IDYES:
1007                         doc->layoutPages();
1008                         break;
1009                 }
1010         }
1011
1012         pInfo->SetMaxPage(doc->page.size());
1013         return DoPreparePrinting(pInfo);
1014 }
1015
1016
1017 void MapView::OnBeginPrinting(QPainter * /*pDC*/, CPrintInfo * /*pInfo*/)
1018 {
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.
1022         clearSelection();
1023         selectDone();
1024         showCorners = false;
1025 }
1026
1027
1028 void MapView::OnEndPrinting(QPainter* /*pDC*/, CPrintInfo* /*pInfo*/)
1029 {
1030         if (GetDocument()->locked)
1031                 OnUpdate(NULL, dupNavigationMode, NULL); // Select a room
1032 }
1033 #endif
1034
1035
1036 //
1037 // CMapView miscellaneous
1038 //
1039 // Add a room:
1040 //
1041 // Input:
1042 //   point:  The point where the mouse was clicked (in logical coordinates)
1043 //
1044 void MapView::addRoom(QPoint & point)
1045 {
1046         QSize docSize(doc->docSize);
1047
1048         if (doc->locked || (point.x() > docSize.width()) || (point.y() > docSize.height()))
1049         {
1050 //              MessageBeep(MB_ICONASTERISK);
1051                 return;
1052         }
1053
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;
1058
1059         if (point.x() < 0)
1060                 point.setX(0);
1061         else if (point.x() + roomWidth > docSize.width())
1062                 point.setX(docSize.width() - roomWidth);
1063
1064         if (point.y() < 0)
1065                 point.setY(0);
1066         else if (point.y() + roomHeight > docSize.height())
1067                 point.setY(docSize.height() - roomHeight);
1068
1069         clearSelection();
1070         const bool wasModified = doc->isDirty;
1071         const int rNum = doc->addRoom(point);
1072
1073         if (rNum >= 0)
1074                 selectRoom(rNum);
1075
1076         selectDone();               // FIXME disable floating comments
1077         editProperties(epuAddRoom, wasModified);
1078 }
1079
1080
1081 //
1082 // Make sure we aren't dragging a room off the edge:
1083 //
1084 // Input:
1085 //   p:  The proposed offset for the selected rooms
1086 //
1087 // Input Variables:
1088 //   rTmp:  Rectangle enclosing the original positions of the selected rooms
1089 //
1090 // Output:
1091 //   p:  The corrected (if necessary) offset for selected rooms
1092 //
1093 void MapView::adjustOffset(QPoint & p) const
1094 {
1095         const QSize size(doc->docSize);
1096
1097         if (p.x() + rTmp.left() < 0)
1098                 p.rx() = -rTmp.left();
1099
1100         if (p.x() + rTmp.right() > size.width())
1101                 p.rx() = size.width() - rTmp.right();
1102
1103         if (p.y() + rTmp.top() < 0)
1104                 p.ry() = -rTmp.top();
1105
1106         if (p.y() + rTmp.bottom() > size.height())
1107                 p.ry() = size.height() - rTmp.bottom();
1108 }
1109
1110
1111 //
1112 // Deselect all rooms:
1113 //
1114 // Input:
1115 //   update:  TRUE means the selected rooms should be redrawn
1116 //
1117 void MapView::clearSelection(bool update/* = true*/)
1118 {
1119         if (doc->room.size() != selected.size())
1120                 selected.resize(doc->room.size(), 0);
1121
1122         if (update && numSelected)
1123         {
1124 #if 0
1125                 QRect r;
1126                 CClientDC dc(this);
1127                 OnPrepareDC(&dc);
1128                 RoomConstItr room = doc->room.getVector().begin();
1129
1130                 for(int i=doc->room.size()-1; numSelected && i>=0; --i)
1131                 {
1132                         if (selected[i])
1133                         {
1134                                 numSelected--;
1135                                 room[i]->getRect(r);
1136                                 dc.LPtoDP(r);
1137                                 r.NormalizeRect();
1138                                 InvalidateRect(r);
1139                         }
1140                 }
1141 #endif
1142         }
1143
1144         numSelected = 0;
1145
1146         for(ByteItr b=selected.begin(); b!=selected.end(); ++b)
1147                 *b = 0;
1148
1149         deselectPages(update);
1150 }
1151
1152
1153 //
1154 // Compute a rectangle enclosing all selected rooms:
1155 //
1156 // Input Variables:
1157 //   numSelected
1158 //   numSelectedPages
1159 //   selected
1160 //   selectedPage
1161 //
1162 // Output:
1163 //   r:  A rectangle enclosing all selected rooms
1164 //
1165 void MapView::computeSelectedRect(QRect & r) const
1166 {
1167 //      r.SetRectEmpty();
1168         r = QRect();
1169
1170         if (numSelected)
1171         {
1172                 RoomConstItr room = doc->room.getVector().begin();
1173
1174                 for(int i=doc->room.size()-1; i>=0; i--)
1175                 {
1176                         if (selected[i])
1177                                 r |= room[i]->getRect();
1178                 }
1179         }
1180
1181         if (numSelectedPages)
1182         {
1183                 for(int i=doc->page.size()-1; i>=0; i--)
1184                 {
1185                         if (selectedPage[i])
1186                                 r |= doc->getPageRect(i);
1187                 }
1188         }
1189 }
1190
1191
1192 //
1193 // Deselect a page:
1194 //
1195 // Input:
1196 //   n:       The page number to be deselected
1197 //   update:  TRUE means the page should be redrawn if necessary
1198 //
1199 // Output Variables:
1200 //   numSelectedPages
1201 //   selectedOne
1202 //   selectedPage
1203 //
1204 void MapView::deselectPage(int n, bool update/* = true*/)
1205 {
1206         ASSERT((n >= 0) && (n < doc->page.size()));
1207
1208         if (selectedPage[n])
1209         {
1210                 selectedPage[n] = false;
1211
1212                 if ((--numSelectedPages == 0) && (numSelected == 1))
1213                 {
1214                         for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1215                                 ; // Find the selected room
1216                 }
1217                 else
1218                         selectedOne = -1;
1219
1220 #if 0
1221                 if (update)
1222                 {
1223                         CClientDC dc(this);
1224                         OnPrepareDC(&dc);
1225                         QRect r;
1226                         doc->getPageRect(n, r);
1227                         dc.LPtoDP(r);
1228                         r.NormalizeRect();
1229                         InvalidateRect(r);
1230                 }
1231 #endif
1232         }
1233 }
1234
1235
1236 //
1237 // Deselect all pages:
1238 //
1239 // Input:
1240 //   update:  TRUE means the selected pages should be redrawn
1241 //
1242 // Output Variables:
1243 //   numSelectedPages
1244 //   selectedOne
1245 //   selectedPage
1246 //
1247 void MapView::deselectPages(bool update/* = true*/)
1248 {
1249         ASSERT(selectedPage.size() == doc->page.size());
1250
1251         if (update && numSelectedPages)
1252         {
1253                 QRect r;
1254 //              CClientDC dc(this);
1255 //              OnPrepareDC(&dc);
1256
1257                 for(int i=doc->page.size()-1; numSelectedPages&&i>=0; i--)
1258                 {
1259                         if (selectedPage[i])
1260                         {
1261                                 numSelectedPages--;
1262                                 r = doc->getPageRect(i);
1263
1264                                 if (!showPages)
1265                                 {
1266                                         // make sure it disappears
1267                                         r.setBottom(r.bottom() + penPageWidth);
1268                                         r.setLeft(r.left() - penPageWidth);
1269                                 }
1270
1271 //                              dc.LPtoDP(r);
1272 //                              r.NormalizeRect();
1273 //                              InvalidateRect(r);
1274                         }
1275                 }
1276         }
1277
1278         numSelectedPages = 0;
1279
1280         for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
1281                 *b = 0;
1282
1283         if (numSelected == 1)
1284         {
1285                 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
1286                         ; // Find the selected room
1287         }
1288         else
1289                 selectedOne = -1;
1290 }
1291
1292
1293 //
1294 // Deselect a room:
1295 //
1296 // Input:
1297 //   n:       The room number to be deselected
1298 //   update:  TRUE means the room should be redrawn if necessary
1299 //
1300 // Output Variables:
1301 //   numSelected
1302 //   selectedOne
1303 //   selected
1304 //
1305 void MapView::deselectRoom(RoomNum n, bool update/* = true*/)
1306 {
1307         ASSERT((n >= 0) && (n < doc->room.size()));
1308
1309         if (doc->room.size() != selected.size())
1310                 selected.resize(doc->room.size(), 0);
1311
1312         if (selected[n])
1313         {
1314                 selected[n] = false;
1315
1316                 if ((--numSelected == 1) && !numSelectedPages)
1317                 {
1318                         for (selectedOne=0; !selected[selectedOne]; selectedOne++)
1319                                 ; // Find the selected room
1320                 }
1321                 else
1322                         selectedOne = -1;
1323
1324 #if 0
1325                 if (update)
1326                 {
1327                         CClientDC dc(this);
1328                         OnPrepareDC(&dc);
1329                         QRect r;
1330                         doc->room[n].getRect(r);
1331                         dc.LPtoDP(r);
1332                         r.NormalizeRect();
1333                         InvalidateRect(r);
1334                 }
1335 #endif
1336         }
1337 }
1338
1339
1340 //
1341 // Update the room comments dialog after the selection changes:
1342 //
1343 void MapView::selectDone()
1344 {
1345 //      static_cast<CMapApp *>(AfxGetApp())->setComment(this, (selectedOne >= 0 ? &(GetDocument()->getRoom(selectedOne)) : NULL));
1346 }
1347
1348
1349 //
1350 // Select a page:
1351 //
1352 // Input:
1353 //   n:       The page number to select
1354 //   update:  TRUE means the selected page should be redrawn if necessary
1355 //
1356 // Output Variables:
1357 //   numSelectedPages
1358 //   selectedOne
1359 //   selectedPage
1360 //
1361 void MapView::selectPage(int n, bool update/* = true*/)
1362 {
1363         if (!selectedPage[n])
1364         {
1365                 selectedPage[n] = true;
1366                 numSelectedPages++;
1367                 selectedOne = -1;
1368
1369 #if 0
1370                 if (update)
1371                 {
1372                         CClientDC dc(this);
1373                         OnPrepareDC(&dc);
1374                         QRect r;
1375                         doc->getPageRect(n, r);
1376                         dc.LPtoDP(r);
1377                         r.NormalizeRect();
1378                         InvalidateRect(r);
1379                 }
1380 #endif
1381         }
1382 }
1383
1384
1385 //
1386 // Select a room:
1387 //
1388 // Input:
1389 //   n:       The room number to select
1390 //   update:  TRUE means the selected room should be redrawn if necessary
1391 //
1392 // Output Variables:
1393 //   numSelected
1394 //   selectedOne
1395 //   selected
1396 //
1397 void MapView::selectRoom(RoomNum n, bool update/*= true*/)
1398 {
1399         if (doc->room.size() != selected.size())
1400                 selected.resize(doc->room.size(), 0);
1401
1402         if (!selected[n])
1403         {
1404                 selected[n] = true;
1405
1406                 if ((++numSelected == 1) && !numSelectedPages)
1407                         selectedOne = n;
1408                 else
1409                         selectedOne = -1;
1410
1411 #if 0
1412                 if (update)
1413                 {
1414                         CClientDC dc(this);
1415                         OnPrepareDC(&dc);
1416                         QRect r;
1417                         doc->room[n].getRect(r);
1418                         dc.LPtoDP(r);
1419                         r.NormalizeRect();
1420                         InvalidateRect(r);
1421                 }
1422 #endif
1423         }
1424 }
1425
1426
1427 void MapView::selectOnlyRoom(RoomNum n)
1428 {
1429         if (selectedOne != n)
1430         {
1431                 clearSelection();
1432                 selectRoom(n);
1433                 selectDone();
1434         }
1435 }
1436
1437
1438 //
1439 // Make sure that a room is visible:
1440 //
1441 // Input:
1442 //   n:  The number of the room to be made visible
1443 //
1444 void MapView::makeRoomVisible(RoomNum n)
1445 {
1446 #if 0
1447         QRect clientRect;
1448         GetClientRect(&clientRect);
1449         QSize viewSize(clientRect.right, clientRect.bottom);
1450
1451         CClientDC cdc(this);
1452         OnPrepareDC(&cdc);
1453         cdc.DPtoLP(&viewSize);
1454
1455         const QSize docSize(doc->getDocSize());
1456         const QPoint curPos(getScrollPosition());
1457         QRect roomRect;
1458         doc->room[n].getRect(roomRect);
1459
1460         QPoint newPos(curPos);
1461
1462         if (roomRect.left - curPos.x < gridX)
1463         {
1464                 newPos.x -= curPos.x - roomRect.left + gridX;
1465
1466                 if (newPos.x < 0)
1467                         newPos.x = 0;
1468         }
1469         else if (roomRect.right - curPos.x + gridX > viewSize.cx)
1470         {
1471                 newPos.x += roomRect.right + gridX - viewSize.cx - curPos.x;
1472
1473                 if (newPos.x + viewSize.cx > docSize.cx)
1474                         newPos.x = docSize.cx - viewSize.cx;
1475         }
1476
1477         if (curPos.y - roomRect.bottom < gridY)
1478         {
1479                 newPos.y += roomRect.bottom + gridY - curPos.y;
1480
1481                 if (newPos.y > 0)
1482                         newPos.y = 0;
1483         }
1484         else if (curPos.y - roomRect.top + gridY > viewSize.cy)
1485         {
1486                 newPos.y += roomRect.top + viewSize.cy - curPos.y - gridY;
1487
1488                 if (viewSize.cy - newPos.y > docSize.cy)
1489                         newPos.y = viewSize.cy - docSize.cy;
1490         }
1491
1492         if (newPos != curPos)
1493         {
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);
1497         }
1498
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);
1504 #endif
1505 }
1506
1507
1508 //
1509 // Paste a string to the clipboard:
1510 //
1511 // Input:
1512 //   text:  The string to paste
1513 //
1514 // Returns:
1515 //   True:   String was successfully pasted
1516 //   False:  Could not paste (clipboard unavailable or out of memory)
1517 //
1518 bool MapView::pasteClipboard(const string & text)
1519 {
1520 #if 0
1521         if (!OpenClipboard())
1522                 return false;
1523
1524         EmptyClipboard();
1525
1526         // Allocate a global memory object for the text
1527         HGLOBAL mem = GlobalAlloc(GMEM_DDESHARE, text.length() + sizeof(TCHAR));
1528
1529         if (mem == NULL)
1530         {
1531                 CloseClipboard();
1532                 return false;
1533         }
1534
1535         // Lock the handle and copy the text to the buffer
1536
1537         strcpy(LPTSTR(GlobalLock(mem)), text.c_str());
1538         GlobalUnlock(mem);
1539
1540         SetClipboardData(CFtrEXT, mem);  // Place the handle on the clipboard
1541         CloseClipboard();
1542 #endif
1543
1544         return true;
1545 }
1546
1547
1548 //
1549 // Update the selected room's comment from the floating dialog:
1550 //
1551 // Input:
1552 //   comment:  The new room comment (must not be NULL)
1553 //
1554 void MapView::setRoomNote(const char * comment)
1555 {
1556         ASSERT(selectedOne >= 0);
1557
1558         if (doc->room[selectedOne].note != comment)
1559         {
1560                 doc->setUndoData(new UndoRoomInfo(*doc, selectedOne));
1561                 doc->setRoomNote(selectedOne, comment);
1562         }
1563 }
1564
1565
1566 int MapView::FindHoveredEdge(void)
1567 {
1568         for(int i=0; i<doc->edge.size(); i++)
1569         {
1570                 QPoint ep1, ep2;
1571
1572                 doc->getEdgePoints(doc->edge[i], ep1, ep2);
1573
1574                 QPointF lineSegment = ep2 - ep1;
1575                 QPointF v1 = mouse - ep1;
1576                 QPointF v2 = mouse - ep2;
1577                 double t = ParameterOfLineAndPoint(ep1, ep2, mouse);
1578                 double distance;
1579
1580                 if (t < 0.0)
1581                         distance = Magnitude(v1);
1582                 else if (t > 1.0)
1583                         distance = Magnitude(v2);
1584                 else
1585                         // distance = ?Det?(ls, v1) / |ls|
1586                         distance = fabs(Determinant(lineSegment, v1)
1587                                 / Magnitude(lineSegment));
1588
1589                 if (distance < 20.0)
1590                         return i;
1591         }
1592
1593         // Didn't find a closely hovered edge
1594         return -1;
1595 }
1596
1597
1598 //
1599 // Set the step size of the scroll bars:
1600 //
1601 void MapView::setScrollBars()
1602 {
1603 #if 0
1604         QRect clientRect;
1605         GetClientRect(&clientRect);
1606         QSize viewSize(clientRect.right, clientRect.bottom);
1607
1608         CClientDC cdc(this);
1609         OnPrepareDC(&cdc);
1610         cdc.DPtoLP(&viewSize);
1611
1612         viewSize.cx -= gridX / 2;
1613         viewSize.cx -= viewSize.cx % gridX + roomWidth;
1614         viewSize.cx = max(viewSize.cx, gridX);
1615
1616         viewSize.cy -= gridY / 2;
1617         viewSize.cy -= viewSize.cy % gridY + roomHeight;
1618         viewSize.cy = max(viewSize.cy, gridY);
1619
1620         setPageStep(viewSize);
1621 #endif
1622 }
1623
1624
1625 //
1626 // Zoom the display:
1627 //
1628 // Input:
1629 //   newZoom:  The new zoom factor (in percent)
1630 //
1631 void MapView::zoomTo(short newZoom)
1632 {
1633         if (opInProgress == gmoAddCorner)
1634                 opInProgress = gmoNone;
1635
1636         if (newZoom == zoom)
1637                 return;
1638
1639         if (opInProgress || scrollDrag || (newZoom < minZoom) || (newZoom > maxZoom))
1640                 ;//MessageBeep(MB_OK);
1641         else
1642         {
1643 //              setDisplayZoom(zoom = newZoom);
1644
1645                 if (selectedOne >= 0)
1646                         makeRoomVisible(selectedOne);
1647
1648                 setScrollBars();
1649 //              GetParentFrame()->OnUpdateFrameTitle(TRUE);
1650         }
1651 }
1652
1653
1654 //
1655 // Navigation:
1656 //---------------------------------------------------------------------------
1657 // Keyboard navigation:
1658 //
1659 // Input:
1660 //   corner:       The direction to move
1661 //   toggleStubs:  TRUE means adding an existing stub should delete it
1662 //
1663 // Input Variables:
1664 //   edgeTmp:      If type1 or type2 is nonzero, use that for a new edge
1665 //   selectedOne:  Must be a valid room number
1666 //
1667 void MapView::navigate(RoomCorner corner, bool toggleStubs/* = true*/)
1668 {
1669         const short roomXoffset[] =
1670         {
1671                 0, 0, gridX + roomWidth, -(gridX + roomWidth),
1672                 gridX + roomWidth, -(gridX + roomWidth), gridX + roomWidth, -(gridX + roomWidth)
1673         }; // FIXME add NNE, etc.
1674
1675         const short roomYoffset[] =
1676         {
1677                 gridY + roomHeight, -(gridY + roomHeight), 0, 0,
1678                 gridY + roomHeight, gridY + roomHeight, -(gridY + roomHeight), -(gridY + roomHeight)
1679         }; // FIXME add NNE, etc.
1680
1681         MapEdge e;
1682
1683         ASSERT(selectedOne >= 0);
1684
1685         if (doc->room[selectedOne].isCorner())
1686         {
1687 //              MessageBeep(MB_ICONASTERISK);
1688                 return;
1689         }
1690
1691         // Look for an existing connection:
1692         int eNum = doc->findEdge(selectedOne, corner, e);
1693
1694         if ((eNum >= 0) && !(e.type2 & etUnexplored))
1695         {
1696                 // Found existing connection, and 2nd end is not explored
1697                 if (toggleStubs && (e.type2 & etNoExit))
1698                 {
1699                         if (edgeTmp.type1 & etNoExit)
1700                         {
1701                                 doc->setUndoData(new UndoDelete(doc->isDirty, doc->edge[eNum]));
1702                         }
1703                         else
1704                         {
1705                                 doc->setUndoData(new UndoChangeEdge(doc->isDirty, doc->edge[eNum]));
1706                                 edgeTmp.room1 = selectedOne;
1707                                 edgeTmp.end1 = corner;
1708                                 doc->addEdge(edgeTmp);
1709                         }
1710
1711                         doc->deleteEdge(eNum);
1712                         return;
1713                 }
1714
1715                 if (edgeTmp.type1 || edgeTmp.type2 || (e.type1 & etOneWay)
1716                         || (e.type2 & etNoRoom2))
1717                 {
1718                         // We were trying to add connection
1719 //                      MessageBeep(MB_ICONASTERISK);
1720                         return;
1721                 }
1722
1723                 if (e.end1 == rcCorner)
1724                 {
1725                         int r = doc->findOtherEnd(eNum);
1726
1727                         if (r >= 0)
1728                                 e.room1 = r;
1729                         else
1730                         {
1731 //                              MessageBeep(MB_ICONASTERISK);
1732                                 return;
1733                         }
1734                 } // end if this connection leads to a corner
1735
1736                 selectOnlyRoom(e.room1);
1737                 makeRoomVisible(e.room1);
1738                 eNum = -1;                  // Don't delete this edge
1739         }
1740         else // eNum < 0 || e.type2 & etUnexplored
1741         {
1742                 // Try to add a new connection
1743                 if (doc->locked || (corner >= rcNNE))
1744                 {
1745 //                      MessageBeep(MB_ICONASTERISK);
1746                         return;
1747                 }
1748
1749                 const bool wasModified = doc->isDirty;
1750                 bool added = false;
1751
1752                 // If adding stub where there's already a stub
1753                 if ((eNum >= 0) && (e.type2 & etUnexplored)
1754                         && (edgeTmp.type1 & etUnexplored))
1755                 {
1756                         if (toggleStubs)
1757                         {
1758                                 // Remove stub connection:
1759                                 doc->setUndoData(new UndoDelete(wasModified, doc->edge[eNum]));
1760                                 doc->deleteEdge(eNum);
1761                         }
1762 //                      else
1763 //                              MessageBeep(MB_ICONASTERISK);
1764
1765                         return;
1766                 }
1767
1768                 e.room1 = selectedOne;
1769                 e.end1  = corner;
1770
1771                 if (edgeTmp.type1 || edgeTmp.type2)
1772                 {
1773                         e.type1 = edgeTmp.type1;
1774                         e.type2 = edgeTmp.type2;
1775                 }
1776                 else
1777                         setEdgeType(e);
1778
1779                 EdgeVec deletedEdges;
1780
1781                 // If there's a room #2 connected to this corner of the room...
1782                 if (!(e.type1 & etNoRoom2))
1783                 {
1784                         QPoint pos(doc->room[selectedOne].pos);
1785                         pos.rx() += roomXoffset[corner];
1786                         pos.ry() += roomYoffset[corner];
1787
1788                         int room = doc->findRoom(pos);
1789
1790                         if (room < -1)
1791                         {
1792                                 // We're off the grid
1793 //                              MessageBeep(MB_ICONASTERISK);
1794                                 return;
1795                         }
1796                         else if (room < 0)
1797                         {
1798                                 // We need to add a new room
1799                                 room = doc->addRoom(pos);
1800                                 added = true;
1801                         }
1802                         else
1803                         {
1804                                 // Existing room, check for existing connection
1805                                 MapEdge oEdge;
1806                                 int oeNum = doc->findEdge(room, oppositeCorner[corner], oEdge);
1807
1808                                 if (oeNum >= 0)
1809                                 {       // There is an existing connection
1810                                         if (oEdge.type2 & etUnexplored)
1811                                         {
1812                                                 deletedEdges.push_back(doc->edge[oeNum]);
1813                                                 doc->deleteEdge(oeNum);
1814
1815                                                 if (eNum > oeNum)
1816                                                         eNum--; // The edge number might have changed
1817                                         }
1818                                         else
1819                                                 room = -1; // Room has a non-stub connection there
1820                                 }
1821                         }
1822
1823                         if (room < 0)
1824                         {
1825 //                              MessageBeep(MB_ICONASTERISK);
1826                                 return;
1827                         }
1828
1829                         selectOnlyRoom(room);
1830                         makeRoomVisible(room);
1831
1832                         e.room2 = room;
1833                         e.end2 = oppositeCorner[corner];
1834                 }
1835
1836                 if (eNum >= 0)
1837                 {
1838                         deletedEdges.push_back(doc->edge[eNum]);
1839                         doc->deleteEdge(eNum);
1840                 }
1841
1842                 doc->addEdge(e);
1843
1844 #if 0
1845                 if (added)
1846                 {
1847                         // Enter room name
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));
1852                         else
1853                                 doc->setUndoData(new UndoAdd(wasModified, selectedOne, 1));
1854                 }
1855                 else if (deletedEdges.size())
1856                 {
1857                         if (deletedEdges.size() == 1)
1858                                 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
1859                         else
1860                                 doc->setUndoData(new UndoChanges(wasModified, -1, 1, deletedEdges));
1861                 }
1862                 else
1863                         doc->setUndoData(new UndoAdd(wasModified));  // Undo new edge
1864 #endif
1865         }
1866 }
1867
1868
1869 //
1870 // Fill in the edge type from the navigation box:
1871 //
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.
1875 //
1876 // Can only create up-down and in-out passages.
1877 //
1878 // Output:
1879 //   e.type1 and e.type2
1880 //
1881 void MapView::setEdgeType(MapEdge & e)
1882 {
1883         e.type1 = e.type2 = etNormal;
1884
1885         RoomCorner corner1, corner2;
1886         char initial, separator;
1887         getNavText(initial, corner1, separator, corner2);
1888
1889         if (initial && initial != '%' && initial != '>')
1890         {
1891 //              MessageBeep(MB_ICONASTERISK);
1892                 return;
1893         }
1894
1895         if (separator)
1896         {
1897                 switch (corner1)
1898                 {
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;
1903                 }
1904
1905                 if (separator == '-')
1906                 {
1907                         switch (corner2)
1908                         {
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;
1913                         }
1914                 }
1915         }
1916         else // standard edge
1917         {
1918                 switch (corner1)
1919                 {
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;
1924                 }
1925         }
1926
1927         if (initial == '%')
1928                 e.type1 |= etObstructed;
1929
1930         if (initial == '>' || separator == '>')
1931                 e.type1 |= etOneWay;
1932
1933         if (initial == '?' || separator == '?')
1934         {
1935                 e.type1 &= ~etSpecial;
1936                 e.type1 |= etUnexplored;
1937                 e.room2 = 0;
1938                 e.end2 = rcSW;
1939         }
1940         else if (initial == '!' || separator == '!')
1941         {
1942                 e.type1 &= ~etSpecial;
1943                 e.type1 |= etNoExit;
1944                 e.room2 = 0;
1945                 e.end2 = rcSW;
1946         }
1947 }
1948
1949
1950 //
1951 // Start typing in the navigation bar:
1952 //
1953 // Stuffs the given char in the navigation bar and sets focus to there.
1954 //
1955 // Input:
1956 //   c:  The character to put in the navigation bar.
1957 //
1958 void MapView::setNavText(char c)
1959 {
1960 #if 0
1961         CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
1962
1963         string text(c);
1964         nb.SetDlgItemText(IDC_NAV_DIR, text);
1965         nb.SetFocus();
1966         CEdit * dir = static_cast<CEdit *>(nb.GetDlgItem(IDC_NAV_DIR));
1967         dir->SetSel(1, 1);
1968         dir->SetSel(-1, 2);
1969 #endif
1970 }
1971
1972
1973 #if 0
1974 void MapView::OnChar(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
1975 {
1976         if (((c >= 'A') && (c <= 'Z'))
1977                 || ((c >= 'a') && (c <= 'z'))
1978                 || (c == '-') || (c == '!') || (c == '?') || (c == '/')
1979                 || (c == '>') || (c == '.') || (c == '~') || (c == '`')
1980                 || (c == '%'))
1981                 setNavText(c);
1982 }
1983 #endif
1984
1985
1986 //
1987 // Parse the text in the navigation bar:
1988 //
1989 // Input:
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.
1993 //
1994 // Output:
1995 //   initial:
1996 //     The non-alphabetic character beginning the text (0 if none)
1997 //   dir1:
1998 //     The direction indicated by the first direction word
1999 //     rcNone if no direction words
2000 //   separator:
2001 //     The non-alphabetic character following the first direction word
2002 //     0 if none
2003 //     Must be either '-', '>', or '?'
2004 //   dir2:
2005 //     The direction indicated by the second direction word
2006 //     rcNone if no second direction
2007 //
2008 void MapView::getNavText(char & initial, RoomCorner & dir1, char & separator, RoomCorner & dir2, bool mustBeMove)
2009 {
2010 #if 0
2011         CDialogBar & nb = static_cast<CMainFrame *>(AfxGetMainWnd())->wndNavBar;
2012         string text;
2013
2014         nb.GetDlgItemText(IDC_NAV_DIR, text);
2015         text.MakeLower();
2016
2017         dir1 = dir2 = rcNone;
2018         initial = separator = '\0';
2019
2020         if (!text.IsEmpty() && (text[0] < 'a' || text[0] > 'z'))
2021         {
2022                 initial = text[0];
2023                 text = text.Mid(1);
2024         }
2025
2026         if (initial == '-')
2027         {
2028                 initial = '\0';
2029                 separator = '-';
2030         }
2031         else if (!text.IsEmpty() && (text[0] == '-' || text[0] == '>'))
2032         {
2033                 separator = text[0];
2034                 text = text.Mid(1);
2035         }
2036         else
2037         {
2038                 dir1 = parseDirection(text);
2039
2040                 if (!text.IsEmpty() && (text[0]=='-' || text[0]=='>' || text[0]=='?'))
2041                 {
2042                         separator = text[0];
2043                         text = text.Mid(1);
2044                 }
2045         }
2046
2047         if (separator)
2048                 dir2 = parseDirection(text);
2049
2050         if (mustBeMove && (dir1 == rcNone || separator == '-' || ((initial || separator) && (dir1 >= rcNumCorners))))
2051                 MessageBeep(MB_ICONASTERISK);
2052         else
2053         {
2054                 nb.SetDlgItemText(IDC_NAV_DIR, tr(""));
2055                 SetFocus();
2056         }
2057 #endif
2058 }
2059
2060
2061 #if 0
2062 //
2063 // Handle the Go button in the navigation bar:
2064 //
2065 void MapView::OnNavGo()
2066 {
2067         if (selectedOne < 0)
2068         {
2069                 MessageBeep(MB_ICONASTERISK);
2070                 return;
2071         }
2072
2073         RoomCorner corner, junk2;
2074         char initial, separator;
2075         getNavText(initial, corner, separator, junk2, true);
2076
2077         if (separator == '-' || junk2 != rcNone)
2078         {
2079                 MessageBeep(MB_ICONASTERISK);
2080                 return;
2081         }
2082
2083         if (corner != rcNone)
2084         {
2085                 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2086
2087                 if (initial == '%')
2088                         edgeTmp.type1 = etObstructed;
2089
2090                 if (initial == '>' || separator == '>')
2091                         edgeTmp.type1 |= etOneWay;
2092
2093                 if (initial == '?' || separator == '?')
2094                         edgeTmp.type1 = etUnexplored;
2095                 else if (initial == '!' || separator == '!')
2096                         edgeTmp.type1 = etNoExit;
2097
2098                 navigate(corner);
2099         }
2100 }
2101
2102
2103 //
2104 // Handle the direction keys on the numeric keypad:
2105 //
2106 // Input:
2107 //   cmd:  Which command was executed
2108 //
2109 void MapView::OnNavGoDir(unsigned int cmd)
2110 {
2111         ASSERT(cmd >= ID_NAV_GO_NORTH && cmd <= ID_NAV_STUB2_OUT);
2112
2113         if (selectedOne >= 0)
2114         {
2115                 SetFocus();
2116                 edgeTmp.type1 = edgeTmp.type2 = etNormal;
2117                 bool toggleStubs = false;
2118
2119                 if (cmd >= ID_NAV_STUB1_NORTH)
2120                 {
2121                         if (cmd >= ID_NAV_STUB2_NORTH)
2122                         {
2123                                 // Stub 2
2124                                 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etNoExit : etUnexplored);
2125                                 cmd -= ID_NAV_STUB2_NORTH - ID_NAV_GO_NORTH;
2126                         }
2127                         else
2128                         {
2129                                 // Stub 1
2130                                 edgeTmp.type1 = (gueApp()->stub1Unexplored() ? etUnexplored : etNoExit);
2131                                 cmd -= ID_NAV_STUB1_NORTH - ID_NAV_GO_NORTH;
2132                         }
2133
2134                         toggleStubs = true;
2135                 }
2136
2137                 navigate(RoomCorner(cmd - ID_NAV_GO_NORTH), toggleStubs);
2138         }
2139         else
2140                 MessageBeep(MB_ICONASTERISK);
2141 }
2142
2143
2144 //
2145 // CMapView message handlers
2146 //---------------------------------------------------------------------------
2147 // Update the comments dialog when the view is activated:
2148 //
2149 void MapView::OnActivateView(bool bActivate, CView * pActivateView, CView * pDeactiveView)
2150 {
2151         if (bActivate)
2152                 selectDone();  // Update comments dialog
2153
2154         CScrollZoomView::OnActivateView(bActivate, pActivateView, pDeactiveView);
2155 }
2156 #endif
2157
2158
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)
2162 {
2163 //printf("MapView::HandleAddCorner... (iTmp=%i, opInProgress=%i)\n", iTmp, opInProgress);
2164         ASSERT(opInProgress == gmoAddCorner);
2165         ASSERT(selectedOne >= 0);
2166
2167         int room = doc->addCorner(selectedOne, iTmp);
2168
2169         if (room >= 0)
2170                 selectOnlyRoom(room);
2171 //      else
2172 //              MessageBeep(MB_ICONASTERISK);
2173
2174         opInProgress = gmoNone;
2175         update();
2176 }
2177
2178
2179 void MapView::HandleAddUnexplored(void)
2180 {
2181         EdgeVec deletedEdges;
2182         const bool wasModified = doc->isDirty;
2183         MapEdge e;
2184
2185         edgeTmp.room1 = roomClicked;
2186         edgeTmp.end1 = cornerClicked;
2187         // This is from MapView::setEdgeType()
2188         edgeTmp.type1 &= ~etSpecial;
2189         edgeTmp.type1 |= etUnexplored;
2190         edgeTmp.room2 = 0;
2191         edgeTmp.end2 = rcSW;
2192
2193         // Look for an existing connection:
2194 //      int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2195         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2196
2197         if (eNum >= 0)
2198         {
2199                 deletedEdges.push_back(doc->edge[eNum]);
2200                 doc->deleteEdge(eNum);
2201         }
2202
2203         doc->addEdge(edgeTmp);
2204
2205         if (deletedEdges.size())
2206                 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2207         else
2208                 doc->setUndoData(new UndoAdd(wasModified));  // Undo new edge
2209
2210         opInProgress = gmoNone;
2211         update();
2212 }
2213
2214
2215 void MapView::HandleAddLoopBack(void)
2216 {
2217         EdgeVec deletedEdges;
2218         const bool wasModified = doc->isDirty;
2219         MapEdge e;
2220
2221         edgeTmp.room1 = roomClicked;
2222         edgeTmp.end1 = cornerClicked;
2223         // This is from MapView::setEdgeType()
2224         edgeTmp.type1 &= ~etSpecial;
2225         edgeTmp.type1 |= etLoopBack;
2226         edgeTmp.room2 = 0;
2227         edgeTmp.end2 = rcSW;
2228
2229         // Look for an existing connection:
2230 //      int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2231         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2232
2233         if (eNum >= 0)
2234         {
2235                 deletedEdges.push_back(doc->edge[eNum]);
2236                 doc->deleteEdge(eNum);
2237         }
2238
2239         doc->addEdge(edgeTmp);
2240
2241         if (deletedEdges.size())
2242                 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2243         else
2244                 doc->setUndoData(new UndoAdd(wasModified));  // Undo new edge
2245
2246         opInProgress = gmoNone;
2247         update();
2248 }
2249
2250
2251 void MapView::HandleAddNoExit(void)
2252 {
2253         EdgeVec deletedEdges;
2254         const bool wasModified = doc->isDirty;
2255         MapEdge e;
2256
2257         edgeTmp.room1 = roomClicked;
2258         edgeTmp.end1 = cornerClicked;
2259         // This is from MapView::setEdgeType()
2260         edgeTmp.type1 &= ~etSpecial;
2261         edgeTmp.type1 |= etNoExit;
2262         edgeTmp.room2 = 0;
2263         edgeTmp.end2 = rcSW;
2264
2265         // Look for an existing connection:
2266 //      int eNum = doc->findEdge(selectedOne, cornerClicked, e);
2267         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2268
2269         if (eNum >= 0)
2270         {
2271                 deletedEdges.push_back(doc->edge[eNum]);
2272                 doc->deleteEdge(eNum);
2273         }
2274
2275         doc->addEdge(edgeTmp);
2276
2277         if (deletedEdges.size())
2278                 doc->setUndoData(new UndoChangeEdge(wasModified, deletedEdges[0]));
2279         else
2280                 doc->setUndoData(new UndoAdd(wasModified));  // Undo new edge
2281
2282         opInProgress = gmoNone;
2283         update();
2284 }
2285
2286
2287 void MapView::SetEdgeDirection(EdgeType et)
2288 {
2289         MapEdge e;
2290
2291         // Look for an existing connection:
2292         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2293
2294         if (eNum >= 0)
2295         {
2296                 // Is the room we clicked on room1 of this edge?
2297                 if (doc->edge[eNum].room1 == roomClicked)
2298                 {
2299                         doc->edge[eNum].type1 &= ~etDirection;
2300                         doc->edge[eNum].type1 |= et;
2301                 }
2302                 else
2303                 {
2304                         doc->edge[eNum].type2 &= ~etDirection;
2305                         doc->edge[eNum].type2 |= et;
2306                 }
2307         }
2308
2309         opInProgress = gmoNone;
2310         update();
2311 }
2312
2313
2314 void MapView::HandleAddUp(void)
2315 {
2316         SetEdgeDirection(etUp);
2317 }
2318
2319
2320 void MapView::HandleAddDown(void)
2321 {
2322         SetEdgeDirection(etDown);
2323 }
2324
2325
2326 void MapView::HandleAddIn(void)
2327 {
2328         SetEdgeDirection(etIn);
2329 }
2330
2331
2332 void MapView::HandleAddOut(void)
2333 {
2334         SetEdgeDirection(etOut);
2335 }
2336
2337
2338 void MapView::SetEdges(int room, int edgeNum, EdgeType eType)
2339 {
2340 /*
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.
2342
2343 Algorithm:
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.
2347    if so, go to 1)
2348    else, we're done.
2349
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...
2352 Bleah.
2353
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.
2355 */
2356         // Go thru from current room to all connected "corner" rooms
2357         while (true)
2358         {
2359                 // Check for the unlikely case that we got passed bad info
2360                 if (doc->edge[edgeNum].HasRoom(room) == false)
2361                         break;
2362
2363                 if (doc->edge[edgeNum].room2 == room)
2364                         doc->edge[edgeNum].Swap();
2365
2366                 if (((eType == etOneWay) && (doc->edge[edgeNum].end2 != rcCorner))
2367                         || (eType != etOneWay))
2368                         doc->edge[edgeNum].type1 |= eType;
2369
2370                 if (doc->edge[edgeNum].end2 != rcCorner)
2371                         return;
2372
2373                 // Find next Edge...  :-/
2374                 room = doc->edge[edgeNum].room2;
2375
2376                 for(int i=0; i<doc->edge.size(); i++)
2377                 {
2378                         // Exclude the edge we just came from...
2379                         if (i == edgeNum)
2380                                 continue;
2381
2382                         if (doc->edge[i].HasRoom(room))
2383                         {
2384                                 edgeNum = i;
2385                                 break;
2386                         }
2387                 }
2388         }
2389 }
2390
2391
2392 void MapView::ClearEdges(int room, int edgeNum, EdgeType eType)
2393 {
2394         // Go thru from current room to all connected "corner" rooms
2395         while (true)
2396         {
2397                 // Check for the unlikely case that we got passed bad info
2398                 if (doc->edge[edgeNum].HasRoom(room) == false)
2399                         break;
2400
2401                 if (doc->edge[edgeNum].room2 == room)
2402                         doc->edge[edgeNum].Swap();
2403
2404                 doc->edge[edgeNum].type1 &= ~eType;
2405
2406                 if (doc->edge[edgeNum].end2 != rcCorner)
2407                         return;
2408
2409                 // Find next Edge...  :-/
2410                 room = doc->edge[edgeNum].room2;
2411
2412                 for(int i=0; i<doc->edge.size(); i++)
2413                 {
2414                         // Exclude the edge we just came from...
2415                         if (i == edgeNum)
2416                                 continue;
2417
2418                         if (doc->edge[i].HasRoom(room))
2419                         {
2420                                 edgeNum = i;
2421                                 break;
2422                         }
2423                 }
2424         }
2425 }
2426
2427
2428 void MapView::HandleAddOneWay(void)
2429 {
2430         MapEdge e;
2431
2432         // Look for an existing connection:
2433         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2434
2435         if (eNum >= 0)
2436                 SetEdges(roomClicked, eNum, etOneWay);
2437
2438         opInProgress = gmoNone;
2439         update();
2440 }
2441
2442
2443 void MapView::HandleClearOneWay(void)
2444 {
2445         MapEdge e;
2446
2447         // Look for an existing connection:
2448         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2449
2450         if (eNum >= 0)
2451                 ClearEdges(roomClicked, eNum, etOneWay);
2452
2453         opInProgress = gmoNone;
2454         update();
2455 }
2456
2457
2458 void MapView::HandleAddRestricted(void)
2459 {
2460         MapEdge e;
2461
2462         // Look for an existing connection:
2463         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2464
2465         if (eNum >= 0)
2466                 SetEdges(roomClicked, eNum, etObstructed);
2467
2468         opInProgress = gmoNone;
2469         update();
2470 }
2471
2472
2473 void MapView::HandleClearRestricted(void)
2474 {
2475         MapEdge e;
2476
2477         // Look for an existing connection:
2478         int eNum = doc->findEdge(roomClicked, cornerClicked, e);
2479
2480         if (eNum >= 0)
2481                 ClearEdges(roomClicked, eNum, etObstructed);
2482
2483         opInProgress = gmoNone;
2484         update();
2485 }
2486
2487
2488 //void MapView::OnEditAddRoom()
2489 void MapView::HandleAddRoom(void)
2490 {
2491         addRoom(scrollDragStart);
2492 }
2493
2494
2495 void MapView::HandleMapProperties(void)
2496 {
2497         MapDialog dlg;
2498
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);
2505
2506         if (dlg.exec() == false)
2507                 return;
2508
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();
2515
2516         if (dlg.title.text().isEmpty() == false)
2517                 setWindowTitle(dlg.title.text());
2518
2519         update();
2520 }
2521
2522
2523 //void MapView::OnEditClear()
2524 void MapView::HandleDelete(void)
2525 {
2526         deleteSelection();
2527         update();
2528 }
2529
2530
2531 #if 0
2532 //--------------------------------------------------------------------
2533 void MapView::OnEditAddPage()
2534 {
2535         MapDoc * const doc = GetDocument();
2536         ASSERT_VALID(doc);
2537
2538         const int p = doc->page.size();
2539
2540         if (p < maxPages)
2541         {
2542                 MapPage page;
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));
2547                 showPages = true;
2548                 doc->addPage(p, page);
2549                 clearSelection();
2550                 selectPage(p);
2551         }
2552 }
2553 #endif
2554
2555
2556 //
2557 // Input:
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.
2561 //
2562 void MapView::deleteSelection(bool removeCorner/* = true*/)
2563 {
2564         if (numSelected || numSelectedPages)
2565         {
2566                 if (doc->locked || (numSelectedPages == doc->page.size()))
2567                 {
2568 //                      MessageBeep(MB_ICONASTERISK);
2569                         return;
2570                 }
2571
2572                 if (removeCorner && (selectedOne >= 0)
2573                         && doc->room[selectedOne].isCorner())
2574                 {
2575                         // Replace this corner with a single edge:
2576                         doc->deleteCorner(selectedOne);
2577                         clearSelection(false);
2578                         selectDone();
2579                         return;
2580                 }
2581                 else
2582                 {
2583                         // Select all corners if one corner or one end is selected:
2584                         const EdgeVec & edge = doc->edge;
2585                         int wereSelected = 0;
2586
2587                         while (wereSelected != numSelected)
2588                         {
2589                                 wereSelected = numSelected;
2590
2591                                 for(EdgeConstItr e=edge.begin(); e!=edge.end(); ++e)
2592                                 {
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);
2597                                 }
2598                         }
2599                 }
2600
2601                 UndoDelete * undoRec = new UndoDelete(*doc, numSelected, selected, numSelectedPages, selectedPage);
2602                 opInProgress = gmoDeleting; // Don't clear selection after first room
2603                 int pos = numSelected;
2604
2605                 for(int i=doc->room.size()-1; i>=0; i--)
2606                 {
2607                         if (selected[i])
2608                                 undoRec->addRoom(--pos, i, doc->extractRoom(i));
2609                 }
2610
2611                 for(int i=doc->page.size()-1; i>=0; i--)
2612                 {
2613                         if (selectedPage[i])
2614                                 doc->deletePage(i);
2615                 }
2616
2617                 selectedPage.resize(doc->page.size(), 0);
2618                 opInProgress = gmoNone;
2619                 doc->setUndoData(undoRec);
2620
2621                 clearSelection(false);
2622                 selectDone();
2623         }
2624 }
2625
2626
2627 #if 0
2628 //--------------------------------------------------------------------
2629 // Cut, Copy & Paste:
2630
2631 void MapView::OnEditCopy()
2632 {
2633         if (numSelected || numSelectedPages)
2634         {
2635                 MapDoc * doc = GetDocument();
2636                 ASSERT_VALID(doc);
2637
2638                 // Deselect all corner rooms:
2639                 for(int i=doc->room.size()-1; i>=0; --i)
2640                 {
2641                         if (selected[i] && doc->room[i].isCorner())
2642                                 deselectRoom(i);
2643                 }
2644
2645                 if (!numSelected && !numSelectedPages)
2646                 {
2647                         selectDone();
2648                         MessageBeep(MB_ICONASTERISK);
2649                         return;
2650                 } // end if only corners were selected
2651
2652                 // Select corners between selected rooms:
2653                 const EdgeVec& edges = doc->edge;
2654
2655                 for (EdgeConstItr e = edges.begin(); e != edges.end(); ++e)
2656                 {
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]))
2661                         {
2662                                 RoomNumVec cornerRooms;
2663                                 cornerRooms.reserve(16); // Should be plenty
2664                                 int otherEnd = doc->findOtherEnd(e, &cornerRooms);
2665
2666                                 if ((otherEnd >= 0) && selected[otherEnd])
2667                                 {
2668                                         for(RNConstItr rn=cornerRooms.begin(); rn!=cornerRooms.end(); ++rn)
2669                                                 selectRoom(*rn);
2670                                 }
2671                         } // end if edge connects selected room to unselected corner
2672                 }
2673
2674                 selectDone();
2675
2676                 // Copy selected rooms and pages:
2677                 gueApp()->setClipboard(new RoomScrap(*doc, numSelected, selected, numSelectedPages, selectedPage));
2678         }
2679 }
2680
2681
2682 void MapView::OnEditCut()
2683 {
2684         OnEditCopy();
2685         deleteSelection();
2686 }
2687
2688
2689 void MapView::OnEditPaste()
2690 {
2691         const RoomScrap * scrap = static_cast<CMapApp *>(AfxGetApp())->getClipboard();
2692
2693         if (scrap)
2694         {
2695                 MapDoc * doc = GetDocument();
2696                 ASSERT_VALID(doc);
2697
2698                 int i = doc->room.size();
2699
2700                 doc->setUndoData(scrap->paste(*doc));
2701                 const int numRooms = doc->room.size();
2702
2703                 if (numRooms > i)
2704                         makeRoomVisible(i);
2705
2706                 clearSelection(true);
2707
2708                 while (i < numRooms)
2709                         selectRoom(i++, true);
2710
2711                 selectDone();
2712         }
2713 }
2714
2715
2716 void MapView::OnEditPaginate()
2717 {
2718         showPages = true;
2719         GetDocument()->layoutPages();
2720 }
2721 #endif
2722
2723
2724 //
2725 // Handle Edit Properties:
2726 //
2727 void MapView::HandleRoomProperties(void)
2728 {
2729 //      editProperties((cmd == ID_EDIT_MAP_PROPERTIES) ? epuMapInfo : epuRoomInfo);
2730         editProperties(epuRoomInfo);
2731 }
2732
2733
2734 //
2735 // Bring up the Properties dialog:
2736 //
2737 // Input:
2738 //   undoType:
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
2743 //   wasModified:
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
2751 //
2752 void MapView::editProperties(EditPropUndo undoType, bool wasModified/*= false*/, EdgeVec * edges/*= NULL*/)
2753 {
2754         bool forceMap = false;
2755
2756         if (undoType == epuMapInfo)
2757         {
2758                 undoType = epuRoomInfo;
2759                 forceMap = true;
2760         }
2761
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();
2767
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);
2776
2777 //      if (roomTab)
2778         {
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));
2789
2790 //              if (!forceMap)
2791 //                      dlg.SetActivePage(&room);
2792         }
2793
2794 //      if (dlg.DoModal() == IDOK)
2795         if (dlg.exec())
2796         {
2797                 if (roomTab)
2798                 {
2799                         bool changed = false;
2800
2801 //                      if (room.border != (0 != (undoRoom->getRoom().flags & rfBorder)))
2802                         if (dlg.border.isChecked() != (0 != (undoRoom->room.flags & rfBorder)))
2803                         {
2804 //                              doc->setRoomFlags(selectedOne, (room.border ? rfBorder : 0), rfBorder);
2805                                 doc->setRoomFlags(selectedOne, (dlg.border.isChecked() ? rfBorder : 0), rfBorder);
2806                                 changed = true;
2807                         }
2808
2809 //                      if (room.name != undoRoom->getRoom().name.c_str())
2810                         if (dlg.name.text().toUtf8().data() != undoRoom->room.name.c_str())
2811                         {
2812 //                              if (room.name.Find('\n') < 0)
2813 //                                      wrapRoomName(room.name);
2814
2815                                 doc->setRoomName(selectedOne, dlg.name.text().toUtf8().data());
2816                                 changed = true;
2817                         }
2818
2819 //                      room.note.TrimRight();
2820
2821                         if (dlg.comment.document()->toPlainText().toUtf8().data() != undoRoom->room.note.c_str())
2822                         {
2823 //                              doc->setRoomNote(selectedOne, room.note);
2824                                 doc->setRoomNote(selectedOne, dlg.comment.document()->toPlainText().toUtf8().data());
2825                                 changed = true;
2826                         }
2827
2828                         if (changed && (undoType == epuRoomInfo))
2829                         {
2830                                 doc->setUndoData(undoRoom);
2831                                 undoRoom = NULL; // undoRoom now belongs to the document
2832                         }
2833                 }
2834
2835                 if (undoType != epuRoomInfo)
2836                 {
2837                         ASSERT(selectedOne >= 0);
2838
2839                         if (edges)
2840                                 doc->setUndoData(new UndoChanges(wasModified, selectedOne, 1, *edges));
2841                         else
2842                                 doc->setUndoData(new UndoAdd(wasModified, selectedOne, (undoType == epuAddRoomEdge ? 1 : 0)));
2843                 }
2844
2845 //              doc->setName(map.name);
2846 //              doc->setNote(map.note);
2847
2848 //              if (doc->locked != bool(map.locked))
2849 //                      doc->OnNavigationMode();
2850
2851 /*              if ((showCorners != bool(map.corners))
2852                         || (showGrid != bool(map.grid))
2853                         || (showPages != bool(map.pages)))
2854                 {
2855                         showCorners = map.corners != 0;
2856                         showGrid    = map.grid    != 0;
2857                         showPages   = map.pages   != 0;
2858
2859                         if (!showPages)
2860                                 deselectPages(false);
2861
2862 //                      InvalidateRect(NULL);
2863                 }*/
2864         }
2865         else if (undoType != epuRoomInfo)
2866         {
2867                 // Cancel adding the room:
2868                 ASSERT(selectedOne >= 0);
2869                 const RoomNum roomNum = selectedOne;
2870                 clearSelection();
2871
2872                 if (undoType == epuAddRoomEdge)
2873                 {
2874                         const MapEdge & edge = doc->edge[doc->edge.size() - 1];
2875                         selectOnlyRoom(edge.room1 == roomNum ? edge.room2 : edge.room1);
2876                 }
2877
2878                 opInProgress = gmoDeleting; // Don't clear selection
2879                 doc->deleteRoom(roomNum);
2880                 opInProgress = gmoNone;
2881
2882                 if (edges)
2883                 {
2884                         doc->addEdges(edges->size());
2885
2886                         for(EdgeConstItr e=edges->begin(); e!=edges->end(); e++)
2887                                 doc->addEdge(*e);
2888                 }
2889
2890                 doc->isDirty = wasModified;
2891         }
2892
2893         selectDone();
2894         update();
2895         delete undoRoom;
2896 }
2897
2898
2899 #if 0
2900 //
2901 // Select all rooms:
2902 //
2903 void MapView::OnEditSelectAll()
2904 {
2905         for(int i=doc->room.size()-1; i>=0; i--)
2906                 selectRoom(i);
2907
2908         selectDone();
2909 }
2910
2911
2912 //---------------------------------------------------------------------------
2913 // Select connected rooms:
2914
2915 void MapView::OnEditSelectConnected()
2916 {
2917         const MapDoc * doc = GetDocument();
2918         ASSERT_VALID(doc);
2919
2920         const int numEdges = doc->getEdgeCount();
2921         const EdgeVec & edge = doc->edge;
2922
2923         int wasSelected = 0;
2924
2925         while (wasSelected != numSelected)
2926         {
2927                 wasSelected = numSelected;
2928
2929                 for(int i=0; i<numEdges; i++)
2930                 {
2931                         if ((edge[i].type1 & etNoRoom2) == 0)
2932                         {
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
2938                 }
2939         } // end while more rooms were selected on this pass
2940
2941         selectDone();
2942 }
2943
2944
2945 void MapView::OnInitialUpdate()
2946 {
2947         const MapDoc * doc = GetDocument();
2948         ASSERT_VALID(doc);
2949
2950         selectedPage.resize(doc->page.size(), 0);
2951         clearSelection(false);
2952
2953         // Set the document size:
2954         init(4 * gridX, doc->getDocSize(), QSize(gridX, gridY));
2955 //  CClientDC dc(this);
2956 //  OnPrepareDC(&dc);
2957         LOGFONT lf;
2958         memset(&lf, 0, sizeof(lf));
2959         lf.lfHeight = 62;
2960         strcpy(lf.lfFaceName, "Arial");
2961         font.CreateFontIndirect(&lf);
2962
2963         if (doc->locked)
2964                 OnUpdate(NULL, dupNavigationMode, NULL);
2965
2966         CScrollZoomView::OnInitialUpdate();
2967         setScrollBars();              // Now fix the scroll bars
2968 }
2969
2970
2971 void MapView::OnKeyDown(unsigned int c, unsigned int nRepCnt, unsigned int nFlags)
2972 {
2973         bool ctrl = (GetKeyState(VK_CONTROL) < 0);
2974
2975         switch (c)
2976         {
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;
2984         default:
2985                 if ((GetKeyState(VK_SHIFT) >= 0) && (c >= '0') && (c <= '9'))
2986                 {
2987                         if (!ctrl && (c < '2'))
2988                                 zoomTo(100);
2989                         else if (ctrl && (c == '0'))
2990                                 zoomTo(200);
2991                         else
2992                                 zoomTo(10 * (c - '0') + (ctrl ? 100 : 0));
2993                 } // end if number key (not shifted)
2994
2995                 break;
2996         }
2997 }
2998 #endif
2999
3000
3001 void MapView::mouseDoubleClickEvent(QMouseEvent * event)
3002 //void MapView::OnLButtonDblClk(unsigned int nFlags, QPoint point)
3003 {
3004         if (scrollDrag || scrollDragTimer)
3005                 return;
3006
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();
3012
3013         // Scroll support
3014         point -= offset;
3015
3016         opInProgress = gmoNone;
3017
3018 //      if (GetCapture() == this)
3019 //              ReleaseCapture();
3020
3021 //      CClientDC dc(this);
3022 //      OnPrepareDC(&dc);
3023 //      dc.DPtoLP(&point);
3024
3025         const int i = doc->roomHit(point);
3026 //printf("MapView::mouseDoubleClickEvent: roomHit=%i\n", i);
3027
3028         if ((i >= 0) && (doc->room[i].isCorner() == false))
3029         {
3030                 selectOnlyRoom(i);
3031                 editProperties(epuRoomInfo);
3032         }
3033         else if (i < 0)
3034                 addRoom(point);
3035 }
3036
3037
3038 /* N.B.: Handles RButton & MButton too */
3039 void MapView::mousePressEvent(QMouseEvent * event)
3040 //void MapView::OnLButtonDown(unsigned int nFlags, QPoint point)
3041 /*
3042 nFlags = MK_CONTROL, MK_SHIFT, MK_{LMR}BUTTON
3043 */
3044 {
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
3052 /*
3053 Qt::NoModifier, Qt::ShiftModifier, Qt::ControlModifier, Qt::AltModifier, etc.
3054 */
3055
3056         // Scrolling support (save this for now)...
3057         point -= offset;
3058         offsetScrollDragStart = offset;
3059         scrollDragStart = point + offset;
3060
3061         if (event->button() == Qt::LeftButton)
3062         {
3063                 if (scrollDrag || scrollDragTimer)
3064                         return;
3065
3066 //      CClientDC dc(this);
3067 //      OnPrepareDC(&dc);
3068 //      dc.DPtoLP(&point);
3069
3070                 RoomCorner corner;
3071                 int room = doc->roomHit(point, &corner);
3072
3073                 if (room < 0)
3074                 {
3075                         if (doc->locked)
3076                                 return; // Must have 1 room selected during navigation
3077
3078                         if (showPages && ((room = doc->pageBorderHit(point)) >= 0))
3079                         {
3080                                 iTmp  = room;
3081
3082 //                              if (nFlags & MK_CONTROL)
3083                                 if (nFlags & Qt::ControlModifier)
3084                                 {
3085                                         opInProgress = gmoControlDown;
3086                                         bTmp = selectedPage[room];
3087                                         b2Tmp = true;           // In page
3088                                 }
3089                                 else
3090                                 {
3091 //                                      if (nFlags & MK_SHIFT)
3092                                         if (nFlags & Qt::ShiftModifier)
3093                                                 opInProgress = gmoShiftDown;
3094                                         else
3095                                                 opInProgress = gmoNone;
3096
3097                                         clearSelection();
3098                                 }
3099
3100                                 selectPage(room);
3101                                 selectDone();
3102
3103                                 if (opInProgress)
3104                                 {
3105 //                                      SetCapture();
3106                                         p1Tmp.rx() = point.x() / gridX;
3107                                         p1Tmp.ry() = point.y() / gridY;
3108                                         p2Tmp = doc->page[room].pos;
3109                                 }
3110                         }
3111                         else
3112                         {
3113 //                              SetCapture();
3114                                 opInProgress = gmoSelectBox;
3115
3116 //                              if (nFlags & MK_CONTROL)
3117                                 if (nFlags & Qt::ControlModifier)
3118                                 {
3119                                         // If Ctrl pressed, don't clear selection:
3120                                         for(ByteItr b=selected.begin(); b!=selected.end(); b++)
3121                                         {
3122                                                 if (*b)
3123                                                         *b = 2; // Mark the rooms that should stay selected
3124                                         }
3125
3126                                         for(ByteItr b=selectedPage.begin(); b!=selectedPage.end(); b++)
3127                                         {
3128                                                 if (*b)
3129                                                         *b = 2; // Mark the pages that should stay selected
3130                                         }
3131                                 }
3132                                 else
3133                                 {
3134                                         clearSelection();
3135                                         selectDone();
3136                                 }
3137
3138                                 p1Tmp = point;
3139                                 p2Tmp = point;
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());
3143                         }
3144
3145                         update();
3146                 }
3147                 else if (doc->locked)
3148                 {
3149 #if 0
3150                         ASSERT(selectedOne >= 0);
3151
3152                         if ((room != selectedOne) && !doc->room[room].isCorner())
3153                         {
3154                                 String path;
3155                                 doc->shortestPath(path, selectedOne, room);
3156
3157                                 if (path.empty())
3158                                         path = tr("Sorry, I don't know how to get there.");
3159                                 else if (path.find('.') != String::npos)
3160                                 {
3161                                         const CMapApp * app = static_cast<CMapApp *>(AfxGetApp());
3162
3163                                         if (app->navigationCopy())
3164                                         {
3165                                                 if (app->navigationCRLF())
3166                                                         path += tr("\r\n");
3167
3168                                                 pasteClipboard(path);
3169                                         }
3170                                 }
3171                                 // end else if path has multiple steps
3172
3173                                 static_cast<CMainFrame *>(AfxGetMainWnd())->setStatusBar(path.c_str());
3174                                 selectOnlyRoom(room);
3175                         }
3176                         // end if moving to new room in navigation mode
3177 #endif
3178                 }
3179 //              else if (nFlags & MK_CONTROL)
3180                 else if (nFlags & Qt::ControlModifier)
3181                 {
3182 //                      SetCapture();
3183                         opInProgress = gmoControlDown;
3184                         ASSERT(room < selected.size());
3185                         bTmp = selected[room];
3186                         b2Tmp = false;              // In room
3187                         iTmp = room;
3188                         selectRoom(room);
3189                         selectDone();
3190                         p1Tmp.rx() = point.x() / gridX;
3191                         p1Tmp.ry() = point.y() / gridY;
3192                         update();
3193                 }
3194 //              else if (nFlags & MK_SHIFT)
3195                 else if (nFlags & Qt::ShiftModifier)
3196                 {
3197 //                      SetCapture();
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;
3204                 }
3205                 else
3206                 {
3207                         selectOnlyRoom(room);
3208
3209                         if (!(doc->room[room].isCorner()))
3210                                 emit(RoomClicked(doc, room));
3211
3212                         if (doc->room[room].isCorner())
3213                         {
3214 //                              SetCapture();
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;
3219                         }
3220                         else if (corner != rcNone)
3221                         {
3222 //                              SetCapture();
3223                                 opInProgress = gmoAddEdge;
3224                                 bTmp = doc->isDirty;
3225                                 iTmp = doc->findEdge(room, corner, edgeTmp);
3226
3227                                 if (iTmp >= 0)
3228                                 {
3229                                         opInProgress = gmoChangeEdge;
3230                                         rTmp.setCoords(point.x() - 29, point.y() - 29, point.x() + 29, point.y() + 29);
3231 //                                      rTmp.InflateRect(29, 29);
3232                                 }
3233
3234                                 if ((iTmp < 0) || (edgeTmp.type2 & etNoRoom2))
3235                                 {
3236                                         edgeTmp.room1 = room;
3237                                         edgeTmp.end1 = corner;
3238                                         setEdgeType(edgeTmp);
3239                                 }
3240
3241                                 edgeTmp.room2 = 0;
3242                                 edgeTmp.end2 = rcSW;
3243                                 doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3244
3245                                 p2Tmp = p1Tmp;
3246 //                              OnPaint();                // Update the view immediately
3247
3248 #if 0
3249                                 if (opInProgress == gmoAddEdge)
3250                                 {
3251                                         dc.SetROP2(R2_NOT);
3252                                         QPen penEdge;
3253
3254                                         if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3255                                                 return;
3256
3257                                         QPen * oldPen = dc.SelectObject(&penEdge);
3258                                         dc.MoveTo(p1Tmp);
3259                                         dc.LineTo(p2Tmp);
3260                                         dc.SelectObject(oldPen);
3261                                 }
3262 #endif
3263                         }
3264
3265                         update();
3266                 }
3267         }
3268         else if (event->button() == Qt::RightButton)
3269         {
3270 //void MapView::OnRButtonDown(unsigned int nFlags, QPoint point)
3271 //{
3272 /*              if (opInProgress == gmoAddCorner)
3273                         opInProgress = gmoNone;
3274
3275 //              CClientDC dc(this);
3276 //              OnPrepareDC(&dc);
3277 //              dc.DPtoLP(&point);
3278
3279                 scrollDragStart = point;
3280
3281                 if (opInProgress)
3282                 {
3283 //                      SetCapture();
3284                         scrollDrag = true;
3285 //                      ::SetCursor(handCursor);
3286                 }//*/
3287 //              else
3288 //                      scrollDragTimer = SetTimer(menuTimer, clickTicks, NULL);
3289 //}
3290         }
3291 }
3292
3293 #if 0
3294 // From scrollzoom
3295 bool MapView::calcScrollPosition(QPoint & pt) const
3296 {
3297         if (pt.x() < 0)
3298                 pt.rx() = 0;
3299
3300 //      if (pt.y() > 0)
3301         if (pt.y() < 0)
3302                 pt.ry() = 0;
3303
3304 //      SCROLLINFO si;
3305 //      VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_HORZ, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3306
3307 //      if (pt.x + si.nPage > si.nMax + scrollStep.cx)
3308 //              pt.x = si.nMax - si.nPage + scrollStep.cx;
3309
3310 //      VERIFY(const_cast<CScrollZoomView *>(this)->GetScrollInfo(SB_VERT, &si, SIF_PAGE | SIF_POS | SIF_RANGE));
3311
3312 //      if (-pt.y + si.nPage > si.nMax + scrollStep.cy)
3313 //              pt.y = si.nPage - si.nMax - scrollStep.cy;
3314
3315 //      pt.x -= pt.x % scrollStep.cx;
3316 //      pt.y -= pt.y % scrollStep.cy;
3317
3318         return offset != pt;
3319 }
3320
3321
3322 // From scrollzoom
3323 void MapView::scrollToPosition(const QPoint & pt)
3324 {
3325         QPoint newOffset(pt);
3326
3327         if (!calcScrollPosition(newOffset))
3328                 return; // Didn't scroll
3329
3330 //      QSize delta(newOffset - offset);
3331         offset = newOffset;
3332
3333 //      CClientDC  dc(this);
3334 //      OnPrepareDC(&dc);
3335 //      dc.LPtoDP(&delta);
3336
3337 //      SetScrollPos(SB_VERT, -offset.y);
3338 //      SetScrollPos(SB_HORZ, offset.x);
3339 //      ScrollWindow(-delta.cx, delta.cy);
3340 }
3341 #endif
3342
3343 void MapView::mouseMoveEvent(QMouseEvent * event)
3344 //void MapView::OnMouseMove(unsigned int nFlags, QPoint point)
3345 {
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();
3352
3353         // Scroll support
3354         point -= offset;
3355
3356         // Location where the mouse is currently pointing
3357         mouse = point;
3358
3359         // Bail if we're just mousing around...
3360         if (mouseDown == false)
3361         {
3362                 hoveredEdge = FindHoveredEdge();
3363
3364                 update();
3365                 return;
3366         }
3367
3368 /*      if (scrollDragTimer)
3369         {
3370 //              KillTimer(scrollDragTimer);
3371                 scrollDragTimer = 0;
3372 //              SetCapture();
3373                 scrollDrag = true;
3374 //              ::SetCursor(handCursor);
3375         }//*/
3376
3377 //      if (GetCapture() != this)
3378 //              return;
3379
3380 //      CClientDC dc(this);
3381 //      OnPrepareDC(&dc);
3382 //      dc.DPtoLP(&point);
3383
3384         if (scrollDrag)
3385         {
3386 #if 0
3387 //              MSG msg;
3388                 QPoint p(offset);
3389 //              p.Offset(scrollDragStart - point);
3390                 p += (scrollDragStart - point);
3391
3392                 if (!calcScrollPosition(p)
3393 )//                     || PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3394                         return;
3395
3396                 if ((opInProgress == gmoAddEdge) || (opInProgress == gmoSelectBox)
3397                         || (opInProgress >= gmoDrag))
3398                 {
3399 #if 0
3400                         // We must handle these specially to avoid display artifacts:
3401                         dc.SetROP2(R2_NOT);
3402                         CGdiObject * old = NULL;
3403                         QPen pen;
3404                         QBrush brush;
3405
3406                         switch (opInProgress)
3407                         {
3408                         case gmoAddEdge:
3409                                 if (!pen.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0 ,0)))
3410                                         return;
3411
3412                                 old = dc.SelectObject(&pen);
3413                                 dc.MoveTo(p1Tmp);
3414                                 dc.LineTo(p2Tmp);
3415                                 break;
3416
3417                         case gmoSelectBox:
3418                                 old = dc.SelectStockObject(HOLLOW_BRUSH);
3419                                 dc.Rectangle(rTmp);
3420                                 break;
3421
3422                         default:
3423                                 drawSelected(&dc);
3424                                 break; // Must be gmoDrag
3425                         }
3426 #endif
3427
3428                         scrollToPosition(p);
3429 //                      OnPaint();
3430 //                      OnPrepareDC(&dc);         // Update with new offset
3431
3432 #if 0
3433                         switch (opInProgress)
3434                         {
3435                         case gmoAddEdge:    dc.MoveTo(p1Tmp);  dc.LineTo(p2Tmp);  break;
3436                         case gmoSelectBox:  dc.Rectangle(rTmp);                   break;
3437                         default: /* drag*/  drawSelected(&dc);                    break;
3438                         }
3439
3440                         if (old)
3441                                 dc.SelectObject(old);
3442 #endif
3443                 }
3444                 else
3445                         scrollToPosition(p);
3446 #endif
3447                 QPoint delta = (point + offset) - scrollDragStart;
3448                 offset = offsetScrollDragStart + delta;
3449                 update();
3450
3451                 return;
3452         }
3453
3454         if (opInProgress == gmoChangeEdge)
3455         {
3456 //              if (rTmp.PtInRect(point))
3457                 if (rTmp.contains(point))
3458                         return;
3459
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
3465         }
3466
3467         if (opInProgress == gmoAddEdge)
3468         {
3469 //              dc.SetROP2(R2_NOT);
3470 //              QPen penEdge;
3471
3472 //              if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0))) // PORT
3473 //                      return;
3474
3475 //              QPen * oldPen = dc.SelectObject(&penEdge);
3476 //              dc.MoveTo(p1Tmp);
3477 //              dc.LineTo(p2Tmp);
3478
3479                 RoomCorner corner;
3480                 int room = doc->roomHit(point, &corner);
3481
3482                 if (corner != rcNone && room != edgeTmp.room1)
3483                 {
3484                         edgeTmp.room2 = room;
3485                         edgeTmp.end2  = corner;
3486                         doc->getEdgePoints(edgeTmp, p1Tmp, p2Tmp);
3487                 }
3488                 else
3489                 {
3490                         p2Tmp = point;
3491                 }
3492
3493 //              dc.MoveTo(p1Tmp);
3494 //              dc.LineTo(p2Tmp);
3495 //              dc.SelectObject(oldPen);
3496         }
3497         else if (opInProgress == gmoSelectBox)
3498         {
3499                 if (p2Tmp != point)
3500                 {
3501                         p2Tmp = point;
3502 //                      dc.SetROP2(R2_NOT);
3503 //                      QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3504 //                      dc.Rectangle(rTmp);
3505
3506                         rTmp = QRect(p1Tmp, point).normalized();
3507 //                      rTmp.NormalizeRect();
3508                         RoomConstItr room = doc->room.getVector().begin();
3509 //                      QRect intersect, r;
3510
3511                         for(int i=doc->room.size()-1; i>=0; i--)
3512                         {
3513                                 if (selected[i] == 2)
3514                                         continue; // Room was already selected before selection box
3515
3516 #if 0
3517                                 room[i]->getRect(r);
3518 //                              intersect.IntersectRect(rTmp, r);
3519                                 intersect = rTmp & r;
3520 #else
3521                                 QRect r = room[i]->getRect();
3522                                 QRect intersect = rTmp & r;
3523 #endif
3524
3525                                 if ((selected[i] != 0) != bool(r == intersect))
3526                                 {
3527                                         selected[i] = !selected[i];
3528                                         numSelected += (selected[i] ? 1 : -1);
3529 //                                      r.DeflateRect(5, 5);
3530 //                                      dc.LPtoDP(&r);
3531 //                                      r.NormalizeRect();
3532 //                                      InvalidateRect(&r);
3533                                 }
3534                         }
3535
3536                         if (showPages)
3537                         {
3538                                 for(int i=doc->page.size()-1; i>=0; i--)
3539                                 {
3540                                         if (selectedPage[i] == 2)
3541                                                 continue; // Page was already selected before selection box
3542
3543 #if 0
3544                                         doc->getPageRect(i, r);
3545 //                                      intersect.IntersectRect(rTmp, r);
3546                                         intersect = rTmp & r;
3547 #else
3548                                         QRect r = doc->getPageRect(i);
3549                                         QRect intersect = rTmp & r;
3550 #endif
3551
3552                                         if ((selectedPage[i] != 0) != bool(r == intersect))
3553                                         {
3554                                                 selectedPage[i]  = !selectedPage[i];
3555                                                 numSelectedPages += (selectedPage[i] ? 1 : -1);
3556 //                                              r.DeflateRect(5, 5);
3557 //                                              dc.LPtoDP(&r);
3558 //                                              r.NormalizeRect();
3559 //                                              InvalidateRect(&r);
3560                                         }
3561                                 }
3562                         }
3563
3564                         if ((numSelected == 1) && !numSelectedPages)
3565                         {
3566                                 for(selectedOne=0; !selected[selectedOne]; selectedOne++)
3567                                         ;                     // Find the selected room
3568                         }
3569                         else
3570                                 selectedOne = -1;
3571
3572                         selectDone();
3573
3574 //                      if (GetUpdateRect(NULL))
3575 //                              OnPaint();
3576
3577 //                      dc.Rectangle(rTmp);
3578 //                      dc.SelectObject(oldBrush);
3579                 }
3580         }
3581         else if (opInProgress == gmoControlDown)
3582         {
3583                 p2Tmp.rx() = gridX * (point.x() / gridX - p1Tmp.x());
3584                 p2Tmp.ry() = gridY * (point.y() / gridY - p1Tmp.y());
3585
3586                 if (p2Tmp.x() || p2Tmp.y())
3587                 {
3588                         opInProgress = gmoControlDrag;
3589                         computeSelectedRect(rTmp);
3590 //                      drawBackground(&dc);
3591 //                      dc.SetROP2(R2_NOT);
3592 //                      drawSelected(&dc);
3593                 }
3594         }
3595         else if (opInProgress == gmoShiftDown)
3596         {
3597                 long ox = point.x() / gridX;
3598                 long oy = point.y() / gridY;
3599
3600                 if (ox < p1Tmp.x())
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;
3608                 else
3609                         return;
3610
3611                 // if dragging a page
3612                 if (iTmp >= 0)
3613                 {
3614                         ASSERT(!numSelected && (numSelectedPages == 1));
3615                         rTmp = doc->getPageRect(iTmp);
3616
3617                         switch (opInProgress)
3618                         {
3619                         case gmoShiftDragUp:
3620                                 p2Tmp.ry() = rTmp.top();
3621                                 break;
3622                         case gmoShiftDragLeft:
3623                                 p2Tmp.rx() = rTmp.right() - roomWidth + gridX;
3624                                 break;
3625                         }
3626                 }
3627
3628                 RoomConstItr room = doc->room.getVector().begin();
3629                 const int roomCount = doc->room.size();
3630
3631                 for(int i=0; i<roomCount; i++)
3632                 {
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);
3638
3639                         if (iTmp < 0)
3640                         {
3641                                 // shift-dragging a room
3642                                 if (opInProgress == gmoShiftDragLeft)
3643                                         p2Tmp.rx() += roomWidth;
3644                                 else if (opInProgress == gmoShiftDragDown)
3645                                         p2Tmp.ry() += roomHeight;
3646                         }
3647                         else
3648                         {
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;
3658                         }
3659
3660                         for(i=doc->page.size()-1; i>=0; --i)
3661                         {
3662                                 rTmp = doc->getPageRect(i);
3663
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);
3669                         }
3670                 }
3671
3672                 selectDone();
3673
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);
3680         }
3681         // end else if gmoShiftDown
3682         else if (opInProgress >= gmoDrag)
3683         {
3684 //              MSG msg;
3685
3686 //              if (PeekMessage(&msg, m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
3687 //                      return; // The mouse has already been moved again
3688
3689                 QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3690
3691                 if (p != p2Tmp)
3692                 {
3693                         adjustOffset(p);
3694
3695                         if (p == p2Tmp)
3696                                 return;
3697
3698 //                      dc.SetROP2(R2_NOT);
3699 //                      drawSelected(&dc);
3700                         p2Tmp = p;
3701 //                      drawSelected(&dc);
3702                 }
3703         }
3704
3705         // Maybe have more discriminant updating...  Maybe.
3706         update();
3707 }
3708
3709
3710 //void MapView::OnLButtonUp(unsigned int nFlags, QPoint point)
3711 void MapView::mouseReleaseEvent(QMouseEvent * event)
3712 {
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();
3719         mouseDown = false;
3720
3721         // Scroll support
3722         point -= offset;
3723
3724         if (event->button() == Qt::LeftButton)
3725         {
3726         //      if (GetCapture() != this)
3727         //      {
3728         //              if (selectedOne >= 0)
3729         //                      makeRoomVisible(selectedOne);
3730         //
3731         //              return;
3732         //      }
3733
3734         //      if (!scrollDrag)
3735         //              ReleaseCapture();
3736
3737         //      CClientDC dc(this);
3738         //      OnPrepareDC(&dc);
3739         //      dc.DPtoLP(&point);
3740
3741                 if (opInProgress == gmoAddEdge)
3742                 {
3743                         // Erase the rubber band:
3744         //              dc.SetROP2(R2_NOT);
3745         //              QPen penEdge;
3746
3747         //              if (!penEdge.CreatePen(PS_SOLID, penEdgeWidth, RGB(0, 0, 0)))
3748         //                      return;
3749
3750         //              QPen * oldPen = dc.SelectObject(&penEdge);
3751         //              dc.MoveTo(p1Tmp);
3752         //              dc.LineTo(p2Tmp);
3753         //              dc.SelectObject(oldPen);
3754
3755                         // Find out where the edge goes:
3756                         RoomCorner corner;
3757                         int room = doc->roomHit(point, &corner);
3758
3759                         if (corner != rcNone && room != edgeTmp.room1)
3760                         {
3761                                 // The edge goes somewhere
3762                                 // If there's a stub on the other end, delete it:
3763                                 EdgeVec deletedEdges;
3764                                 MapEdge oldEdge;
3765                                 int oldEdgeNum = doc->findEdge(room, corner, oldEdge);
3766
3767                                 if (oldEdgeNum >= 0 && (oldEdge.type2 & etUnexplored))
3768                                 {
3769         //                              deletedEdges.push_back(doc->getEdge(oldEdgeNum));
3770                                         deletedEdges.push_back(doc->edge[oldEdgeNum]);
3771                                         doc->deleteEdge(oldEdgeNum);
3772                                 }
3773
3774                                 // Add the new or changed edge:
3775                                 if (edgeTmp.type2 & etOneWay)
3776                                 {
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;
3783                                 }
3784                                 else
3785                                 {
3786                                         edgeTmp.room2 = room;
3787                                         edgeTmp.end2  = corner;
3788                                 }
3789
3790                                 if (iTmp >= 0)
3791                                         deletedEdges.push_back(e2Tmp);
3792
3793                                 if (deletedEdges.size())
3794                                 {
3795                                         if (deletedEdges.size() == 1)
3796                                                 doc->setUndoData(new UndoChangeEdge(bTmp, deletedEdges[0]));
3797                                         else
3798                                                 doc->setUndoData(new UndoChanges(bTmp, -1, 1, deletedEdges));
3799                                 }
3800                                 else
3801                                         doc->setUndoData(new UndoAdd(bTmp));
3802
3803                                 doc->addEdge(edgeTmp);
3804                         }
3805                         else
3806                         {
3807                                 if (iTmp >= 0)
3808                                 {
3809                                         // We just deleted the old edge
3810                                         if ((e2Tmp.end1 == rcCorner)
3811                                                 || (!(e2Tmp.type1 & etNoRoom2) && (e2Tmp.end2 == rcCorner)))
3812                                         {
3813                                                 doc->addEdge(e2Tmp);  // Put it back temporarily
3814                                                 selectOnlyRoom((e2Tmp.end1 == rcCorner) ? e2Tmp.room1 : e2Tmp.room2);
3815                                                 deleteSelection(false); // Remove entire connection
3816                                         }
3817                                         else
3818                                                 // not a connection to a corner
3819                                                 doc->setUndoData(new UndoDelete(bTmp, e2Tmp));
3820                                 }
3821                                 else if (selectedOne >= 0)
3822                                         makeRoomVisible(selectedOne); // We just clicked on a room corner
3823                         }
3824                         // end if edge doesn't go anywhere
3825                 }
3826                 else if (opInProgress == gmoChangeEdge)
3827                 {
3828                         // We didn't change the edge
3829                         if (selectedOne >= 0)
3830                                 makeRoomVisible(selectedOne);
3831                 }
3832         //      else if (opInProgress == gmoSelectBox)
3833         //      {
3834         //              dc.SetROP2(R2_NOT);
3835         //              QBrush * oldBrush = static_cast<QBrush *>(dc.SelectStockObject(HOLLOW_BRUSH));
3836         //              dc.Rectangle(rTmp);
3837         //              dc.SelectObject(oldBrush);
3838         //      }
3839                 else if (opInProgress == gmoControlDown)
3840                 {
3841                         if (bTmp)
3842                         {
3843                                 if (b2Tmp)
3844                                         deselectPage(iTmp);
3845                                 else
3846                                         deselectRoom(iTmp);
3847
3848                                 selectDone();
3849                         }
3850                         // end if room or page was already selected
3851                 }
3852                 else if (opInProgress >= gmoDrag)
3853                 {
3854                         QPoint p(gridX * (point.x() / gridX - p1Tmp.x()), gridY * (point.y() / gridY - p1Tmp.y()));
3855                         adjustOffset(p);
3856
3857                         if (p.x() || p.y())
3858                         {
3859                                 const QSize offset1(p.x(), p.y());
3860                                 doc->setUndoData(new UndoMove(doc->isDirty, offset1, numSelected, selected, numSelectedPages, selectedPage));
3861
3862                                 for(int i=doc->room.size()-1; i>=0; i--)
3863                                 {
3864                                         if (selected[i])
3865                                                 doc->moveRoom(i, offset1);
3866                                 }
3867
3868                                 for(int i=doc->page.size()-1; i>=0; i--)
3869                                 {
3870                                         if (selectedPage[i])
3871                                                 doc->movePage(i, offset1);
3872                                 }
3873                         }
3874         //              else
3875         //                      InvalidateRect(NULL);
3876                 }
3877
3878                 opInProgress = gmoNone;
3879         }
3880         else if (event->button() == Qt::RightButton)
3881         {
3882 //void MapView::OnRButtonUp(unsigned int nFlags, QPoint point)
3883 //{
3884 //              if (scrollDragTimer)
3885                 if (true)
3886                 {
3887         //              KillTimer(scrollDragTimer);
3888                         scrollDragTimer = 0;
3889
3890                         RoomCorner corner;
3891                         int room = doc->roomHit(point, &corner);
3892                         cornerClicked = corner; // Bleah
3893                         roomClicked = room;     // Bleah
3894
3895                         if (room < 0)
3896                         {
3897                                 if (showPages && (room = doc->pageBorderHit(point)) >= 0)
3898                                 {
3899                                         clearSelection();
3900                                         selectPage(room);
3901                                         selectDone();
3902                                 }
3903
3904                                 mapContextMenu->popup(mapToGlobal(event->pos()));
3905                         }
3906                         else
3907                         {
3908                                 selectOnlyRoom(room);
3909                                 opInProgress = gmoNone;
3910
3911                                 if ((corner != rcNone) && !doc->locked)
3912                                 {
3913                                         iTmp = doc->findEdge(room, corner, edgeTmp);
3914
3915                                         if ((iTmp >= 0) && !(edgeTmp.type2 & etNoRoom2))
3916                                         {
3917                                                 opInProgress = gmoAddCorner;
3918                                                 addCornerAct->setEnabled(true);
3919                                         }
3920                                         else
3921                                                 addCornerAct->setEnabled(false);
3922                                 }
3923
3924                                 roomContextMenu->popup(mapToGlobal(event->pos()));
3925                                 mouse = QPoint(0, 0);
3926                         }
3927
3928         //              dc.LPtoDP(&point);
3929         //              ClientToScreen(&point);
3930         //              pop->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd());
3931         //              //opInProgress = gmoNone;  // FIXME
3932                 }
3933         //      else if (!scrollDrag || (GetCapture() != this))
3934         //              CScrollZoomView::OnRButtonUp(nFlags, point);
3935                 else
3936                 {
3937         //              if (opInProgress == gmoNone)
3938         //                      ReleaseCapture();
3939         //              else
3940         //                      ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
3941
3942 //                      scrollDrag = false;
3943                 }
3944 //}
3945         }
3946
3947         update();
3948 }
3949
3950
3951 #if 0
3952 int MapView::OnMouseActivate(CWnd * wnd, unsigned int hitTest, unsigned int message)
3953 {
3954         const int  result = CScrollZoomView::OnMouseActivate(wnd, hitTest, message);
3955
3956         if ((result == MA_ACTIVATE) && GUEmapEatClicks)
3957                 return MA_ACTIVATEANDEAT;
3958         else
3959                 return result;
3960 }
3961
3962
3963 bool MapView::OnMouseWheel(unsigned int nFlags, short zDelta, QPoint pt)
3964 {
3965         if (nFlags == MK_CONTROL)
3966         {
3967                 OnViewZoom((zDelta > 0) ? ID_VIEW_ZOOM_IN : ID_VIEW_ZOOM_OUT);
3968                 return TRUE;
3969         }
3970
3971         return CScrollZoomView::OnMouseWheel(nFlags, zDelta, pt);
3972 }
3973
3974
3975 void MapView::OnSize(unsigned int nType, int cx, int cy)
3976 {
3977         CScrollZoomView::OnSize(nType, cx, cy);
3978
3979         if (initialized())  // Make sure view has been initialized
3980                 setScrollBars();
3981 }
3982
3983
3984 void MapView::OnTimer(unsigned int idEvent)
3985 {
3986         if (idEvent == menuTimer)
3987         {
3988                 // We've held the button down long enough, switch to dragging:
3989                 SetCapture();
3990                 KillTimer(scrollDragTimer);
3991                 scrollDragTimer = 0;
3992                 scrollDrag = true;
3993                 ::SetCursor(handCursor);
3994         }
3995         else
3996                 CScrollZoomView::OnTimer(idEvent);
3997 }
3998
3999
4000 //
4001 // Update the view after the document changes:
4002 //
4003 // Input:
4004 //   lHint:
4005 //     dupDeletedRoom:
4006 //       A room has just been deleted
4007 //     dupPageCount:
4008 //       The number of pages has changed
4009 //     dupRoomComment:
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.
4016 //   pHint:
4017 //     NULL means invalidate the entire view (but see lHint)
4018 //     Otherwise, it's a QRect* to the area to invalidate
4019 //
4020 void MapView::OnUpdate(CView * pSender, LPARAM lHint, CObject * pHint)
4021 {
4022         if (lHint == dupDeletedRoom && opInProgress != gmoDeleting && numSelected)
4023         {
4024                 // Clear selection unless this is the view doing the deleting:
4025                 clearSelection();
4026                 selectDone();
4027         }
4028         else if (lHint == dupPageCount && opInProgress != gmoDeleting)
4029         {
4030                 selectedPage.resize(GetDocument()->getPages().size(), 0);
4031
4032                 if (numSelectedPages)
4033                 {
4034                         deselectPages();
4035                         selectDone();
4036                 }
4037         }
4038         else if (lHint == dupRoomComment)
4039         {
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);
4043
4044                 if (!pHint)
4045                         return;         // Don't invalidate entire view
4046         }
4047
4048         if (lHint == dupNavigationMode)
4049         {
4050                 const MapDoc * doc = GetDocument();
4051                 ASSERT_VALID(doc);
4052                 const int numRooms = doc->room.size();
4053
4054                 if (selectedOne < 0 || doc->room[selectedOne].isCorner())
4055                         selectOnlyRoom(0);
4056
4057                 makeRoomVisible(selectedOne);
4058         } // end if switching to navigation mode
4059         else if (pHint)
4060         {
4061                 QRect rect(*reinterpret_cast<const QRect *>(pHint));
4062                 CClientDC dc(this);
4063                 OnPrepareDC(&dc);
4064                 dc.LPtoDP(&rect);
4065                 rect.NormalizeRect();
4066                 InvalidateRect(rect);
4067         }
4068         else if (lHint != dupRoomComment)
4069                 Invalidate();
4070 }
4071
4072
4073 void MapView::OnUpdateEditAddCorner(CCmdUI* pCmdUI)
4074 {
4075         pCmdUI->Enable(opInProgress == gmoAddCorner);
4076 }
4077
4078
4079 void MapView::OnUpdateEditPaste(CCmdUI* pCmdUI)
4080 {
4081         pCmdUI->Enable((static_cast<CMapApp*>(AfxGetApp())->getClipboard() != NULL)
4082                 && !GetDocument()->locked);
4083 }
4084
4085
4086 //
4087 // Commands which require selected rooms and unlocked document:
4088 //
4089 // Edit|Clear
4090 // Edit|Cut
4091 // Edit|Copy
4092 // Edit|Select connected
4093 //
4094 void MapView::OnUpdateSelectedUnlocked(CCmdUI * pCmdUI)
4095 {
4096         MapDoc * const doc = GetDocument();
4097         ASSERT_VALID(doc);
4098
4099         bool  selected = numSelected;
4100
4101         if ((pCmdUI->m_nID != ID_EDIT_SELECT_CONNECTED) && numSelectedPages)
4102         {
4103                 if ((pCmdUI->m_nID != ID_EDIT_COPY)
4104                         && (doc->page.size() == numSelectedPages))
4105                         selected = false;         // Can't delete all pages
4106                 else
4107                         selected = true;
4108         }
4109
4110         pCmdUI->Enable(selected && !doc->locked);
4111 }
4112
4113
4114 //
4115 // Commands which require the document to be unlocked:
4116 //
4117 // Edit|Select all
4118 //
4119 void MapView::OnUpdateUnlocked(CCmdUI* pCmdUI)
4120 {
4121         pCmdUI->Enable(!GetDocument()->locked);
4122 }
4123
4124
4125 //
4126 // Toggle the grid on and off:
4127 //
4128 void MapView::OnViewGrid()
4129 {
4130         showGrid = !showGrid;
4131         InvalidateRect(NULL);
4132 }
4133
4134
4135 void MapView::OnUpdateViewGrid(CCmdUI * pCmdUI)
4136 {
4137         pCmdUI->SetCheck(showGrid);
4138 }
4139
4140
4141 void MapView::OnViewZoom(unsigned int cmd)
4142 {
4143         ASSERT((cmd == ID_VIEW_ZOOM_IN) || (cmd == ID_VIEW_ZOOM_OUT));
4144
4145         if (cmd == ID_VIEW_ZOOM_OUT)
4146                 zoomTo(zoom % 10 ? zoom - zoom % 10 : zoom - 10);
4147         else
4148                 zoomTo(zoom + 10 - zoom % 10);
4149 }
4150
4151
4152 void MapView::OnUpdateViewZoom(CCmdUI * pCmdUI)
4153 {
4154         pCmdUI->Enable((pCmdUI->m_nID == ID_VIEW_ZOOM_IN)
4155                 ? (zoom < maxZoom) : (zoom > minZoom));
4156 }
4157
4158
4159 //
4160 // Redraw the window:
4161 //
4162 void MapView::OnWindowRefresh()
4163 {
4164         InvalidateRect(NULL);
4165 }
4166
4167
4168 /////////////////////////////////////////////////////////////////////////////
4169 /////////////////////////////////////////////////////////////////////////////
4170 // CRepaginateDlg dialog
4171
4172 CRepaginateDlg::CRepaginateDlg(): CDialog(CRepaginateDlg::IDD, NULL)
4173 {
4174         //{{AFX_DATA_INIT(CRepaginateDlg)
4175         //}}AFX_DATA_INIT
4176 }
4177
4178 BEGIN_MESSAGE_MAP(CRepaginateDlg, CDialog)
4179         //{{AFX_MSG_MAP(CRepaginateDlg)
4180         ON_BN_CLICKED(IDYES, OnYes)
4181         //}}AFX_MSG_MAP
4182 END_MESSAGE_MAP();
4183
4184 /////////////////////////////////////////////////////////////////////////////
4185 // CRepaginateDlg message handlers
4186
4187
4188 bool CRepaginateDlg::OnInitDialog()
4189 {
4190         CDialog::OnInitDialog();
4191
4192         SendDlgItemMessage(IDC_EXCLAMATION, STM_SETICON, (WPARAM) gueApp()->LoadStandardIcon(IDI_EXCLAMATION));
4193         MessageBeep(MB_ICONEXCLAMATION);
4194
4195         return true;  // return TRUE unless you set the focus to a control
4196 }
4197
4198
4199 void CRepaginateDlg::OnYes()
4200 {
4201         EndDialog(IDYES);
4202 }
4203 #endif
4204