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