3 // Copyright 1997-2007 by Christopher J. Madsen
4 // (C) 2019 James Hammons
6 // GUEmap is licensed under either version 2 of the GPL, or (at your option)
7 // any later version. See LICENSE file for details.
9 // GUEmap.cpp: Defines the class behaviors for the application
12 #include "guemapapp.h"
14 #include <QApplication>
17 // Main app constructor--we stick globally accessible stuff here...
19 GUEMapApp::GUEMapApp(int & argc, char * argv[]): QApplication(argc, argv)//, charWnd(NULL)
21 mainWindow = new MainWin;
26 // Here's the main application loop--short and simple...
27 int main(int argc, char * argv[])
29 Q_INIT_RESOURCE(guemap); // This must the same name as the qrc filename
31 GUEMapApp app(argc, argv);
40 #include <winspool.h> // For printer settings
42 #include "MainFrm.hpp"
43 #include "ChildFrm.hpp"
44 #include "CommentDlg.hpp"
45 #include "FindDlg.hpp"
47 #include "MapView.hpp"
53 static char THIS_FILE[] = __FILE__;
56 bool GUEmapEatClicks = true;
57 HCURSOR handCursor = NULL;
59 const char *const iniCDlg = "CommentDialog";
60 const char *const iniMainWin = "MainWindow";
61 const char *const iniWinLeft = "Left";
62 const char *const iniWinRight = "Right";
63 const char *const iniWinTop = "Top";
64 const char *const iniWinBottom = "Bottom";
66 const char *const iniNavigation = "Navigation";
67 const char *const iniNavCopy = "AutoCopy";
68 const char *const iniNavCRLF = "AddCRLF";
69 const char *const iniNavEdit = "AutoEdit";
70 const char *const iniNavStub = "PreferUnexplored";
72 /////////////////////////////////////////////////////////////////////////////
73 // Miscellaneous functions:
74 //--------------------------------------------------------------------
75 // Copy a string, replacing CRLF with space:
77 // Assumes that any CR is followed by a LF.
80 // source: The string to copy
83 // dest: The copied string, with any CRLF replaced by a space
85 void copyToOneLine(String& dest, const String& source)
89 while ((p = dest.find(_T('\r'),p)) != String::npos) {
93 } // end copyToOneLine
95 //--------------------------------------------------------------------
96 // Fill paragraphs in a string:
98 // Removes all leading and trailing space, then removes CRLF pairs
99 // unless they are on a blank line or followed by a space. Assumes
100 // that any CR is followed by a LF.
103 // s: The string to fill
106 // s: The filled string
108 void fillParagraphs(String& s)
114 // Convert tabs to spaces (just in case):
115 while ((p = s.find(_T('\t'),p)) != String::npos)
119 while ((p = s.find(_T('\r'),p)) != String::npos) {
120 if ((s[p-1] != _T('\n')) && // Not a blank line &
121 s.find_first_not_of(_T("\n\r "), p) == p+2) { // next char not a space
123 while (s[p-1] == _T(' ')) {
126 } // end while spaces precede this CR
127 s.erase(p,len); // Erase the CR and preceding spaces
128 s[p] = _T(' '); // Change the LF to a space
130 ++p; // Don't remove this CRLF
131 } // end while more CRs
132 } // end fillParagraphs
134 //--------------------------------------------------------------------
135 // Set a window's position:
138 // pos: The new position of the window (in screen coordinates)
139 // w: The window to move
142 // If pos.right is 0, or any part of the window is outside the
143 // current desktop, then we don't move the window.
145 void setWindowPos(const CRect& pos, CWnd* w)
147 if (pos.right && (pos.left < pos.right) && (pos.top < pos.bottom)) {
149 ::GetWindowRect(::GetDesktopWindow(),&desktop);
150 if ((pos.left >= desktop.left) &&
151 (pos.right <= desktop.right) &&
152 (pos.top >= desktop.top) &&
153 (pos.bottom <= desktop.bottom))
156 } // end setWindowPos
158 //--------------------------------------------------------------------
159 // Trim whitespace from left of string:
161 void trimLeft(String& s)
163 StrIdx p = s.find_first_not_of(_T("\n\r\t "));
164 if (p > 0) s.erase(0, p);
167 //--------------------------------------------------------------------
168 // Trim whitespace from right of string:
170 void trimRight(String& s)
172 StrIdx p = s.find_last_not_of(_T("\n\r\t "));
173 if (p < s.length()) s.erase(p+1);
176 /////////////////////////////////////////////////////////////////////////////
179 BEGIN_MESSAGE_MAP(CMapApp, CWinApp)
180 //{{AFX_MSG_MAP(CMapApp)
181 ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
182 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
183 ON_COMMAND(ID_VIEW_COMMENTS, OnViewComments)
184 ON_COMMAND(ID_VIEW_SETTINGS, OnViewSettings)
185 ON_UPDATE_COMMAND_UI(ID_VIEW_COMMENTS, OnUpdateViewComments)
187 // Standard file based document commands
188 ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
189 ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
190 // Standard print setup command
191 ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
192 // Standard help commands (see also MainFrame.cpp)
193 ON_COMMAND(ID_HELP_INDEX, CWinApp::OnHelpIndex)
196 /////////////////////////////////////////////////////////////////////////////
197 // CMapApp construction
205 // TODO: add construction code here,
206 // Place all significant initialization in InitInstance
214 } // end CMapApp::~CMapApp
216 /////////////////////////////////////////////////////////////////////////////
217 // The one and only CMapApp object
222 /////////////////////////////////////////////////////////////////////////////
223 // CMapApp initialization
225 BOOL CMapApp::InitInstance()
227 // Standard initialization
228 // If you are not using these features and wish to reduce the size
229 // of your final executable, you should remove from the following
230 // the specific initialization routines you do not need.
233 Enable3dControls(); // Call this when using MFC in a shared DLL
235 Enable3dControlsStatic(); // Call this when linking to MFC statically
238 mapWinClass = AfxRegisterWndClass(
239 CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
240 LoadStandardCursor(IDC_ARROW), // Standard cursor
241 (HBRUSH)::GetStockObject(WHITE_BRUSH)
244 handCursor = LoadCursor(IDC_HAND);
246 LoadStdProfileSettings(9); // Load standard INI file options (including MRU)
248 // Register the application's document templates. Document templates
249 // serve as the connection between documents, frame windows and views.
251 CMultiDocTemplate* pDocTemplate;
252 pDocTemplate = new CMultiDocTemplate(
254 RUNTIME_CLASS(CMapDoc),
255 RUNTIME_CLASS(CChildFrame), // custom MDI child frame
256 RUNTIME_CLASS(CMapView));
257 AddDocTemplate(pDocTemplate);
259 // create room comments dialog
260 if (!(commentDlg = new CCommentDlg()))
263 loadWindowPos(commentDlg->pos, iniCDlg);
265 // create main MDI Frame window
266 CMainFrame* pMainFrame = new CMainFrame;
267 if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
269 m_pMainWnd = pMainFrame;
271 // Enable file manager drag/drop and DDE Execute open
273 RegisterShellFileTypes(TRUE);
275 // Parse command line for standard shell commands, DDE, file open
276 CCommandLineInfo cmdInfo;
277 ParseCommandLine(cmdInfo);
279 // Dispatch commands specified on the command line
280 if (!ProcessShellCommand(cmdInfo))
283 // The main window has been initialized, so show and update it.
285 loadWindowPos(pos, iniMainWin);
286 setWindowPos(pos, pMainFrame);
287 pMainFrame->ShowWindow(m_nCmdShow);
288 pMainFrame->UpdateWindow();
289 pMainFrame->DragAcceptFiles(TRUE);
291 setLandscape(); // Switch to landscape mode
293 // Load navigation options:
294 autoEdit = GetProfileInt(iniNavigation,iniNavEdit,true);
295 naviCopy = GetProfileInt(iniNavigation,iniNavCopy,true);
296 naviCRLF = GetProfileInt(iniNavigation,iniNavCRLF,false);
297 preferUnexplored = GetProfileInt(iniNavigation,iniNavStub,true);
301 } // end CMapApp::InitInstance
303 //--------------------------------------------------------------------
304 // Load a window position from the INI file:
307 // section: The title of the section to load it from
310 // pos: The window position
312 void CMapApp::loadWindowPos(CRect& pos, LPCTSTR section)
314 pos.left = GetProfileInt(section, iniWinLeft, 0);
315 pos.right = GetProfileInt(section, iniWinRight, 0);
316 pos.top = GetProfileInt(section, iniWinTop, 0);
317 pos.bottom = GetProfileInt(section, iniWinBottom, 0);
318 } // end CMapApp::loadWindowPos
320 //--------------------------------------------------------------------
321 // Set landscape mode as default:
323 // From KB: Q126897 (1-21-96)
325 void CMapApp::setLandscape()
327 // Get default printer settings.
330 pd.lStructSize = (DWORD) sizeof(PRINTDLG);
331 if (GetPrinterDeviceDefaults(&pd)) {
332 // Lock memory handle.
333 DEVMODE FAR* pDevMode =
334 (DEVMODE FAR*)::GlobalLock(m_hDevMode);
335 LPDEVNAMES lpDevNames;
336 LPTSTR lpszDriverName, lpszDeviceName, lpszPortName;
340 // Change printer settings in here.
341 pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
342 // Unlock memory handle.
343 lpDevNames = (LPDEVNAMES)GlobalLock(pd.hDevNames);
344 lpszDriverName = (LPTSTR )lpDevNames + lpDevNames->wDriverOffset;
345 lpszDeviceName = (LPTSTR )lpDevNames + lpDevNames->wDeviceOffset;
346 lpszPortName = (LPTSTR )lpDevNames + lpDevNames->wOutputOffset;
348 ::OpenPrinter(lpszDeviceName, &hPrinter, NULL);
349 ::DocumentProperties(NULL,hPrinter,lpszDeviceName,pDevMode,
350 pDevMode, DM_IN_BUFFER|DM_OUT_BUFFER);
352 // Sync the pDevMode.
353 // See SDK help for DocumentProperties for more info.
354 ::ClosePrinter(hPrinter);
355 ::GlobalUnlock(m_hDevNames);
356 ::GlobalUnlock(m_hDevMode);
359 } // end CMapApp::setLandscape
361 /////////////////////////////////////////////////////////////////////////////
364 int CMapApp::ExitInstance()
366 if (initialized) { // We finished initialization
367 WriteProfileInt(iniNavigation, iniNavEdit, autoEdit);
368 WriteProfileInt(iniNavigation, iniNavCopy, naviCopy);
369 WriteProfileInt(iniNavigation, iniNavCRLF, naviCRLF);
370 WriteProfileInt(iniNavigation, iniNavStub, preferUnexplored);
373 if (commentDlg->GetSafeHwnd())
374 OnViewComments(); // Destroy comment window
375 saveWindowPos(commentDlg->pos, iniCDlg);
378 if (findDlg && findDlg->GetSafeHwnd())
379 findDlg->DestroyWindow();
382 return CWinApp::ExitInstance();
383 } // end CMapApp::ExitInstance
385 //--------------------------------------------------------------------
386 // Save a window position to the INI file:
389 // pos: The window position
390 // section: The title of the section to save it in
392 void CMapApp::saveWindowPos(LPCRECT pos, LPCTSTR section)
394 WriteProfileInt(section, iniWinLeft, pos->left);
395 WriteProfileInt(section, iniWinRight, pos->right);
396 WriteProfileInt(section, iniWinTop, pos->top);
397 WriteProfileInt(section, iniWinBottom, pos->bottom);
398 } // end CMapApp::saveWindowPos
400 /////////////////////////////////////////////////////////////////////////////
401 // CAboutDlg dialog used for App About
403 class CAboutDlg : public CDialog
409 //{{AFX_DATA(CAboutDlg)
410 enum { IDD = IDD_ABOUTBOX };
414 // ClassWizard generated virtual function overrides
415 //{{AFX_VIRTUAL(CAboutDlg)
417 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
422 //{{AFX_MSG(CAboutDlg)
424 DECLARE_MESSAGE_MAP();
427 CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
429 //{{AFX_DATA_INIT(CAboutDlg)
434 void CAboutDlg::DoDataExchange(CDataExchange* pDX)
436 CDialog::DoDataExchange(pDX);
437 //{{AFX_DATA_MAP(CAboutDlg)
441 BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
442 //{{AFX_MSG_MAP(CAboutDlg)
446 // App command to run the dialog
447 void CMapApp::OnAppAbout()
451 } // end CMapApp::OnAppAbout
453 /////////////////////////////////////////////////////////////////////////////
455 //--------------------------------------------------------------------
456 // Find text in comments:
458 void CMapApp::OnEditFind()
461 VERIFY(findDlg = new CFindDlg(commentDlg));
462 if (!findDlg) return;
465 if (findDlg->GetSafeHwnd() != NULL) {
466 findDlg->SetActiveWindow();
467 findDlg->GotoDlgCtrl(findDlg->GetDlgItem(IDC_FIND_WHAT));
470 setWindowPos(findDlg->pos, findDlg);
472 } // end CMapApp::OnEditFind
474 //====================================================================
475 // The Room Comments dialog:
476 //--------------------------------------------------------------------
477 // Display or hide the free-floating Room Comments dialog:
479 void CMapApp::OnViewComments()
481 if (commentDlg->GetSafeHwnd() != NULL) {
482 commentDlg->GetWindowRect(&commentDlg->pos);
483 commentDlg->DestroyWindow();
485 commentDlg->Create();
486 setWindowPos(commentDlg->pos, commentDlg);
487 commentDlg->SetWindowText(commentDlg->title.empty()
488 ? _T("Room Comments")
489 : commentDlg->title.c_str());
490 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,WM_ENABLE,
491 commentDlg->fromView != NULL);
492 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,EM_SETREADONLY,
493 commentDlg->fromView == NULL);
494 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,EM_SETSEL, -1, -2);
496 } // end CMapApp::OnViewComments
498 void CMapApp::OnUpdateViewComments(CCmdUI* pCmdUI)
500 pCmdUI->SetCheck(commentDlg->GetSafeHwnd() != NULL);
501 } // end CMapApp::OnUpdateViewComments
503 //--------------------------------------------------------------------
504 // Move the Room Comments dialog so it doesn't obscure the map:
508 // The screen coordinates that should not be obscured
509 // Must be normalized
511 void CMapApp::adjustCommentPos(LPCRECT mapPos)
513 if (commentDlg->GetSafeHwnd()) {
515 commentDlg->GetWindowRect(&dlgPos);
516 if (r.IntersectRect(dlgPos, mapPos)) {
518 m_pMainWnd->GetClientRect(&mainPos);
519 m_pMainWnd->ClientToScreen(&mainPos);
522 static_cast<CMainFrame*>(m_pMainWnd)->MDIGetActive(&max);
524 curWin = curWin->GetTopWindow(); // Get the active view
525 curWin->GetClientRect(&r); // and make sure we don't
526 curWin->ClientToScreen(&r); // cover up the scroll bars
527 mainPos.bottom = r.bottom; // on the right and bottom
528 mainPos.right = r.right;
529 } // end if active window is maximized
530 dlgPos.OffsetRect(mainPos.right - dlgPos.right, // Top right
531 mainPos.top - dlgPos.top);
532 if (!r.IntersectRect(dlgPos, mapPos)) goto move;
533 dlgPos.OffsetRect(mainPos.right - dlgPos.right, // Bottom right
534 mainPos.bottom - dlgPos.bottom);
535 if (!r.IntersectRect(dlgPos, mapPos)) goto move;
536 dlgPos.OffsetRect(mainPos.left - dlgPos.left, // Bottom left
537 mainPos.bottom - dlgPos.bottom);
538 if (!r.IntersectRect(dlgPos, mapPos)) goto move;
539 dlgPos.OffsetRect(mainPos.left - dlgPos.left, // Top left
540 mainPos.top - dlgPos.top);
541 if (r.IntersectRect(dlgPos, mapPos)) return; // Give up
543 commentDlg->MoveWindow(dlgPos);
544 } // end if area is obscured
545 } // end if commentDlg is visible
546 } // end CMapApp::adjustCommentPos
548 //--------------------------------------------------------------------
549 // Disable comment dialog when closing its view:
552 // view: The view that is closing
554 void CMapApp::closingView(const CMapView* view)
556 if (commentDlg->fromView == view)
557 setComment(NULL, NULL);
558 } // end CMapApp::closingView
560 //--------------------------------------------------------------------
561 // Switch to the Room Comments dialog:
563 void CMapApp::editComment()
565 if (commentDlg->GetSafeHwnd() == NULL)
568 commentDlg->SetFocus();
569 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT, EM_SETSEL, -1, -2);
571 } // end CMapApp::editComment
573 //--------------------------------------------------------------------
574 // Update the Room Comments dialog:
578 // The map view which is posting this comment
579 // NULL means the view is closing, so disable the dialog
581 // The room whose comment should be displayed in the dialog
582 // NULL means no room is selected, so display map comment
583 // takeFocus: (default true)
584 // TRUE means to override a comment from a different view
586 void CMapApp::setComment(CMapView* fromView, const MapRoom* room,
587 bool takeFocus) const
589 if (!commentDlg->active &&
590 (takeFocus || (commentDlg->fromView == fromView))) {
591 commentDlg->fromView = fromView;
592 if (room && !(room->flags & rfCorner)) {
593 Strcpy(commentDlg->comment, room->note);
594 copyToOneLine(commentDlg->title, room->name);
595 commentDlg->roomComment = true;
598 const CMapDoc* doc = fromView->GetDocument();
601 Strcpy(commentDlg->comment, doc->getNote());
602 commentDlg->title = doc->getName();
603 if (!commentDlg->title.empty())
604 commentDlg->title += _T(" -- ");
606 commentDlg->comment.Empty();
607 commentDlg->title.erase();
608 } // end else no active view
609 commentDlg->title += _T("Map Comments");
610 commentDlg->roomComment = false;
611 } // end else no current room
612 if (commentDlg->GetSafeHwnd()) {
613 commentDlg->UpdateData(FALSE);
614 commentDlg->SetWindowText(commentDlg->title.empty()
615 ? _T("Room Comments")
616 : commentDlg->title.c_str());
617 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,WM_ENABLE,
619 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,EM_SETREADONLY,
621 } // end if comment dialog is open
622 } // end if we should update the comment
623 } // end CMapApp::setComment
625 //====================================================================
626 // CMapApp Options dialog:
627 //--------------------------------------------------------------------
628 // Bring up the Options dialog:
630 void CMapApp::OnViewSettings()
633 } // end CMapApp::OnViewSettings
635 //--------------------------------------------------------------------
636 // Bring up the Options dialog:
638 void CMapApp::editOptions()
640 CPropertySheet dlg(IDR_OPTIONS);
645 nav.editProps = autoEdit;
646 nav.stubs = (preferUnexplored ? 0 : 1);
649 if (dlg.DoModal() == IDOK) {
650 autoEdit = nav.editProps;
653 preferUnexplored = (nav.stubs == 0);
654 } // end if OK pressed
655 } // end CMapApp::editOptions