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 // mainwin.cpp: implementation of the MainWin class
18 #include "roomwidget.h"
24 IMPLEMENT_DYNAMIC(CMainWin, CMDIFrameWnd)
26 BEGIN_MESSAGE_MAP(CMainWin, CMDIFrameWnd)
27 //{{AFX_MSG_MAP(CMainWin)
32 // Global help commands
33 ON_COMMAND(ID_HELP_FINDER, CMDIFrameWnd::OnHelpFinder)
34 ON_COMMAND(ID_HELP, CMDIFrameWnd::OnHelp)
35 ON_COMMAND(ID_CONTEXT_HELP, CMDIFrameWnd::OnContextHelp)
36 ON_COMMAND(ID_DEFAULT_HELP, CMDIFrameWnd::OnHelpFinder)
37 // Toggle the navigation bar:
38 ON_UPDATE_COMMAND_UI(ID_VIEW_NAVBAR, OnUpdateControlBarMenu)
39 ON_COMMAND_EX(ID_VIEW_NAVBAR, OnBarCheck)
42 static UINT indicators[] =
44 ID_SEPARATOR, // status line indicator
51 MainWin::MainWin(): settings("Underground Software", "GUEmap")
53 setCentralWidget(&mdiArea);
54 mdiArea.setViewMode(QMdiArea::TabbedView);
55 mdiArea.setTabsClosable(true);
57 setWindowIcon(QIcon(":/res/guemap.ico"));
58 setWindowTitle("GUEmap");
60 aboutWin = new AboutWindow(this);
66 undoAct->setEnabled(false);
67 statusBar()->showMessage(tr("Ready"));
71 // Create Dock widgets
72 QDockWidget * dock1 = new QDockWidget(tr("Rooms"), this);
73 /*RoomWidget * */rw = new RoomWidget;
75 addDockWidget(Qt::RightDockWidgetArea, dock1);
76 // QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
77 // BlockWidget * bw = new BlockWidget;
78 // dock2->setWidget(bw);
79 // addDockWidget(Qt::RightDockWidgetArea, dock2);
80 // QDockWidget * dock3 = new QDockWidget(tr("Object"), this);
81 // ObjectWidget * ow = new ObjectWidget;
82 // dock3->setWidget(ow);
83 // addDockWidget(Qt::RightDockWidgetArea, dock3);
84 // Needed for saveState()
85 dock1->setObjectName("Rooms");
86 // dock2->setObjectName("Blocks");
87 // dock3->setObjectName("Object");
90 // zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
91 // statusBar()->addPermanentWidget(zoomIndicator);
92 // statusBar()->showMessage(tr("Ready"));
95 // setUnifiedTitleAndToolBarOnMac(true);
96 // Global::font = new QFont("Verdana", 15, QFont::Bold);
98 // connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
99 // connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
100 // connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
102 // connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
106 void MainWin::closeEvent(QCloseEvent * event)
109 event->accept(); // Use ignore() if can't close for some reason
110 //Do we have a memory leak here if we don't delete the font in the Object???
113 void MainWin::FileNew(void)
115 MapView * map = new MapView;
116 map->setWindowTitle("Untitled");
117 map->clearSelection();
118 QMdiSubWindow * sw = mdiArea.addSubWindow(map);
119 mdiArea.setActiveSubWindow(sw);
123 connect(map, SIGNAL(RoomClicked(MapDoc *, int)), rw, SLOT(ShowInfo(MapDoc *, int)));
125 statusBar()->showMessage(tr("New map created."), 2000);
128 void MainWin::FileOpen(void)
130 QString filename = QFileDialog::getOpenFileName(this, tr("Open Map"), "",
131 tr("GUEmap files (*.gmp)"));
133 // User cancelled open
134 if (filename.isEmpty())
137 LoadFile(filename.toUtf8().data());
140 void MainWin::FileOpenRecent(void)
142 QAction * action = qobject_cast<QAction *>(sender());
147 LoadFile(action->data().toString());
150 void MainWin::LoadFile(QString filename)
152 MapView * map = new MapView;
153 bool successful = ReadFile(map->doc, filename.toUtf8().data());
157 // Make sure to delete any hanging objects in the container...
161 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
162 msg.setIcon(QMessageBox::Critical);
168 map->doc->filename = filename.toStdString();
169 map->setWindowTitle(map->doc->name.c_str());
170 map->clearSelection();
171 QMdiSubWindow * sw = mdiArea.addSubWindow(map);
172 mdiArea.setActiveSubWindow(sw);
176 connect(map, SIGNAL(RoomClicked(MapDoc *, int)), rw, SLOT(ShowInfo(MapDoc *, int)));
179 statusBar()->showMessage(tr("Map loaded."), 2000);
182 void MainWin::FileSaveBase(MapView * map, QString filename)
184 if (filename.endsWith(".gmp") == false)
187 bool successful = WriteFile(map->doc, filename.toUtf8().data());
191 // In this case, we should unlink the created file, since it's not
193 QFile::remove(filename);
196 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(filename));
197 msg.setIcon(QMessageBox::Critical);
202 map->doc->filename = filename.toStdString();
203 map->doc->isDirty = false;
204 statusBar()->showMessage(tr("Map saved."), 2000);
207 void MainWin::FileSave(void)
209 QMdiSubWindow * sw = mdiArea.activeSubWindow();
210 MapView * map = (MapView *)(sw->widget());
211 QString filename = map->doc->filename.c_str();
213 if (filename.isEmpty())
215 filename = QFileDialog::getSaveFileName(this, tr("Save Map"), "",
216 tr("GUEmap maps (*.gmp)"));
218 // Bail if the user clicked "Cancel"
219 if (filename.isEmpty())
223 FileSaveBase(map, filename);
226 void MainWin::FileSaveAs(void)
228 QMdiSubWindow * sw = mdiArea.activeSubWindow();
229 MapView * map = (MapView *)(sw->widget());
230 // QString filename = map->doc->filename.c_str();
232 QString filename = QFileDialog::getSaveFileName(this, tr("Save Map As"),
233 map->doc->filename.c_str(), tr("GUEmap maps (*.gmp)"));
235 // Bail if the user clicked "Cancel"
236 if (filename.isEmpty())
239 FileSaveBase(map, filename);
242 void MainWin::FileClose(void)
244 QMdiSubWindow * sw = mdiArea.activeSubWindow();
245 MapView * map = (MapView *)(sw->widget());
247 if (map->doc->isDirty)
250 msg.setText(tr("The map has been modified."));
251 msg.setInformativeText(tr("Do you want to save your changes?"));
252 msg.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
253 msg.setDefaultButton(QMessageBox::Save);
254 msg.setIcon(QMessageBox::Warning);
256 int response = msg.exec();
258 if (response == QMessageBox::Save)
263 statusBar()->showMessage(tr("Map closed."), 2000);
266 void MainWin::EditUndo(void)
268 QMdiSubWindow * sw = mdiArea.activeSubWindow();
273 MapView * map = (MapView *)(sw->widget());
278 if (map->doc->undoData != 0)
280 map->doc->setUndoData(map->doc->undoData->undo(*(map->doc)));
281 statusBar()->showMessage(tr("Undo successful."));
286 statusBar()->showMessage(tr("Ready."));
289 void MainWin::MenuFixUndo(void)
291 QMdiSubWindow * sw = mdiArea.activeSubWindow();
296 MapView * map = (MapView *)(sw->widget());
297 UndoRec * undo = map->doc->undoData;
301 undoAct->setText(QString(tr("&Undo %1")).arg(undo->getName()));
302 undoAct->setEnabled(true);
306 undoAct->setText(tr("Can't Undo"));
307 undoAct->setEnabled(false);
311 void MainWin::EditDelete(void)
313 QMdiSubWindow * sw = mdiArea.activeSubWindow();
318 MapView * map = (MapView *)(sw->widget());
323 map->deleteSelection();
327 void MainWin::EditSelectAll(void)
329 QMdiSubWindow * sw = mdiArea.activeSubWindow();
334 MapView * map = (MapView *)(sw->widget());
339 for(int i=map->doc->room.size()-1; i>=0; i--)
345 void MainWin::HelpAbout(void)
350 void MainWin::CreateActions(void)
352 fileNewAct = CreateAction(tr("&New Map"), tr("New Map"), tr("Creates a new map."), QIcon(":/res/file-new.png"), QKeySequence(tr("Ctrl+n")));
353 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
355 fileOpenAct = CreateAction(tr("&Open Map"), tr("Open Map"), tr("Opens an existing map from a file."), QIcon(":/res/file-open.png"), QKeySequence(tr("Ctrl+o")));
356 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
358 fileSaveAct = CreateAction(tr("&Save Map"), tr("Save Map"), tr("Saves the current map to a file."), QIcon(":/res/file-save.png"), QKeySequence(tr("Ctrl+s")));
359 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
361 fileSaveAsAct = CreateAction(tr("Save Map &As"), tr("Save As"), tr("Saves the current map to a file with a different name."), QIcon(":/res/file-save-as.png"), QKeySequence(tr("Ctrl+Shift+s")));
362 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
364 fileCloseAct = CreateAction(tr("&Close Map"), tr("Close Map"), tr("Closes the current map."), QIcon(":/res/file-close.png"), QKeySequence(tr("Ctrl+c")));
365 connect(fileCloseAct, SIGNAL(triggered()), this, SLOT(FileClose()));
367 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."), QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
368 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
370 undoAct = CreateAction(tr("&Undo"), tr("Undo"), tr("Undoes the last action."), QIcon(), QKeySequence(tr("Ctrl+z")));
371 connect(undoAct, SIGNAL(triggered()), this, SLOT(EditUndo()));
373 deleteAct = CreateAction(tr("&Delete"), tr("Delete"), tr("Deletes the selected items."), QIcon(), QKeySequence(tr("Delete")));
374 connect(deleteAct, SIGNAL(triggered()), this, SLOT(EditDelete()));
376 selectAllAct = CreateAction(tr("Select &All"), tr("Select All"), tr("Selects all items."), QIcon(), QKeySequence(tr("Ctrl+a")));
377 connect(selectAllAct, SIGNAL(triggered()), this, SLOT(EditSelectAll()));
379 aboutAct = CreateAction(tr("About &GUEmap"), tr("About GUEmap"), tr("Gives information about this program."), QIcon(), QKeySequence());
380 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
382 for(int i=0; i<MRU_MAX; i++)
384 QAction * rfa = new QAction(this);
385 rfa->setVisible(false);
386 connect(rfa, SIGNAL(triggered()), this, SLOT(FileOpenRecent()));
391 void MainWin::CreateMenus(void)
393 QMenu * menu = menuBar()->addMenu(tr("&File"));
394 menu->addAction(fileNewAct);
395 menu->addAction(fileOpenAct);
397 QMenu * recentMenu = menu->addMenu(tr("Open &Recent"));
399 for(int i=0; i<MRU_MAX; i++)
400 recentMenu->addAction(mruAct.at(i));
402 UpdateMRUActionList();
404 menu->addAction(fileSaveAct);
405 menu->addAction(fileSaveAsAct);
406 menu->addAction(fileCloseAct);
407 menu->addSeparator();
408 menu->addAction(exitAct);
410 menu = menuBar()->addMenu(tr("&Edit"));
411 menu->addAction(undoAct);
412 connect(menu, SIGNAL(aboutToShow()), this, SLOT(MenuFixUndo()));
413 menu->addSeparator();
414 menu->addAction(deleteAct);
415 menu->addAction(selectAllAct);
417 menu = menuBar()->addMenu(tr("&View"));
418 menu->addAction(zoomInAct);
419 menu->addAction(zoomOutAct);
422 menu->addAction(snapToGridAct);
423 menu->addAction(groupAct);
424 menu->addAction(fixAngleAct);
425 menu->addAction(fixLengthAct);
426 menu->addAction(rotateAct);
427 menu->addAction(mirrorAct);
428 menu->addAction(trimAct);
429 menu->addAction(triangulateAct);
430 menu->addAction(connectAct);
431 menu->addAction(disconnectAct);
432 menu->addSeparator();
433 menu->addAction(addLineAct);
434 menu->addAction(addCircleAct);
435 menu->addAction(addArcAct);
436 menu->addAction(addPolygonAct);
437 menu->addAction(addSplineAct);
438 menu->addAction(addDimensionAct);
439 menu->addSeparator();
440 menu->addAction(settingsAct);
443 menu = menuBar()->addMenu(tr("&Help"));
444 menu->addAction(aboutAct);
447 void MainWin::CreateToolbars(void)
450 QToolBar * toolbar = addToolBar(tr("File"));
451 toolbar->setObjectName("File"); // Needed for saveState()
452 toolbar->addAction(fileNewAct);
453 toolbar->addAction(fileOpenAct);
454 toolbar->addAction(fileSaveAct);
455 toolbar->addAction(fileSaveAsAct);
456 toolbar->addAction(fileCloseAct);
457 // toolbar->addAction(exitAct);
459 toolbar = addToolBar(tr("View"));
460 toolbar->setObjectName("View");
461 toolbar->addAction(zoomInAct);
462 toolbar->addAction(zoomOutAct);
464 QSpinBox * spinbox = new QSpinBox;
465 toolbar->addWidget(spinbox);
466 // QLineEdit * lineedit = new QLineEdit;
467 toolbar->addWidget(baseUnitInput);
468 toolbar->addWidget(dimensionSizeInput);
470 toolbar = addToolBar(tr("Edit"));
471 toolbar->setObjectName("Edit");
472 toolbar->addAction(snapToGridAct);
473 toolbar->addAction(groupAct);
474 toolbar->addAction(fixAngleAct);
475 toolbar->addAction(fixLengthAct);
476 toolbar->addAction(rotateAct);
477 toolbar->addAction(mirrorAct);
478 toolbar->addAction(trimAct);
479 toolbar->addAction(triangulateAct);
480 toolbar->addAction(deleteAct);
481 toolbar->addAction(connectAct);
482 toolbar->addAction(disconnectAct);
483 toolbar->addSeparator();
484 toolbar->addAction(addLineAct);
485 toolbar->addAction(addCircleAct);
486 toolbar->addAction(addArcAct);
487 toolbar->addAction(addPolygonAct);
488 toolbar->addAction(addSplineAct);
489 toolbar->addAction(addDimensionAct);
491 spinbox->setRange(4, 256);
492 spinbox->setValue(12);
493 baseUnitInput->setText("12");
494 connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(HandleGridSizeInPixels(int)));
495 connect(baseUnitInput, SIGNAL(textChanged(QString)), this, SLOT(HandleGridSizeInBaseUnits(QString)));
496 connect(dimensionSizeInput, SIGNAL(textChanged(QString)), this, SLOT(HandleDimensionSize(QString)));
498 PenWidget * pw = new PenWidget();
499 toolbar = addToolBar(tr("Pen"));
500 toolbar->setObjectName(tr("Pen"));
501 toolbar->addWidget(pw);
502 connect(drawing, SIGNAL(ObjectSelected(Object *)), pw, SLOT(SetFields(Object *)));
503 connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float)));
504 connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int)));
505 connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t)));
509 void MainWin::ReadSettings(void)
511 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
512 QSize size = settings.value("size", QSize(400, 400)).toSize();
513 // drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
514 // snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
517 restoreState(settings.value("windowState").toByteArray());
520 void MainWin::WriteSettings(void)
522 settings.setValue("pos", pos());
523 settings.setValue("size", size());
524 settings.setValue("windowState", saveState());
525 // settings.setValue("useAntialiasing", drawing->useAntialiasing);
526 // settings.setValue("snapToGrid", snapToGridAct->isChecked());
529 void MainWin::UpdateMRUActionList(void)
531 QStringList mruFilePaths = settings.value("recentFiles").toStringList();
533 int mruSize = (mruFilePaths.size() <= MRU_MAX ? mruFilePaths.size() : MRU_MAX);
535 for(int i=0; i<mruSize; i++)
537 QString filename = QFileInfo(mruFilePaths.at(i)).fileName();
538 mruAct.at(i)->setText(filename);
539 mruAct.at(i)->setData(mruFilePaths.at(i));
540 mruAct.at(i)->setVisible(true);
543 for(int i=mruSize; i<MRU_MAX; i++)
544 mruAct.at(i)->setVisible(false);
547 void MainWin::AdjustMRU(const QString & filePath)
549 documentName = filePath;
550 setWindowFilePath(documentName);
552 QStringList mruFilePaths = settings.value("recentFiles").toStringList();
553 mruFilePaths.removeAll(filePath);
554 mruFilePaths.prepend(filePath);
556 while (mruFilePaths.size() > MRU_MAX)
557 mruFilePaths.removeLast();
559 settings.setValue("recentFiles", mruFilePaths);
561 UpdateMRUActionList();
565 int MainWin::OnCreate(LPCREATESTRUCT lpCreateStruct)
567 if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
570 if (!m_wndToolBar.Create(this) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
572 TRACE0("Failed to create toolbar\n");
573 return -1; // fail to create
576 if (!wndNavBar.Create(this, IDD_NAVBAR, CBRS_TOP, ID_VIEW_NAVBAR))
578 TRACE0("Failed to create navigation bar\n");
579 return -1; // fail to create
582 if (!m_wndStatusBar.Create(this)
583 || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators) / sizeof(UINT)))
585 TRACE0("Failed to create status bar\n");
586 return -1; // fail to create
590 // Add tool tips and resizeable toolbar:
591 m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
592 // Make toolbar dockable:
593 m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
594 EnableDocking(CBRS_ALIGN_ANY);
595 DockControlBar(&m_wndToolBar);
597 wndNavBar.SetBarStyle(wndNavBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY);
598 wndNavBar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);
599 DockControlBarLeftOf(&wndNavBar, &m_wndToolBar);
601 // Restore toolbar state:
603 state.LoadState("Toolbars");
609 void MainWin::OnDestroy()
614 state.SaveState("Toolbars");
617 GetWindowPlacement(&pos);
618 static_cast<CMapApp *>(AfxGetApp())->saveWindowPos(&pos.rcNormalPosition, iniMainWin);
620 CMDIFrameWnd::OnDestroy();
623 bool MainWin::PreCreateWindow(CREATESTRUCT & cs)
625 // TODO: Modify the Window class or styles here by modifying
626 // the CREATESTRUCT cs
628 return CMDIFrameWnd::PreCreateWindow(cs);
631 /////////////////////////////////////////////////////////////////////////////
632 // CMainWin message handlers
633 //--------------------------------------------------------------------
634 // Keep track of whether we should be eating mouse clicks:
636 void MainWin::OnActivateApp(bool bActive, HTASK task)
638 GUEmapEatClicks = !bActive;
639 CMDIFrameWnd::OnActivateApp(bActive, task);
642 /////////////////////////////////////////////////////////////////////////////
643 // Dock control bars on the same line:
645 // Taken from \MSDev\Samples\MFC\General\DockTool\MainFrm.cpp
647 void MainWin::DockControlBarLeftOf(CControlBar * Bar, CControlBar * LeftOf)
653 // get MFC to adjust the dimensions of all docked ToolBars
654 // so that GetWindowRect will be accurate
656 LeftOf->GetWindowRect(&rect);
657 rect.OffsetRect(1, 0);
658 dw = LeftOf->GetBarStyle();
661 n = (dw & CBRS_ALIGN_TOP ? AFX_IDW_DOCKBAR_TOP : n);
662 n = (dw & CBRS_ALIGN_BOTTOM && n == 0 ? AFX_IDW_DOCKBAR_BOTTOM : n);
663 n = (dw & CBRS_ALIGN_LEFT && n == 0 ? AFX_IDW_DOCKBAR_LEFT : n);
664 n = (dw & CBRS_ALIGN_RIGHT && n == 0 ? AFX_IDW_DOCKBAR_RIGHT : n);
666 // When we take the default parameters on rect, DockControlBar will dock
667 // each Toolbar on a seperate line. By calculating a rectangle, we in effect
668 // are simulating a Toolbar being dragged to that location and docked.
669 DockControlBar(Bar, n, &rect);