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 printf("FileOpen: container size = %li\n", container.objects.size());
122 drawing->document = container;
124 documentName = filename;
125 setWindowTitle(QString("Architektonas - %1").arg(documentName));
126 statusBar()->showMessage(tr("Drawing loaded."));
130 void ApplicationWindow::FileSave(void)
132 if (documentName.isEmpty())
133 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
134 "", tr("Architektonas drawings (*.drawing)"));
136 FILE * file = fopen(documentName.toAscii().data(), "w");
141 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
142 msg.setIcon(QMessageBox::Critical);
147 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
153 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
154 msg.setIcon(QMessageBox::Critical);
156 // In this case, we should unlink the created file, since it's not right...
157 unlink(documentName.toAscii().data());
161 setWindowTitle(QString("Architektonas - %1").arg(documentName));
162 statusBar()->showMessage(tr("Drawing saved."));
166 void ApplicationWindow::FileSaveAs(void)
168 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
169 documentName, tr("Architektonas drawings (*.drawing)"));
171 if (!filename.isEmpty())
173 documentName = filename;
179 void ApplicationWindow::SnapToGridTool(void)
181 Object::SetSnapMode(snapToGridAct->isChecked());
185 void ApplicationWindow::FixAngle(void)
187 Object::SetFixedAngle(fixAngleAct->isChecked());
191 void ApplicationWindow::FixLength(void)
193 Object::SetFixedLength(fixLengthAct->isChecked());
197 // We want certain tools to be exclusive, and this approach isn't working correctly...
198 void ApplicationWindow::DeleteTool(void)
201 ClearUIToolStatesExcept(deleteAct);
202 SetInternalToolStates();
206 void ApplicationWindow::DimensionTool(void)
208 ClearUIToolStatesExcept(addDimensionAct);
209 SetInternalToolStates();
213 void ApplicationWindow::RotateTool(void)
215 ClearUIToolStatesExcept(rotateAct);
216 SetInternalToolStates();
220 void ApplicationWindow::AddLineTool(void)
222 ClearUIToolStatesExcept(addLineAct);
223 SetInternalToolStates();
227 void ApplicationWindow::AddCircleTool(void)
229 ClearUIToolStatesExcept(addCircleAct);
230 SetInternalToolStates();
234 void ApplicationWindow::AddArcTool(void)
236 ClearUIToolStatesExcept(addArcAct);
237 SetInternalToolStates();
241 void ApplicationWindow::AddPolygonTool(void)
243 ClearUIToolStatesExcept(addPolygonAct);
244 SetInternalToolStates();
248 void ApplicationWindow::ZoomInTool(void)
250 double zoomFactor = 2.0;
252 We need to find the center of the screen, then figure out where the new corner
253 will be in the zoomed in window.
255 So we know in Qt coords, the center is found via:
256 size.width() / 2 --> xCenter
257 size.height() / 2 --> yCenter
259 transform x/yCenter to Cartesian coordinates. So far, so good.
261 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
262 (after subtracting from center, that is...)
264 QSize size = drawing->size();
265 Vector center(size.width() / 2.0, size.height() / 2.0);
266 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
267 center = Painter::QtToCartesianCoords(center);
268 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
269 Vector newOrigin = center - ((center - Painter::origin) / zoomFactor);
270 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
271 Painter::origin = newOrigin;
273 //printf("Zoom in... level going from %02f to ", Painter::zoom);
274 // This just zooms leaving origin intact... should zoom in at the current center! [DONE]
275 Painter::zoom *= zoomFactor;
276 drawing->gridSpacing = 12.0 / Painter::zoom;
277 zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
278 drawing->UpdateGridBackground();
283 void ApplicationWindow::ZoomOutTool(void)
288 origin = (223, 160.5)
289 newOrigin should be (-10, -10)
292 center - origin = (213, 150.5)
293 origin - center = (-213, -150.5)
295 + center = (-10, -10)
298 double zoomFactor = 2.0;
299 QSize size = drawing->size();
300 Vector center(size.width() / 2.0, size.height() / 2.0);
301 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
302 center = Painter::QtToCartesianCoords(center);
303 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
304 // Vector newOrigin = (center - Painter::origin) * zoomFactor;
305 // Vector newOrigin = center - (Painter::origin * zoomFactor);
306 Vector newOrigin = center + ((Painter::origin - center) * zoomFactor);
307 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
308 Painter::origin = newOrigin;
309 //printf("Zoom out...\n");
310 // This just zooms leaving origin intact... should zoom out at the current center! [DONE]
311 Painter::zoom /= zoomFactor;
312 drawing->gridSpacing = 12.0 / Painter::zoom;
313 zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
314 drawing->UpdateGridBackground();
319 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
321 if (exception != addArcAct)
322 addArcAct->setChecked(false);
324 if (exception != addCircleAct)
325 addCircleAct->setChecked(false);
327 if (exception != addDimensionAct)
328 addDimensionAct->setChecked(false);
330 if (exception != addLineAct)
331 addLineAct->setChecked(false);
333 if (exception != addPolygonAct)
334 addPolygonAct->setChecked(false);
336 if (exception != deleteAct)
337 deleteAct->setChecked(false);
339 if (exception != rotateAct)
340 rotateAct->setChecked(false);
344 void ApplicationWindow::SetInternalToolStates(void)
346 Object::SetDeleteActive(deleteAct->isChecked());
347 Object::SetDimensionActive(addDimensionAct->isChecked());
348 drawing->SetRotateToolActive(rotateAct->isChecked());
349 drawing->SetAddLineToolActive(addLineAct->isChecked());
350 drawing->SetAddCircleToolActive(addCircleAct->isChecked());
354 void ApplicationWindow::HelpAbout(void)
360 void ApplicationWindow::Settings(void)
362 SettingsDialog dlg(this);
363 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
365 if (dlg.exec() == false)
368 // Deal with stuff here (since user hit "OK" button...)
369 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
374 void ApplicationWindow::HandleGrouping(void)
376 // Group a bunch of selected objects together or ungroup a selected group.
380 void ApplicationWindow::CreateActions(void)
382 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
383 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
384 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
386 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);
387 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
389 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
390 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
391 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
393 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
394 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
395 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
397 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(), true);
398 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
400 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
401 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
403 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
404 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
406 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
407 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
409 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
410 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
412 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
413 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
415 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
416 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
418 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);
419 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
421 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
422 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
424 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
425 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
427 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+n")));
428 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
430 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")));
431 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
433 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")));
434 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
436 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")));
437 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
439 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+w")));
441 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/generic-tool.png"), QKeySequence());
442 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
444 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/generic-tool.png"), QKeySequence("g"));
445 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
447 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
448 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
449 /* QActionGroup * group = new QActionGroup(this);
450 group->addAction(deleteAct);
451 group->addAction(addDimensionAct);
452 group->addAction(addLineAct);
453 group->addAction(addCircleAct);
454 group->addAction(addArcAct);//*/
459 // Consolidates action creation from a multi-step process to a single-step one.
461 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
462 QIcon icon, QKeySequence key, bool checkable/*= false*/)
464 QAction * action = new QAction(icon, name, this);
465 action->setToolTip(tooltip);
466 action->setStatusTip(statustip);
467 action->setShortcut(key);
468 action->setCheckable(checkable);
475 // This is essentially the same as the previous function, but this allows more
476 // than one key sequence to be added as key shortcuts.
478 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
479 QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
481 QAction * action = new QAction(icon, name, this);
482 action->setToolTip(tooltip);
483 action->setStatusTip(statustip);
484 QList<QKeySequence> keyList;
485 keyList.append(key1);
486 keyList.append(key2);
487 action->setShortcuts(keyList);
488 action->setCheckable(checkable);
494 void ApplicationWindow::CreateMenus(void)
496 QMenu * menu = menuBar()->addMenu(tr("&File"));
497 menu->addAction(fileNewAct);
498 menu->addAction(fileOpenAct);
499 menu->addAction(fileSaveAct);
500 menu->addAction(fileSaveAsAct);
501 menu->addAction(fileCloseAct);
502 menu->addSeparator();
503 menu->addAction(exitAct);
505 menu = menuBar()->addMenu(tr("&View"));
506 menu->addAction(zoomInAct);
507 menu->addAction(zoomOutAct);
509 menu = menuBar()->addMenu(tr("&Edit"));
510 menu->addAction(snapToGridAct);
511 menu->addAction(groupAct);
512 menu->addAction(fixAngleAct);
513 menu->addAction(fixLengthAct);
514 menu->addAction(rotateAct);
515 menu->addSeparator();
516 menu->addAction(deleteAct);
517 menu->addSeparator();
518 menu->addAction(addLineAct);
519 menu->addAction(addCircleAct);
520 menu->addAction(addArcAct);
521 menu->addAction(addPolygonAct);
522 menu->addAction(addDimensionAct);
523 menu->addSeparator();
524 menu->addAction(settingsAct);
526 menu = menuBar()->addMenu(tr("&Help"));
527 menu->addAction(aboutAct);
531 void ApplicationWindow::CreateToolbars(void)
533 QToolBar * toolbar = addToolBar(tr("File"));
534 toolbar->addAction(exitAct);
536 toolbar = addToolBar(tr("View"));
537 toolbar->addAction(zoomInAct);
538 toolbar->addAction(zoomOutAct);
540 toolbar = addToolBar(tr("Edit"));
541 toolbar->addAction(snapToGridAct);
542 toolbar->addAction(groupAct);
543 toolbar->addAction(fixAngleAct);
544 toolbar->addAction(fixLengthAct);
545 toolbar->addAction(rotateAct);
546 toolbar->addAction(deleteAct);
547 toolbar->addSeparator();
548 toolbar->addAction(addLineAct);
549 toolbar->addAction(addCircleAct);
550 toolbar->addAction(addArcAct);
551 toolbar->addAction(addPolygonAct);
552 toolbar->addAction(addDimensionAct);
556 void ApplicationWindow::ReadSettings(void)
558 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
559 QSize size = settings.value("size", QSize(400, 400)).toSize();
560 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
561 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
564 // pos = settings.value("charWndPos", QPoint(0, 0)).toPoint();
565 // size = settings.value("charWndSize", QSize(200, 200)).toSize();
566 // ((TTEdit *)qApp)->charWnd->resize(size);
567 // ((TTEdit *)qApp)->charWnd->move(pos);
571 void ApplicationWindow::WriteSettings(void)
573 settings.setValue("pos", pos());
574 settings.setValue("size", size());
575 settings.setValue("useAntialiasing", drawing->useAntialiasing);
576 settings.setValue("snapToGrid", snapToGridAct->isChecked());
577 // settings.setValue("charWndPos", ((TTEdit *)qApp)->charWnd->pos());
578 // settings.setValue("charWndSize", ((TTEdit *)qApp)->charWnd->size());