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());
350 // We can be sure that if we've come here, then either an active tool is
351 // being deactivated, or a new tool is being created. In either case, the
352 // old tool needs to be deleted.
353 if (drawing->toolAction)
355 delete drawing->toolAction;
356 drawing->toolAction = NULL;
359 drawing->SetAddLineToolActive(addLineAct->isChecked());
360 drawing->SetAddCircleToolActive(addCircleAct->isChecked());
361 drawing->SetAddDimensionToolActive(addDimensionAct->isChecked());
365 void ApplicationWindow::HelpAbout(void)
371 void ApplicationWindow::Settings(void)
373 SettingsDialog dlg(this);
374 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
376 if (dlg.exec() == false)
379 // Deal with stuff here (since user hit "OK" button...)
380 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
385 void ApplicationWindow::HandleGrouping(void)
387 // Group a bunch of selected objects together or ungroup a selected group.
391 void ApplicationWindow::CreateActions(void)
393 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
394 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
395 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
397 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);
398 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
400 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
401 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
402 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
404 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
405 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
406 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
408 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(), true);
409 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
411 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
412 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
414 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
415 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
417 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
418 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
420 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
421 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
423 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
424 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
426 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
427 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
429 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);
430 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
432 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
433 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
435 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
436 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
438 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+n")));
439 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
441 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")));
442 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
444 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")));
445 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
447 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")));
448 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
450 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+w")));
452 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/generic-tool.png"), QKeySequence());
453 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
455 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/generic-tool.png"), QKeySequence("g"));
456 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
458 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
459 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
460 /* QActionGroup * group = new QActionGroup(this);
461 group->addAction(deleteAct);
462 group->addAction(addDimensionAct);
463 group->addAction(addLineAct);
464 group->addAction(addCircleAct);
465 group->addAction(addArcAct);//*/
470 // Consolidates action creation from a multi-step process to a single-step one.
472 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
473 QIcon icon, QKeySequence key, bool checkable/*= false*/)
475 QAction * action = new QAction(icon, name, this);
476 action->setToolTip(tooltip);
477 action->setStatusTip(statustip);
478 action->setShortcut(key);
479 action->setCheckable(checkable);
486 // This is essentially the same as the previous function, but this allows more
487 // than one key sequence to be added as key shortcuts.
489 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
490 QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
492 QAction * action = new QAction(icon, name, this);
493 action->setToolTip(tooltip);
494 action->setStatusTip(statustip);
495 QList<QKeySequence> keyList;
496 keyList.append(key1);
497 keyList.append(key2);
498 action->setShortcuts(keyList);
499 action->setCheckable(checkable);
505 void ApplicationWindow::CreateMenus(void)
507 QMenu * menu = menuBar()->addMenu(tr("&File"));
508 menu->addAction(fileNewAct);
509 menu->addAction(fileOpenAct);
510 menu->addAction(fileSaveAct);
511 menu->addAction(fileSaveAsAct);
512 menu->addAction(fileCloseAct);
513 menu->addSeparator();
514 menu->addAction(exitAct);
516 menu = menuBar()->addMenu(tr("&View"));
517 menu->addAction(zoomInAct);
518 menu->addAction(zoomOutAct);
520 menu = menuBar()->addMenu(tr("&Edit"));
521 menu->addAction(snapToGridAct);
522 menu->addAction(groupAct);
523 menu->addAction(fixAngleAct);
524 menu->addAction(fixLengthAct);
525 menu->addAction(rotateAct);
526 menu->addSeparator();
527 menu->addAction(deleteAct);
528 menu->addSeparator();
529 menu->addAction(addLineAct);
530 menu->addAction(addCircleAct);
531 menu->addAction(addArcAct);
532 menu->addAction(addPolygonAct);
533 menu->addAction(addDimensionAct);
534 menu->addSeparator();
535 menu->addAction(settingsAct);
537 menu = menuBar()->addMenu(tr("&Help"));
538 menu->addAction(aboutAct);
542 void ApplicationWindow::CreateToolbars(void)
544 QToolBar * toolbar = addToolBar(tr("File"));
545 toolbar->addAction(exitAct);
547 toolbar = addToolBar(tr("View"));
548 toolbar->addAction(zoomInAct);
549 toolbar->addAction(zoomOutAct);
551 toolbar = addToolBar(tr("Edit"));
552 toolbar->addAction(snapToGridAct);
553 toolbar->addAction(groupAct);
554 toolbar->addAction(fixAngleAct);
555 toolbar->addAction(fixLengthAct);
556 toolbar->addAction(rotateAct);
557 toolbar->addAction(deleteAct);
558 toolbar->addSeparator();
559 toolbar->addAction(addLineAct);
560 toolbar->addAction(addCircleAct);
561 toolbar->addAction(addArcAct);
562 toolbar->addAction(addPolygonAct);
563 toolbar->addAction(addDimensionAct);
567 void ApplicationWindow::ReadSettings(void)
569 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
570 QSize size = settings.value("size", QSize(400, 400)).toSize();
571 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
572 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
575 // pos = settings.value("charWndPos", QPoint(0, 0)).toPoint();
576 // size = settings.value("charWndSize", QSize(200, 200)).toSize();
577 // ((TTEdit *)qApp)->charWnd->resize(size);
578 // ((TTEdit *)qApp)->charWnd->move(pos);
582 void ApplicationWindow::WriteSettings(void)
584 settings.setValue("pos", pos());
585 settings.setValue("size", size());
586 settings.setValue("useAntialiasing", drawing->useAntialiasing);
587 settings.setValue("snapToGrid", snapToGridAct->isChecked());
588 // settings.setValue("charWndPos", ((TTEdit *)qApp)->charWnd->pos());
589 // settings.setValue("charWndSize", ((TTEdit *)qApp)->charWnd->size());