2 // applicationwindow.cpp: Architektonas
4 // Part of the Architektonas Project
5 // (C) 2011 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- -------------------------------------------------------------
12 // JLH 03/22/2011 Created this file
13 // JLH 09/29/2011 Added simple zoom in/out functionality
14 // JLH 10/03/2011 Fixed zoom tool to zoom in/out from center of screen
24 // Uncomment this for debugging...
26 //#define DEBUGFOO // Various tool debugging...
27 //#define DEBUGTP // Toolpalette debugging...
29 #include "applicationwindow.h"
32 #include "drawingview.h"
34 #include "generaltab.h"
36 #include "settingsdialog.h"
39 ApplicationWindow::ApplicationWindow(): settings("Underground Software", "Architektonas")
41 drawing = new DrawingView(this);
42 drawing->setMouseTracking(true); // We want *all* mouse events...!
43 setCentralWidget(drawing);
45 aboutWin = new AboutWindow(this);
47 // ((TTEdit *)qApp)->charWnd = new CharWindow(this);
49 setWindowIcon(QIcon(":/res/atns-icon.png"));
50 setWindowTitle("Architektonas");
57 zoomIndicator = new QLabel("Grid: 12.0\" Zoom: 12.5%");
58 statusBar()->addPermanentWidget(zoomIndicator);
59 statusBar()->showMessage(tr("Ready"));
63 // connect(textEdit->document(), SIGNAL(contentsChanged()),
64 // this, SLOT(documentWasModified()));
66 // setCurrentFile("");
67 setUnifiedTitleAndToolBarOnMac(true);
69 // ((TTEdit *)qApp)->charWnd->show();//eh?
70 Object::SetFont(new QFont("Verdana", 15, QFont::Bold));
74 void ApplicationWindow::closeEvent(QCloseEvent * event)
77 event->accept(); // Use ignore() if can't close for some reason
78 //Do we have a memory leak here if we don't delete the font in the Object???
82 void ApplicationWindow::FileNew(void)
84 // Should warn the user if drawing hasn't been saved...
85 drawing->document.Clear();
88 setWindowTitle("Architektonas - Untitled");
89 statusBar()->showMessage(tr("New drawing is ready."));
93 void ApplicationWindow::FileOpen(void)
95 QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
96 "", tr("Architektonas files (*.drawing)"));
97 FILE * file = fopen(filename.toAscii().data(), "r");
102 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
103 msg.setIcon(QMessageBox::Critical);
108 Container container(Vector(0, 0));
109 bool successful = FileIO::LoadAtnsFile(file, &container);
115 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
116 msg.setIcon(QMessageBox::Critical);
121 drawing->document = container;
123 documentName = filename;
124 setWindowTitle(QString("Architektonas - %1").arg(documentName));
125 statusBar()->showMessage(tr("Drawing loaded."));
129 void ApplicationWindow::FileSave(void)
131 if (documentName.isEmpty())
132 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
133 "", tr("Architektonas drawings (*.drawing)"));
135 FILE * file = fopen(documentName.toAscii().data(), "w");
140 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
141 msg.setIcon(QMessageBox::Critical);
146 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
152 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
153 msg.setIcon(QMessageBox::Critical);
155 // In this case, we should unlink the created file, since it's not right...
156 unlink(documentName.toAscii().data());
160 setWindowTitle(QString("Architektonas - %1").arg(documentName));
161 statusBar()->showMessage(tr("Drawing saved."));
165 void ApplicationWindow::FileSaveAs(void)
167 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
168 documentName, tr("Architektonas drawings (*.drawing)"));
170 if (!filename.isEmpty())
172 documentName = filename;
178 void ApplicationWindow::SnapToGridTool(void)
180 Object::SetSnapMode(snapToGridAct->isChecked());
184 void ApplicationWindow::FixAngle(void)
186 Object::SetFixedAngle(fixAngleAct->isChecked());
190 void ApplicationWindow::FixLength(void)
192 Object::SetFixedLength(fixLengthAct->isChecked());
196 // We want certain tools to be exclusive, and this approach isn't working correctly...
197 void ApplicationWindow::DeleteTool(void)
200 ClearUIToolStatesExcept(deleteAct);
201 SetInternalToolStates();
205 void ApplicationWindow::DimensionTool(void)
207 ClearUIToolStatesExcept(addDimensionAct);
208 SetInternalToolStates();
212 void ApplicationWindow::RotateTool(void)
214 ClearUIToolStatesExcept(rotateAct);
215 SetInternalToolStates();
219 void ApplicationWindow::AddLineTool(void)
221 ClearUIToolStatesExcept(addLineAct);
222 SetInternalToolStates();
226 void ApplicationWindow::AddCircleTool(void)
228 ClearUIToolStatesExcept(addCircleAct);
229 SetInternalToolStates();
233 void ApplicationWindow::AddArcTool(void)
235 ClearUIToolStatesExcept(addArcAct);
236 SetInternalToolStates();
240 void ApplicationWindow::AddPolygonTool(void)
242 ClearUIToolStatesExcept(addPolygonAct);
243 SetInternalToolStates();
247 void ApplicationWindow::ZoomInTool(void)
249 double zoomFactor = 2.0;
251 We need to find the center of the screen, then figure out where the new corner
252 will be in the zoomed in window.
254 So we know in Qt coords, the center is found via:
255 size.width() / 2 --> xCenter
256 size.height() / 2 --> yCenter
258 transform x/yCenter to Cartesian coordinates. So far, so good.
260 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
261 (after subtracting from center, that is...)
263 QSize size = drawing->size();
264 Vector center(size.width() / 2.0, size.height() / 2.0);
265 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
266 center = Painter::QtToCartesianCoords(center);
267 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
268 Vector newOrigin = center - ((center - Painter::origin) / zoomFactor);
269 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
270 Painter::origin = newOrigin;
272 //printf("Zoom in... level going from %02f to ", Painter::zoom);
273 // This just zooms leaving origin intact... should zoom in at the current center! [DONE]
274 Painter::zoom *= zoomFactor;
275 drawing->gridSpacing = 12.0 / Painter::zoom;
276 zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
277 drawing->UpdateGridBackground();
282 void ApplicationWindow::ZoomOutTool(void)
287 origin = (223, 160.5)
288 newOrigin should be (-10, -10)
291 center - origin = (213, 150.5)
292 origin - center = (-213, -150.5)
294 + center = (-10, -10)
297 double zoomFactor = 2.0;
298 QSize size = drawing->size();
299 Vector center(size.width() / 2.0, size.height() / 2.0);
300 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
301 center = Painter::QtToCartesianCoords(center);
302 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
303 // Vector newOrigin = (center - Painter::origin) * zoomFactor;
304 // Vector newOrigin = center - (Painter::origin * zoomFactor);
305 Vector newOrigin = center + ((Painter::origin - center) * zoomFactor);
306 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
307 Painter::origin = newOrigin;
308 //printf("Zoom out...\n");
309 // This just zooms leaving origin intact... should zoom out at the current center! [DONE]
310 Painter::zoom /= zoomFactor;
311 drawing->gridSpacing = 12.0 / Painter::zoom;
312 zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
313 drawing->UpdateGridBackground();
318 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
320 if (exception != addArcAct)
321 addArcAct->setChecked(false);
323 if (exception != addCircleAct)
324 addCircleAct->setChecked(false);
326 if (exception != addDimensionAct)
327 addDimensionAct->setChecked(false);
329 if (exception != addLineAct)
330 addLineAct->setChecked(false);
332 if (exception != addPolygonAct)
333 addPolygonAct->setChecked(false);
335 if (exception != deleteAct)
336 deleteAct->setChecked(false);
338 if (exception != rotateAct)
339 rotateAct->setChecked(false);
343 void ApplicationWindow::SetInternalToolStates(void)
345 Object::SetDeleteActive(deleteAct->isChecked());
346 Object::SetDimensionActive(addDimensionAct->isChecked());
347 drawing->SetRotateToolActive(rotateAct->isChecked());
348 drawing->SetAddLineToolActive(addLineAct->isChecked());
349 drawing->SetAddCircleToolActive(addCircleAct->isChecked());
353 void ApplicationWindow::HelpAbout(void)
359 void ApplicationWindow::Settings(void)
361 SettingsDialog dlg(this);
362 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
364 if (dlg.exec() == false)
367 // Deal with stuff here (since user hit "OK" button...)
368 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
373 void ApplicationWindow::HandleGrouping(void)
375 // Group a bunch of selected objects together or ungroup a selected group.
379 void ApplicationWindow::CreateActions(void)
381 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
382 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
383 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
385 snapToGridAct = CreateAction(tr("Snap To &Grid"), tr("Snap To Grid"), tr("Snaps mouse cursor to the visible grid when moving/creating objects."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("S")), true);
386 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
388 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
389 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
390 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
392 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
393 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
394 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
396 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(), true);
397 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
399 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
400 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
402 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
403 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
405 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
406 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
408 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
409 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
411 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
412 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
414 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
415 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
417 rotateAct = CreateAction(tr("&Rotate Objects"), tr("Rotate"), tr("Rotate object(s) around an arbitrary center."), QIcon(":/res/rotate-tool.png"), QKeySequence(tr("R,O")), true);
418 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
420 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
421 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
423 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
424 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
426 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+n")));
427 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
429 fileOpenAct = CreateAction(tr("&Open Drawing"), tr("Open Drawing"), tr("Opens an existing drawing from a file."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+o")));
430 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
432 fileSaveAct = CreateAction(tr("&Save Drawing"), tr("Save Drawing"), tr("Saves the current drawing to a file."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+s")));
433 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
435 fileSaveAsAct = CreateAction(tr("Save Drawing &As"), tr("Save As"), tr("Saves the current drawing to a file with a different name."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+Shift+s")));
436 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
438 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+w")));
440 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/generic-tool.png"), QKeySequence());
441 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
443 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/generic-tool.png"), QKeySequence("g"));
444 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
446 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
447 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
448 /* QActionGroup * group = new QActionGroup(this);
449 group->addAction(deleteAct);
450 group->addAction(addDimensionAct);
451 group->addAction(addLineAct);
452 group->addAction(addCircleAct);
453 group->addAction(addArcAct);//*/
458 // Consolidates action creation from a multi-step process to a single-step one.
460 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
461 QIcon icon, QKeySequence key, bool checkable/*= false*/)
463 QAction * action = new QAction(icon, name, this);
464 action->setToolTip(tooltip);
465 action->setStatusTip(statustip);
466 action->setShortcut(key);
467 action->setCheckable(checkable);
474 // This is essentially the same as the previous function, but this allows more
475 // than one key sequence to be added as key shortcuts.
477 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
478 QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
480 QAction * action = new QAction(icon, name, this);
481 action->setToolTip(tooltip);
482 action->setStatusTip(statustip);
483 QList<QKeySequence> keyList;
484 keyList.append(key1);
485 keyList.append(key2);
486 action->setShortcuts(keyList);
487 action->setCheckable(checkable);
493 void ApplicationWindow::CreateMenus(void)
495 QMenu * menu = menuBar()->addMenu(tr("&File"));
496 menu->addAction(fileNewAct);
497 menu->addAction(fileOpenAct);
498 menu->addAction(fileSaveAct);
499 menu->addAction(fileSaveAsAct);
500 menu->addAction(fileCloseAct);
501 menu->addSeparator();
502 menu->addAction(exitAct);
504 menu = menuBar()->addMenu(tr("&View"));
505 menu->addAction(zoomInAct);
506 menu->addAction(zoomOutAct);
508 menu = menuBar()->addMenu(tr("&Edit"));
509 menu->addAction(snapToGridAct);
510 menu->addAction(groupAct);
511 menu->addAction(fixAngleAct);
512 menu->addAction(fixLengthAct);
513 menu->addAction(rotateAct);
514 menu->addSeparator();
515 menu->addAction(deleteAct);
516 menu->addSeparator();
517 menu->addAction(addLineAct);
518 menu->addAction(addCircleAct);
519 menu->addAction(addArcAct);
520 menu->addAction(addPolygonAct);
521 menu->addAction(addDimensionAct);
522 menu->addSeparator();
523 menu->addAction(settingsAct);
525 menu = menuBar()->addMenu(tr("&Help"));
526 menu->addAction(aboutAct);
530 void ApplicationWindow::CreateToolbars(void)
532 QToolBar * toolbar = addToolBar(tr("File"));
533 toolbar->addAction(exitAct);
535 toolbar = addToolBar(tr("View"));
536 toolbar->addAction(zoomInAct);
537 toolbar->addAction(zoomOutAct);
539 toolbar = addToolBar(tr("Edit"));
540 toolbar->addAction(snapToGridAct);
541 toolbar->addAction(groupAct);
542 toolbar->addAction(fixAngleAct);
543 toolbar->addAction(fixLengthAct);
544 toolbar->addAction(rotateAct);
545 toolbar->addAction(deleteAct);
546 toolbar->addSeparator();
547 toolbar->addAction(addLineAct);
548 toolbar->addAction(addCircleAct);
549 toolbar->addAction(addArcAct);
550 toolbar->addAction(addPolygonAct);
551 toolbar->addAction(addDimensionAct);
555 void ApplicationWindow::ReadSettings(void)
557 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
558 QSize size = settings.value("size", QSize(400, 400)).toSize();
559 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
560 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
563 // pos = settings.value("charWndPos", QPoint(0, 0)).toPoint();
564 // size = settings.value("charWndSize", QSize(200, 200)).toSize();
565 // ((TTEdit *)qApp)->charWnd->resize(size);
566 // ((TTEdit *)qApp)->charWnd->move(pos);
570 void ApplicationWindow::WriteSettings(void)
572 settings.setValue("pos", pos());
573 settings.setValue("size", size());
574 settings.setValue("useAntialiasing", drawing->useAntialiasing);
575 settings.setValue("snapToGrid", snapToGridAct->isChecked());
576 // settings.setValue("charWndPos", ((TTEdit *)qApp)->charWnd->pos());
577 // settings.setValue("charWndSize", ((TTEdit *)qApp)->charWnd->size());