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