]> Shamusworld >> Repos - guemap/blob - src/guemapapp.cpp
Fix room adding to work properly, misc. small changes.
[guemap] / src / guemapapp.cpp
1 //
2 // GUEmap
3 // Copyright 1997-2007 by 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 // GUEmap.cpp: Defines the class behaviors for the application
10 //
11
12 #include "guemapapp.h"
13
14 #include <QApplication>
15 #include "mainwin.h"
16
17 // Main app constructor--we stick globally accessible stuff here...
18
19 GUEMapApp::GUEMapApp(int & argc, char * argv[]): QApplication(argc, argv)//, charWnd(NULL)
20 {
21         mainWindow = new MainWin;
22         mainWindow->show();
23 }
24
25 // Here's the main application loop--short and simple...
26 int main(int argc, char * argv[])
27 {
28         Q_INIT_RESOURCE(guemap);        // This must the same name as the qrc filename
29
30         GUEMapApp app(argc, argv);
31
32         return app.exec();
33 }
34
35 #if 0
36 #include "StdAfx.hpp"
37 #include "GUEmap.hpp"
38
39 #include <winspool.h>           // For printer settings
40
41 #include "MainFrm.hpp"
42 #include "ChildFrm.hpp"
43 #include "CommentDlg.hpp"
44 #include "FindDlg.hpp"
45 #include "MapDoc.hpp"
46 #include "MapView.hpp"
47 #include "NavOpt.hpp"
48
49 #ifdef _DEBUG
50 #define new DEBUG_NEW
51 #undef THIS_FILE
52 static char THIS_FILE[] = __FILE__;
53 #endif
54
55 bool     GUEmapEatClicks = true;
56 HCURSOR  handCursor = NULL;
57
58 const char *const iniCDlg        = "CommentDialog";
59 const char *const iniMainWin     = "MainWindow";
60 const char *const iniWinLeft     = "Left";
61 const char *const iniWinRight    = "Right";
62 const char *const iniWinTop      = "Top";
63 const char *const iniWinBottom   = "Bottom";
64
65 const char *const iniNavigation  = "Navigation";
66 const char *const iniNavCopy     = "AutoCopy";
67 const char *const iniNavCRLF     = "AddCRLF";
68 const char *const iniNavEdit     = "AutoEdit";
69 const char *const iniNavStub     = "PreferUnexplored";
70
71 /////////////////////////////////////////////////////////////////////////////
72 // Miscellaneous functions:
73 //--------------------------------------------------------------------
74 // Copy a string, replacing CRLF with space:
75 //
76 // Assumes that any CR is followed by a LF.
77 //
78 // Input:
79 //   source:  The string to copy
80 //
81 // Output:
82 //   dest:    The copied string, with any CRLF replaced by a space
83
84 void copyToOneLine(String& dest, const String& source)
85 {
86   dest = source;
87   StrIdx  p = 0;
88   while ((p = dest.find(_T('\r'),p)) != String::npos) {
89     dest.erase(p,1);
90     dest[p] = _T(' ');
91   }
92 } // end copyToOneLine
93
94 //--------------------------------------------------------------------
95 // Fill paragraphs in a string:
96 //
97 // Removes all leading and trailing space, then removes CRLF pairs
98 // unless they are on a blank line or followed by a space.  Assumes
99 // that any CR is followed by a LF.
100 //
101 // Input:
102 //   s:  The string to fill
103 //
104 // Output:
105 //   s:  The filled string
106
107 void fillParagraphs(String& s)
108 {
109   trimRight(s);
110   trimLeft(s);
111
112   StrIdx  p = 0;
113   // Convert tabs to spaces (just in case):
114   while ((p = s.find(_T('\t'),p)) != String::npos)
115     s[p] = _T(' ');
116
117   p = 0;
118   while ((p = s.find(_T('\r'),p)) != String::npos) {
119     if ((s[p-1] != _T('\n')) &&                       // Not a blank line &
120         s.find_first_not_of(_T("\n\r "), p) == p+2) { // next char not a space
121       StrIdx  len = 1;
122       while (s[p-1] == _T(' ')) {
123         --p;
124         ++len;
125       } // end while spaces precede this CR
126       s.erase(p,len);           // Erase the CR and preceding spaces
127       s[p] = _T(' ');           // Change the LF to a space
128     } else
129       ++p; // Don't remove this CRLF
130   } // end while more CRs
131 } // end fillParagraphs
132
133 //--------------------------------------------------------------------
134 // Set a window's position:
135 //
136 // Input:
137 //   pos:  The new position of the window (in screen coordinates)
138 //   w:    The window to move
139 //
140 // Note:
141 //   If pos.right is 0, or any part of the window is outside the
142 //   current desktop, then we don't move the window.
143
144 void setWindowPos(const CRect& pos, CWnd* w)
145 {
146   if (pos.right && (pos.left < pos.right) && (pos.top < pos.bottom)) {
147     CRect desktop;
148     ::GetWindowRect(::GetDesktopWindow(),&desktop);
149     if ((pos.left   >= desktop.left)  &&
150         (pos.right  <= desktop.right) &&
151         (pos.top    >= desktop.top)   &&
152         (pos.bottom <= desktop.bottom))
153       w->MoveWindow(&pos);
154   }
155 } // end setWindowPos
156
157 //--------------------------------------------------------------------
158 // Trim whitespace from left of string:
159
160 void trimLeft(String& s)
161 {
162   StrIdx  p = s.find_first_not_of(_T("\n\r\t "));
163   if (p > 0) s.erase(0, p);
164 } // end trimLeft
165
166 //--------------------------------------------------------------------
167 // Trim whitespace from right of string:
168
169 void trimRight(String& s)
170 {
171   StrIdx  p = s.find_last_not_of(_T("\n\r\t "));
172   if (p < s.length()) s.erase(p+1);
173 } // end trimRight
174
175 /////////////////////////////////////////////////////////////////////////////
176 // CMapApp
177
178 BEGIN_MESSAGE_MAP(CMapApp, CWinApp)
179         //{{AFX_MSG_MAP(CMapApp)
180         ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
181         ON_COMMAND(ID_EDIT_FIND, OnEditFind)
182         ON_COMMAND(ID_VIEW_COMMENTS, OnViewComments)
183         ON_COMMAND(ID_VIEW_SETTINGS, OnViewSettings)
184         ON_UPDATE_COMMAND_UI(ID_VIEW_COMMENTS, OnUpdateViewComments)
185         //}}AFX_MSG_MAP
186         // Standard file based document commands
187         ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
188         ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
189         // Standard print setup command
190         ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
191         // Standard help commands (see also MainFrame.cpp)
192         ON_COMMAND(ID_HELP_INDEX, CWinApp::OnHelpIndex)
193 END_MESSAGE_MAP();
194
195 /////////////////////////////////////////////////////////////////////////////
196 // CMapApp construction
197
198 CMapApp::CMapApp()
199 : clipboard(NULL),
200   commentDlg(NULL),
201   findDlg(NULL),
202   initialized(false)
203 {
204         // TODO: add construction code here,
205         // Place all significant initialization in InitInstance
206 }
207
208 CMapApp::~CMapApp()
209 {
210   delete clipboard;
211   delete commentDlg;
212   delete findDlg;
213 } // end CMapApp::~CMapApp
214
215 /////////////////////////////////////////////////////////////////////////////
216 // The one and only CMapApp object
217
218 CMapApp theApp;
219 String  mapWinClass;
220
221 /////////////////////////////////////////////////////////////////////////////
222 // CMapApp initialization
223
224 BOOL CMapApp::InitInstance()
225 {
226   // Standard initialization
227   // If you are not using these features and wish to reduce the size
228   //  of your final executable, you should remove from the following
229   //  the specific initialization routines you do not need.
230
231 #ifdef _AFXDLL
232   Enable3dControls();           // Call this when using MFC in a shared DLL
233 #else
234   Enable3dControlsStatic();     // Call this when linking to MFC statically
235 #endif
236
237   mapWinClass = AfxRegisterWndClass(
238     CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
239     LoadStandardCursor(IDC_ARROW), // Standard cursor
240     (HBRUSH)::GetStockObject(WHITE_BRUSH)
241   );
242
243   handCursor = LoadCursor(IDC_HAND);
244
245   LoadStdProfileSettings(9); // Load standard INI file options (including MRU)
246
247   // Register the application's document templates.  Document templates
248   //  serve as the connection between documents, frame windows and views.
249
250   CMultiDocTemplate* pDocTemplate;
251   pDocTemplate = new CMultiDocTemplate(
252     IDR_GUEMAPTYPE,
253     RUNTIME_CLASS(CMapDoc),
254     RUNTIME_CLASS(CChildFrame), // custom MDI child frame
255     RUNTIME_CLASS(CMapView));
256   AddDocTemplate(pDocTemplate);
257
258   // create room comments dialog
259   if (!(commentDlg = new CCommentDlg()))
260     return FALSE;
261
262   loadWindowPos(commentDlg->pos, iniCDlg);
263
264   // create main MDI Frame window
265   CMainFrame* pMainFrame = new CMainFrame;
266   if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
267     return FALSE;
268   m_pMainWnd = pMainFrame;
269
270   // Enable file manager drag/drop and DDE Execute open
271   EnableShellOpen();
272   RegisterShellFileTypes(TRUE);
273
274   // Parse command line for standard shell commands, DDE, file open
275   CCommandLineInfo cmdInfo;
276   ParseCommandLine(cmdInfo);
277
278   // Dispatch commands specified on the command line
279   if (!ProcessShellCommand(cmdInfo))
280     return FALSE;
281
282   // The main window has been initialized, so show and update it.
283   CRect  pos;
284   loadWindowPos(pos, iniMainWin);
285   setWindowPos(pos, pMainFrame);
286   pMainFrame->ShowWindow(m_nCmdShow);
287   pMainFrame->UpdateWindow();
288   pMainFrame->DragAcceptFiles(TRUE);
289
290   setLandscape();               // Switch to landscape mode
291
292   // Load navigation options:
293   autoEdit = GetProfileInt(iniNavigation,iniNavEdit,true);
294   naviCopy = GetProfileInt(iniNavigation,iniNavCopy,true);
295   naviCRLF = GetProfileInt(iniNavigation,iniNavCRLF,false);
296   preferUnexplored = GetProfileInt(iniNavigation,iniNavStub,true);
297
298   initialized = true;
299   return TRUE;
300 } // end CMapApp::InitInstance
301
302 //--------------------------------------------------------------------
303 // Load a window position from the INI file:
304 //
305 // Input:
306 //   section:  The title of the section to load it from
307 //
308 // Output:
309 //   pos:  The window position
310
311 void CMapApp::loadWindowPos(CRect& pos, LPCTSTR section)
312 {
313   pos.left   = GetProfileInt(section, iniWinLeft,   0);
314   pos.right  = GetProfileInt(section, iniWinRight,  0);
315   pos.top    = GetProfileInt(section, iniWinTop,    0);
316   pos.bottom = GetProfileInt(section, iniWinBottom, 0);
317 } // end CMapApp::loadWindowPos
318
319 //--------------------------------------------------------------------
320 // Set landscape mode as default:
321 //
322 // From KB: Q126897 (1-21-96)
323
324 void CMapApp::setLandscape()
325 {
326   // Get default printer settings.
327   PRINTDLG   pd;
328
329   pd.lStructSize = (DWORD) sizeof(PRINTDLG);
330   if (GetPrinterDeviceDefaults(&pd)) {
331     // Lock memory handle.
332     DEVMODE FAR* pDevMode =
333       (DEVMODE FAR*)::GlobalLock(m_hDevMode);
334     LPDEVNAMES lpDevNames;
335     LPTSTR lpszDriverName, lpszDeviceName, lpszPortName;
336     HANDLE hPrinter;
337
338     if (pDevMode) {
339       // Change printer settings in here.
340       pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
341       // Unlock memory handle.
342       lpDevNames = (LPDEVNAMES)GlobalLock(pd.hDevNames);
343       lpszDriverName = (LPTSTR )lpDevNames + lpDevNames->wDriverOffset;
344       lpszDeviceName = (LPTSTR )lpDevNames + lpDevNames->wDeviceOffset;
345       lpszPortName   = (LPTSTR )lpDevNames + lpDevNames->wOutputOffset;
346
347       ::OpenPrinter(lpszDeviceName, &hPrinter, NULL);
348       ::DocumentProperties(NULL,hPrinter,lpszDeviceName,pDevMode,
349                            pDevMode, DM_IN_BUFFER|DM_OUT_BUFFER);
350
351       // Sync the pDevMode.
352       // See SDK help for DocumentProperties for more info.
353       ::ClosePrinter(hPrinter);
354       ::GlobalUnlock(m_hDevNames);
355       ::GlobalUnlock(m_hDevMode);
356     }
357   }
358 } // end CMapApp::setLandscape
359
360 /////////////////////////////////////////////////////////////////////////////
361 // CMapApp shutdown:
362
363 int CMapApp::ExitInstance()
364 {
365   if (initialized) { // We finished initialization
366     WriteProfileInt(iniNavigation, iniNavEdit, autoEdit);
367     WriteProfileInt(iniNavigation, iniNavCopy, naviCopy);
368     WriteProfileInt(iniNavigation, iniNavCRLF, naviCRLF);
369     WriteProfileInt(iniNavigation, iniNavStub, preferUnexplored);
370
371     if (commentDlg) {
372       if (commentDlg->GetSafeHwnd())
373         OnViewComments();       // Destroy comment window
374       saveWindowPos(commentDlg->pos, iniCDlg);
375     }
376
377     if (findDlg && findDlg->GetSafeHwnd())
378       findDlg->DestroyWindow();
379   }
380
381   return CWinApp::ExitInstance();
382 } // end CMapApp::ExitInstance
383
384 //--------------------------------------------------------------------
385 // Save a window position to the INI file:
386 //
387 // Input:
388 //   pos:      The window position
389 //   section:  The title of the section to save it in
390
391 void CMapApp::saveWindowPos(LPCRECT pos, LPCTSTR section)
392 {
393   WriteProfileInt(section, iniWinLeft,   pos->left);
394   WriteProfileInt(section, iniWinRight,  pos->right);
395   WriteProfileInt(section, iniWinTop,    pos->top);
396   WriteProfileInt(section, iniWinBottom, pos->bottom);
397 } // end CMapApp::saveWindowPos
398
399 /////////////////////////////////////////////////////////////////////////////
400 // CAboutDlg dialog used for App About
401
402 class CAboutDlg : public CDialog
403 {
404  public:
405   CAboutDlg();
406
407   // Dialog Data
408   //{{AFX_DATA(CAboutDlg)
409   enum { IDD = IDD_ABOUTBOX };
410   CString       username;
411   //}}AFX_DATA
412
413   // ClassWizard generated virtual function overrides
414   //{{AFX_VIRTUAL(CAboutDlg)
415  protected:
416   virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
417   //}}AFX_VIRTUAL
418
419   // Implementation
420  protected:
421   //{{AFX_MSG(CAboutDlg)
422   //}}AFX_MSG
423   DECLARE_MESSAGE_MAP();
424 }; // end CAboutDlg
425
426 CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
427 {
428         //{{AFX_DATA_INIT(CAboutDlg)
429         username = _T("");
430         //}}AFX_DATA_INIT
431 }
432
433 void CAboutDlg::DoDataExchange(CDataExchange* pDX)
434 {
435         CDialog::DoDataExchange(pDX);
436         //{{AFX_DATA_MAP(CAboutDlg)
437         //}}AFX_DATA_MAP
438 }
439
440 BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
441         //{{AFX_MSG_MAP(CAboutDlg)
442         //}}AFX_MSG_MAP
443 END_MESSAGE_MAP();
444
445 // App command to run the dialog
446 void CMapApp::OnAppAbout()
447 {
448   CAboutDlg aboutDlg;
449   aboutDlg.DoModal();
450 } // end CMapApp::OnAppAbout
451
452 /////////////////////////////////////////////////////////////////////////////
453 // CMapApp commands
454 //--------------------------------------------------------------------
455 // Find text in comments:
456
457 void CMapApp::OnEditFind()
458 {
459   if (!findDlg) {
460     VERIFY(findDlg = new CFindDlg(commentDlg));
461     if (!findDlg) return;
462   }
463
464   if (findDlg->GetSafeHwnd() != NULL) {
465     findDlg->SetActiveWindow();
466     findDlg->GotoDlgCtrl(findDlg->GetDlgItem(IDC_FIND_WHAT));
467   } else {
468     findDlg->Create();
469     setWindowPos(findDlg->pos, findDlg);
470   }
471 } // end CMapApp::OnEditFind
472
473 //====================================================================
474 // The Room Comments dialog:
475 //--------------------------------------------------------------------
476 // Display or hide the free-floating Room Comments dialog:
477
478 void CMapApp::OnViewComments()
479 {
480   if (commentDlg->GetSafeHwnd() != NULL) {
481     commentDlg->GetWindowRect(&commentDlg->pos);
482     commentDlg->DestroyWindow();
483   } else {
484     commentDlg->Create();
485     setWindowPos(commentDlg->pos, commentDlg);
486     commentDlg->SetWindowText(commentDlg->title.empty()
487                               ? _T("Room Comments")
488                               : commentDlg->title.c_str());
489     commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,WM_ENABLE,
490                                    commentDlg->fromView != NULL);
491     commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,EM_SETREADONLY,
492                                    commentDlg->fromView == NULL);
493     commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,EM_SETSEL, -1, -2);
494   }
495 } // end CMapApp::OnViewComments
496
497 void CMapApp::OnUpdateViewComments(CCmdUI* pCmdUI)
498 {
499   pCmdUI->SetCheck(commentDlg->GetSafeHwnd() != NULL);
500 } // end CMapApp::OnUpdateViewComments
501
502 //--------------------------------------------------------------------
503 // Move the Room Comments dialog so it doesn't obscure the map:
504 //
505 // Input:
506 //   mapPos:
507 //     The screen coordinates that should not be obscured
508 //     Must be normalized
509
510 void CMapApp::adjustCommentPos(LPCRECT mapPos)
511 {
512   if (commentDlg->GetSafeHwnd()) {
513     CRect  dlgPos, r;
514     commentDlg->GetWindowRect(&dlgPos);
515     if (r.IntersectRect(dlgPos, mapPos)) {
516       CRect  mainPos;
517       m_pMainWnd->GetClientRect(&mainPos);
518       m_pMainWnd->ClientToScreen(&mainPos);
519       BOOL max;
520       const CWnd* curWin =
521         static_cast<CMainFrame*>(m_pMainWnd)->MDIGetActive(&max);
522       if (max) {
523         curWin = curWin->GetTopWindow(); // Get the active view
524         curWin->GetClientRect(&r);       // and make sure we don't
525         curWin->ClientToScreen(&r);      // cover up the scroll bars
526         mainPos.bottom = r.bottom;       // on the right and bottom
527         mainPos.right  = r.right;
528       } // end if active window is maximized
529       dlgPos.OffsetRect(mainPos.right - dlgPos.right, // Top right
530                         mainPos.top - dlgPos.top);
531       if (!r.IntersectRect(dlgPos, mapPos)) goto move;
532       dlgPos.OffsetRect(mainPos.right - dlgPos.right, // Bottom right
533                         mainPos.bottom - dlgPos.bottom);
534       if (!r.IntersectRect(dlgPos, mapPos)) goto move;
535       dlgPos.OffsetRect(mainPos.left - dlgPos.left,   // Bottom left
536                         mainPos.bottom - dlgPos.bottom);
537       if (!r.IntersectRect(dlgPos, mapPos)) goto move;
538       dlgPos.OffsetRect(mainPos.left - dlgPos.left,   // Top left
539                         mainPos.top - dlgPos.top);
540       if (r.IntersectRect(dlgPos, mapPos)) return;  // Give up
541      move:
542       commentDlg->MoveWindow(dlgPos);
543     } // end if area is obscured
544   } // end if commentDlg is visible
545 } // end CMapApp::adjustCommentPos
546
547 //--------------------------------------------------------------------
548 // Disable comment dialog when closing its view:
549 //
550 // Input:
551 //   view:  The view that is closing
552
553 void CMapApp::closingView(const CMapView* view)
554 {
555   if (commentDlg->fromView == view)
556     setComment(NULL, NULL);
557 } // end CMapApp::closingView
558
559 //--------------------------------------------------------------------
560 // Switch to the Room Comments dialog:
561
562 void CMapApp::editComment()
563 {
564   if (commentDlg->GetSafeHwnd() == NULL)
565     OnViewComments();
566   else {
567     commentDlg->SetFocus();
568     commentDlg->SendDlgItemMessage(IDC_CD_COMMENT, EM_SETSEL, -1, -2);
569   }
570 } // end CMapApp::editComment
571
572 //--------------------------------------------------------------------
573 // Update the Room Comments dialog:
574 //
575 // Input:
576 //   fromView:
577 //     The map view which is posting this comment
578 //     NULL means the view is closing, so disable the dialog
579 //   room:
580 //     The room whose comment should be displayed in the dialog
581 //     NULL means no room is selected, so display map comment
582 //   takeFocus: (default true)
583 //     TRUE means to override a comment from a different view
584
585 void CMapApp::setComment(CMapView* fromView, const MapRoom* room,
586                          bool takeFocus) const
587 {
588   if (!commentDlg->active &&
589       (takeFocus || (commentDlg->fromView == fromView))) {
590     commentDlg->fromView = fromView;
591     if (room && !(room->flags & rfCorner)) {
592       Strcpy(commentDlg->comment, room->note);
593       copyToOneLine(commentDlg->title, room->name);
594       commentDlg->roomComment = true;
595     } else {
596       if (fromView) {
597         const CMapDoc*  doc = fromView->GetDocument();
598         ASSERT_VALID(doc);
599
600         Strcpy(commentDlg->comment, doc->getNote());
601         commentDlg->title = doc->getName();
602         if (!commentDlg->title.empty())
603           commentDlg->title += _T(" -- ");
604       } else {
605         commentDlg->comment.Empty();
606         commentDlg->title.erase();
607       } // end else no active view
608       commentDlg->title += _T("Map Comments");
609       commentDlg->roomComment = false;
610     } // end else no current room
611     if (commentDlg->GetSafeHwnd()) {
612       commentDlg->UpdateData(FALSE);
613       commentDlg->SetWindowText(commentDlg->title.empty()
614                                 ? _T("Room Comments")
615                                 : commentDlg->title.c_str());
616       commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,WM_ENABLE,
617                                      fromView != NULL);
618       commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,EM_SETREADONLY,
619                                      fromView == NULL);
620     } // end if comment dialog is open
621   } // end if we should update the comment
622 } // end CMapApp::setComment
623
624 //====================================================================
625 // CMapApp Options dialog:
626 //--------------------------------------------------------------------
627 // Bring up the Options dialog:
628
629 void CMapApp::OnViewSettings()
630 {
631   editOptions();
632 } // end CMapApp::OnViewSettings
633
634 //--------------------------------------------------------------------
635 // Bring up the Options dialog:
636
637 void CMapApp::editOptions()
638 {
639   CPropertySheet dlg(IDR_OPTIONS);
640
641   COptNavPage  nav;
642   nav.copy = naviCopy;
643   nav.CRLF = naviCRLF;
644   nav.editProps = autoEdit;
645   nav.stubs = (preferUnexplored ? 0 : 1);
646   dlg.AddPage(&nav);
647
648   if (dlg.DoModal() == IDOK) {
649     autoEdit = nav.editProps;
650     naviCopy = nav.copy;
651     naviCRLF = nav.CRLF;
652     preferUnexplored = (nav.stubs == 0);
653   } // end if OK pressed
654 } // end CMapApp::editOptions
655 #endif