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;
25 // Here's the main application loop--short and simple...
26 int main(int argc, char * argv[])
28 Q_INIT_RESOURCE(guemap); // This must the same name as the qrc filename
30 GUEMapApp app(argc, argv);
39 #include <winspool.h> // For printer settings
41 #include "MainFrm.hpp"
42 #include "ChildFrm.hpp"
43 #include "CommentDlg.hpp"
44 #include "FindDlg.hpp"
46 #include "MapView.hpp"
52 static char THIS_FILE[] = __FILE__;
55 bool GUEmapEatClicks = true;
56 HCURSOR handCursor = NULL;
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";
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";
71 /////////////////////////////////////////////////////////////////////////////
72 // Miscellaneous functions:
73 //--------------------------------------------------------------------
74 // Copy a string, replacing CRLF with space:
76 // Assumes that any CR is followed by a LF.
79 // source: The string to copy
82 // dest: The copied string, with any CRLF replaced by a space
84 void copyToOneLine(String& dest, const String& source)
88 while ((p = dest.find(_T('\r'),p)) != String::npos) {
92 } // end copyToOneLine
94 //--------------------------------------------------------------------
95 // Fill paragraphs in a string:
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.
102 // s: The string to fill
105 // s: The filled string
107 void fillParagraphs(String& s)
113 // Convert tabs to spaces (just in case):
114 while ((p = s.find(_T('\t'),p)) != String::npos)
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
122 while (s[p-1] == _T(' ')) {
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
129 ++p; // Don't remove this CRLF
130 } // end while more CRs
131 } // end fillParagraphs
133 //--------------------------------------------------------------------
134 // Set a window's position:
137 // pos: The new position of the window (in screen coordinates)
138 // w: The window to move
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.
144 void setWindowPos(const CRect& pos, CWnd* w)
146 if (pos.right && (pos.left < pos.right) && (pos.top < pos.bottom)) {
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))
155 } // end setWindowPos
157 //--------------------------------------------------------------------
158 // Trim whitespace from left of string:
160 void trimLeft(String& s)
162 StrIdx p = s.find_first_not_of(_T("\n\r\t "));
163 if (p > 0) s.erase(0, p);
166 //--------------------------------------------------------------------
167 // Trim whitespace from right of string:
169 void trimRight(String& s)
171 StrIdx p = s.find_last_not_of(_T("\n\r\t "));
172 if (p < s.length()) s.erase(p+1);
175 /////////////////////////////////////////////////////////////////////////////
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)
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)
195 /////////////////////////////////////////////////////////////////////////////
196 // CMapApp construction
204 // TODO: add construction code here,
205 // Place all significant initialization in InitInstance
213 } // end CMapApp::~CMapApp
215 /////////////////////////////////////////////////////////////////////////////
216 // The one and only CMapApp object
221 /////////////////////////////////////////////////////////////////////////////
222 // CMapApp initialization
224 BOOL CMapApp::InitInstance()
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.
232 Enable3dControls(); // Call this when using MFC in a shared DLL
234 Enable3dControlsStatic(); // Call this when linking to MFC statically
237 mapWinClass = AfxRegisterWndClass(
238 CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
239 LoadStandardCursor(IDC_ARROW), // Standard cursor
240 (HBRUSH)::GetStockObject(WHITE_BRUSH)
243 handCursor = LoadCursor(IDC_HAND);
245 LoadStdProfileSettings(9); // Load standard INI file options (including MRU)
247 // Register the application's document templates. Document templates
248 // serve as the connection between documents, frame windows and views.
250 CMultiDocTemplate* pDocTemplate;
251 pDocTemplate = new CMultiDocTemplate(
253 RUNTIME_CLASS(CMapDoc),
254 RUNTIME_CLASS(CChildFrame), // custom MDI child frame
255 RUNTIME_CLASS(CMapView));
256 AddDocTemplate(pDocTemplate);
258 // create room comments dialog
259 if (!(commentDlg = new CCommentDlg()))
262 loadWindowPos(commentDlg->pos, iniCDlg);
264 // create main MDI Frame window
265 CMainFrame* pMainFrame = new CMainFrame;
266 if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
268 m_pMainWnd = pMainFrame;
270 // Enable file manager drag/drop and DDE Execute open
272 RegisterShellFileTypes(TRUE);
274 // Parse command line for standard shell commands, DDE, file open
275 CCommandLineInfo cmdInfo;
276 ParseCommandLine(cmdInfo);
278 // Dispatch commands specified on the command line
279 if (!ProcessShellCommand(cmdInfo))
282 // The main window has been initialized, so show and update it.
284 loadWindowPos(pos, iniMainWin);
285 setWindowPos(pos, pMainFrame);
286 pMainFrame->ShowWindow(m_nCmdShow);
287 pMainFrame->UpdateWindow();
288 pMainFrame->DragAcceptFiles(TRUE);
290 setLandscape(); // Switch to landscape mode
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);
300 } // end CMapApp::InitInstance
302 //--------------------------------------------------------------------
303 // Load a window position from the INI file:
306 // section: The title of the section to load it from
309 // pos: The window position
311 void CMapApp::loadWindowPos(CRect& pos, LPCTSTR section)
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
319 //--------------------------------------------------------------------
320 // Set landscape mode as default:
322 // From KB: Q126897 (1-21-96)
324 void CMapApp::setLandscape()
326 // Get default printer settings.
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;
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;
347 ::OpenPrinter(lpszDeviceName, &hPrinter, NULL);
348 ::DocumentProperties(NULL,hPrinter,lpszDeviceName,pDevMode,
349 pDevMode, DM_IN_BUFFER|DM_OUT_BUFFER);
351 // Sync the pDevMode.
352 // See SDK help for DocumentProperties for more info.
353 ::ClosePrinter(hPrinter);
354 ::GlobalUnlock(m_hDevNames);
355 ::GlobalUnlock(m_hDevMode);
358 } // end CMapApp::setLandscape
360 /////////////////////////////////////////////////////////////////////////////
363 int CMapApp::ExitInstance()
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);
372 if (commentDlg->GetSafeHwnd())
373 OnViewComments(); // Destroy comment window
374 saveWindowPos(commentDlg->pos, iniCDlg);
377 if (findDlg && findDlg->GetSafeHwnd())
378 findDlg->DestroyWindow();
381 return CWinApp::ExitInstance();
382 } // end CMapApp::ExitInstance
384 //--------------------------------------------------------------------
385 // Save a window position to the INI file:
388 // pos: The window position
389 // section: The title of the section to save it in
391 void CMapApp::saveWindowPos(LPCRECT pos, LPCTSTR section)
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
399 /////////////////////////////////////////////////////////////////////////////
400 // CAboutDlg dialog used for App About
402 class CAboutDlg : public CDialog
408 //{{AFX_DATA(CAboutDlg)
409 enum { IDD = IDD_ABOUTBOX };
413 // ClassWizard generated virtual function overrides
414 //{{AFX_VIRTUAL(CAboutDlg)
416 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
421 //{{AFX_MSG(CAboutDlg)
423 DECLARE_MESSAGE_MAP();
426 CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
428 //{{AFX_DATA_INIT(CAboutDlg)
433 void CAboutDlg::DoDataExchange(CDataExchange* pDX)
435 CDialog::DoDataExchange(pDX);
436 //{{AFX_DATA_MAP(CAboutDlg)
440 BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
441 //{{AFX_MSG_MAP(CAboutDlg)
445 // App command to run the dialog
446 void CMapApp::OnAppAbout()
450 } // end CMapApp::OnAppAbout
452 /////////////////////////////////////////////////////////////////////////////
454 //--------------------------------------------------------------------
455 // Find text in comments:
457 void CMapApp::OnEditFind()
460 VERIFY(findDlg = new CFindDlg(commentDlg));
461 if (!findDlg) return;
464 if (findDlg->GetSafeHwnd() != NULL) {
465 findDlg->SetActiveWindow();
466 findDlg->GotoDlgCtrl(findDlg->GetDlgItem(IDC_FIND_WHAT));
469 setWindowPos(findDlg->pos, findDlg);
471 } // end CMapApp::OnEditFind
473 //====================================================================
474 // The Room Comments dialog:
475 //--------------------------------------------------------------------
476 // Display or hide the free-floating Room Comments dialog:
478 void CMapApp::OnViewComments()
480 if (commentDlg->GetSafeHwnd() != NULL) {
481 commentDlg->GetWindowRect(&commentDlg->pos);
482 commentDlg->DestroyWindow();
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);
495 } // end CMapApp::OnViewComments
497 void CMapApp::OnUpdateViewComments(CCmdUI* pCmdUI)
499 pCmdUI->SetCheck(commentDlg->GetSafeHwnd() != NULL);
500 } // end CMapApp::OnUpdateViewComments
502 //--------------------------------------------------------------------
503 // Move the Room Comments dialog so it doesn't obscure the map:
507 // The screen coordinates that should not be obscured
508 // Must be normalized
510 void CMapApp::adjustCommentPos(LPCRECT mapPos)
512 if (commentDlg->GetSafeHwnd()) {
514 commentDlg->GetWindowRect(&dlgPos);
515 if (r.IntersectRect(dlgPos, mapPos)) {
517 m_pMainWnd->GetClientRect(&mainPos);
518 m_pMainWnd->ClientToScreen(&mainPos);
521 static_cast<CMainFrame*>(m_pMainWnd)->MDIGetActive(&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
542 commentDlg->MoveWindow(dlgPos);
543 } // end if area is obscured
544 } // end if commentDlg is visible
545 } // end CMapApp::adjustCommentPos
547 //--------------------------------------------------------------------
548 // Disable comment dialog when closing its view:
551 // view: The view that is closing
553 void CMapApp::closingView(const CMapView* view)
555 if (commentDlg->fromView == view)
556 setComment(NULL, NULL);
557 } // end CMapApp::closingView
559 //--------------------------------------------------------------------
560 // Switch to the Room Comments dialog:
562 void CMapApp::editComment()
564 if (commentDlg->GetSafeHwnd() == NULL)
567 commentDlg->SetFocus();
568 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT, EM_SETSEL, -1, -2);
570 } // end CMapApp::editComment
572 //--------------------------------------------------------------------
573 // Update the Room Comments dialog:
577 // The map view which is posting this comment
578 // NULL means the view is closing, so disable the dialog
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
585 void CMapApp::setComment(CMapView* fromView, const MapRoom* room,
586 bool takeFocus) const
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;
597 const CMapDoc* doc = fromView->GetDocument();
600 Strcpy(commentDlg->comment, doc->getNote());
601 commentDlg->title = doc->getName();
602 if (!commentDlg->title.empty())
603 commentDlg->title += _T(" -- ");
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,
618 commentDlg->SendDlgItemMessage(IDC_CD_COMMENT,EM_SETREADONLY,
620 } // end if comment dialog is open
621 } // end if we should update the comment
622 } // end CMapApp::setComment
624 //====================================================================
625 // CMapApp Options dialog:
626 //--------------------------------------------------------------------
627 // Bring up the Options dialog:
629 void CMapApp::OnViewSettings()
632 } // end CMapApp::OnViewSettings
634 //--------------------------------------------------------------------
635 // Bring up the Options dialog:
637 void CMapApp::editOptions()
639 CPropertySheet dlg(IDR_OPTIONS);
644 nav.editProps = autoEdit;
645 nav.stubs = (preferUnexplored ? 0 : 1);
648 if (dlg.DoModal() == IDOK) {
649 autoEdit = nav.editProps;
652 preferUnexplored = (nav.stubs == 0);
653 } // end if OK pressed
654 } // end CMapApp::editOptions