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 "blockwidget.h"
33 #include "drawingview.h"
35 #include "generaltab.h"
36 #include "layerwidget.h"
38 #include "settingsdialog.h"
41 ApplicationWindow::ApplicationWindow(): settings("Underground Software", "Architektonas")
43 drawing = new DrawingView(this);
44 drawing->setMouseTracking(true); // We want *all* mouse events...!
45 setCentralWidget(drawing);
47 aboutWin = new AboutWindow(this);
49 // ((TTEdit *)qApp)->charWnd = new CharWindow(this);
51 setWindowIcon(QIcon(":/res/atns-icon.png"));
52 setWindowTitle("Architektonas");
58 // Create Dock widgets
59 QDockWidget * dock1 = new QDockWidget(tr("Layers"), this);
60 LayerWidget * lw = new LayerWidget;
62 addDockWidget(Qt::RightDockWidgetArea, dock1);
63 QDockWidget * dock2 = new QDockWidget(tr("Blocks"), this);
64 // BlockWidget * bw = new BlockWidget;
65 // dock2->setWidget(bw);
66 addDockWidget(Qt::RightDockWidgetArea, dock2);
69 zoomIndicator = new QLabel("Grid: 12.0\" Zoom: 12.5%");
70 statusBar()->addPermanentWidget(zoomIndicator);
71 statusBar()->showMessage(tr("Ready"));
74 setUnifiedTitleAndToolBarOnMac(true);
75 Object::SetFont(new QFont("Verdana", 15, QFont::Bold));
79 void ApplicationWindow::closeEvent(QCloseEvent * event)
82 event->accept(); // Use ignore() if can't close for some reason
83 //Do we have a memory leak here if we don't delete the font in the Object???
87 void ApplicationWindow::FileNew(void)
89 // Should warn the user if drawing hasn't been saved...
90 drawing->document.Clear();
93 setWindowTitle("Architektonas - Untitled");
94 statusBar()->showMessage(tr("New drawing is ready."));
98 void ApplicationWindow::FileOpen(void)
100 QString filename = QFileDialog::getOpenFileName(this, tr("Open Drawing"),
101 "", tr("Architektonas files (*.drawing)"));
102 FILE * file = fopen(filename.toAscii().data(), "r");
107 msg.setText(QString(tr("Could not open file \"%1\" for loading!")).arg(filename));
108 msg.setIcon(QMessageBox::Critical);
113 Container container(Vector(0, 0));
114 bool successful = FileIO::LoadAtnsFile(file, &container);
120 msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
121 msg.setIcon(QMessageBox::Critical);
126 printf("FileOpen: container size = %li\n", container.objects.size());
127 drawing->document = container;
129 documentName = filename;
130 setWindowTitle(QString("Architektonas - %1").arg(documentName));
131 statusBar()->showMessage(tr("Drawing loaded."));
135 void ApplicationWindow::FileSave(void)
137 if (documentName.isEmpty())
138 documentName = QFileDialog::getSaveFileName(this, tr("Save Drawing"),
139 "", tr("Architektonas drawings (*.drawing)"));
141 FILE * file = fopen(documentName.toAscii().data(), "w");
146 msg.setText(QString(tr("Could not open file \"%1\" for saving!")).arg(documentName));
147 msg.setIcon(QMessageBox::Critical);
152 bool successful = FileIO::SaveAtnsFile(file, &drawing->document);
158 msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
159 msg.setIcon(QMessageBox::Critical);
161 // In this case, we should unlink the created file, since it's not right...
162 unlink(documentName.toAscii().data());
166 setWindowTitle(QString("Architektonas - %1").arg(documentName));
167 statusBar()->showMessage(tr("Drawing saved."));
171 void ApplicationWindow::FileSaveAs(void)
173 QString filename = QFileDialog::getSaveFileName(this, tr("Save Drawing As"),
174 documentName, tr("Architektonas drawings (*.drawing)"));
176 if (!filename.isEmpty())
178 documentName = filename;
184 void ApplicationWindow::SnapToGridTool(void)
186 Object::SetSnapMode(snapToGridAct->isChecked());
190 void ApplicationWindow::FixAngle(void)
192 Object::SetFixedAngle(fixAngleAct->isChecked());
196 void ApplicationWindow::FixLength(void)
198 Object::SetFixedLength(fixLengthAct->isChecked());
202 // We want certain tools to be exclusive, and this approach isn't working correctly...
203 void ApplicationWindow::DeleteTool(void)
205 // For this tool, we check first to see if anything is selected. If so, we
206 // delete those and *don't* select the delete tool.
207 if (drawing->document.ItemsSelected() > 0)
209 drawing->document.DeleteSelectedItems();
211 deleteAct->setChecked(false);
215 // Otherwise, toggle the state of the tool
216 ClearUIToolStatesExcept(deleteAct);
217 SetInternalToolStates();
221 void ApplicationWindow::DimensionTool(void)
223 ClearUIToolStatesExcept(addDimensionAct);
224 SetInternalToolStates();
228 void ApplicationWindow::RotateTool(void)
230 ClearUIToolStatesExcept(rotateAct);
231 SetInternalToolStates();
235 void ApplicationWindow::AddLineTool(void)
237 ClearUIToolStatesExcept(addLineAct);
238 SetInternalToolStates();
242 void ApplicationWindow::AddCircleTool(void)
244 ClearUIToolStatesExcept(addCircleAct);
245 SetInternalToolStates();
249 void ApplicationWindow::AddArcTool(void)
251 ClearUIToolStatesExcept(addArcAct);
252 SetInternalToolStates();
256 void ApplicationWindow::AddPolygonTool(void)
258 ClearUIToolStatesExcept(addPolygonAct);
259 SetInternalToolStates();
263 void ApplicationWindow::ZoomInTool(void)
265 double zoomFactor = 2.0;
267 We need to find the center of the screen, then figure out where the new corner
268 will be in the zoomed in window.
270 So we know in Qt coords, the center is found via:
271 size.width() / 2 --> xCenter
272 size.height() / 2 --> yCenter
274 transform x/yCenter to Cartesian coordinates. So far, so good.
276 when zooming in, new origin will be (xCenter - origin.x) / 2, (yCenter - origin.y) / 2
277 (after subtracting from center, that is...)
279 QSize size = drawing->size();
280 Vector center(size.width() / 2.0, size.height() / 2.0);
281 //printf("Zoom in... Center=%.2f,%.2f; ", center.x, center.y);
282 center = Painter::QtToCartesianCoords(center);
283 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
284 Vector newOrigin = center - ((center - Painter::origin) / zoomFactor);
285 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
286 Painter::origin = newOrigin;
288 //printf("Zoom in... level going from %02f to ", Painter::zoom);
289 // This just zooms leaving origin intact... should zoom in at the current center! [DONE]
290 Painter::zoom *= zoomFactor;
291 drawing->gridSpacing = 12.0 / Painter::zoom;
292 zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
293 drawing->UpdateGridBackground();
298 void ApplicationWindow::ZoomOutTool(void)
303 origin = (223, 160.5)
304 newOrigin should be (-10, -10)
307 center - origin = (213, 150.5)
308 origin - center = (-213, -150.5)
310 + center = (-10, -10)
313 double zoomFactor = 2.0;
314 QSize size = drawing->size();
315 Vector center(size.width() / 2.0, size.height() / 2.0);
316 //printf("Zoom out... Center=%.2f,%.2f; ", center.x, center.y);
317 center = Painter::QtToCartesianCoords(center);
318 //printf("(%.2f,%.2f); origin=%.2f,%.2f; ", center.x, center.y, Painter::origin.x, Painter::origin.y);
319 // Vector newOrigin = (center - Painter::origin) * zoomFactor;
320 // Vector newOrigin = center - (Painter::origin * zoomFactor);
321 Vector newOrigin = center + ((Painter::origin - center) * zoomFactor);
322 //printf("newOrigin=%.2f,%.2f;\n", newOrigin.x, newOrigin.y);
323 Painter::origin = newOrigin;
324 //printf("Zoom out...\n");
325 // This just zooms leaving origin intact... should zoom out at the current center! [DONE]
326 Painter::zoom /= zoomFactor;
327 drawing->gridSpacing = 12.0 / Painter::zoom;
328 zoomIndicator->setText(QString("Grid: %2\" Zoom: %1%").arg(Painter::zoom * 100.0 * SCREEN_ZOOM).arg(drawing->gridSpacing));
329 drawing->UpdateGridBackground();
334 void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception)
336 if (exception != addArcAct)
337 addArcAct->setChecked(false);
339 if (exception != addCircleAct)
340 addCircleAct->setChecked(false);
342 if (exception != addDimensionAct)
343 addDimensionAct->setChecked(false);
345 if (exception != addLineAct)
346 addLineAct->setChecked(false);
348 if (exception != addPolygonAct)
349 addPolygonAct->setChecked(false);
351 if (exception != deleteAct)
352 deleteAct->setChecked(false);
354 if (exception != rotateAct)
355 rotateAct->setChecked(false);
359 void ApplicationWindow::SetInternalToolStates(void)
361 Object::SetDeleteActive(deleteAct->isChecked());
362 Object::SetDimensionActive(addDimensionAct->isChecked());
363 drawing->SetRotateToolActive(rotateAct->isChecked());
365 // We can be sure that if we've come here, then either an active tool is
366 // being deactivated, or a new tool is being created. In either case, the
367 // old tool needs to be deleted.
368 if (drawing->toolAction)
370 delete drawing->toolAction;
371 drawing->toolAction = NULL;
374 drawing->SetAddLineToolActive(addLineAct->isChecked());
375 drawing->SetAddCircleToolActive(addCircleAct->isChecked());
376 drawing->SetAddDimensionToolActive(addDimensionAct->isChecked());
380 void ApplicationWindow::HelpAbout(void)
386 void ApplicationWindow::Settings(void)
388 SettingsDialog dlg(this);
389 dlg.generalTab->antialiasChk->setChecked(drawing->useAntialiasing);
391 if (dlg.exec() == false)
394 // Deal with stuff here (since user hit "OK" button...)
395 drawing->useAntialiasing = dlg.generalTab->antialiasChk->isChecked();
401 // Group a bunch of selected objects (which can include other groups) together
402 // or ungroup a selected group.
404 void ApplicationWindow::HandleGrouping(void)
406 int itemsSelected = drawing->document.ItemsSelected();
408 // If nothing selected, do nothing
409 if (itemsSelected == 0)
411 statusBar()->showMessage(tr("No objects selected to make a group from."));
415 // If it's a group that's selected, ungroup it and leave the objects in a
417 if (itemsSelected == 1)
419 Object * object = drawing->document.SelectedItem(0);
423 printf("SelectedItem = NULL!\n");
425 printf("SelectedItem = %08X, type = %i\n", object, object->type);
428 if (object == NULL || object->type != OTContainer)
430 statusBar()->showMessage(tr("A group requires two or more selected objects."));
434 // Need the parent of the group, we're assuming here that the parent is
435 // the drawing's document. Does it matter? Maybe...
436 // Could just stipulate that grouping like this only takes place where the
437 // parent of the group is the drawing's document. Makes life much simpler.
438 ((Container *)object)->SelectAll();
439 ((Container *)object)->MoveContentsTo(&(drawing->document));
440 drawing->document.Delete(object);
441 statusBar()->showMessage(tr("Objects ungrouped."));
443 // Otherwise, if it's a group of 2 or more objects (which can be groups too)
444 // group them and select the group
445 else if (itemsSelected > 1)
447 Container * container = new Container(Vector(), &(drawing->document));
448 drawing->document.MoveSelectedContentsTo(container);
449 drawing->document.Add(container);
450 container->DeselectAll();
451 container->state = OSSelected;
452 statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
459 void ApplicationWindow::CreateActions(void)
461 exitAct = CreateAction(tr("&Quit"), tr("Quit"), tr("Exits the application."),
462 QIcon(":/res/quit.png"), QKeySequence(tr("Ctrl+q")));
463 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
465 snapToGridAct = CreateAction(tr("Snap To &Grid"), tr("Snap To Grid"), tr("Snaps mouse cursor to the visible grid when moving/creating objects."), QIcon(":/res/snap-to-grid-tool.png"), QKeySequence(tr("S")), true);
466 connect(snapToGridAct, SIGNAL(triggered()), this, SLOT(SnapToGridTool()));
468 fixAngleAct = CreateAction(tr("Fix &Angle"), tr("Fix Angle"), tr("Fixes the angle of an object."),
469 QIcon(":/res/fix-angle.png"), QKeySequence(tr("F,A")), true);
470 connect(fixAngleAct, SIGNAL(triggered()), this, SLOT(FixAngle()));
472 fixLengthAct = CreateAction(tr("Fix &Length"), tr("Fix Length"), tr("Fixes the length of an object."),
473 QIcon(":/res/fix-length.png"), QKeySequence(tr("F,L")), true);
474 connect(fixLengthAct, SIGNAL(triggered()), this, SLOT(FixLength()));
476 deleteAct = CreateAction(tr("&Delete"), tr("Delete Object"), tr("Deletes selected objects."), QIcon(":/res/delete-tool.png"), QKeySequence(tr("Delete")), true);
477 connect(deleteAct, SIGNAL(triggered()), this, SLOT(DeleteTool()));
479 addDimensionAct = CreateAction(tr("Add &Dimension"), tr("Add Dimension"), tr("Adds a dimension to the drawing."), QIcon(":/res/dimension-tool.png"), QKeySequence("D,I"), true);
480 connect(addDimensionAct, SIGNAL(triggered()), this, SLOT(DimensionTool()));
482 addLineAct = CreateAction(tr("Add &Line"), tr("Add Line"), tr("Adds lines to the drawing."), QIcon(":/res/add-line-tool.png"), QKeySequence("A,L"), true);
483 connect(addLineAct, SIGNAL(triggered()), this, SLOT(AddLineTool()));
485 addCircleAct = CreateAction(tr("Add &Circle"), tr("Add Circle"), tr("Adds circles to the drawing."), QIcon(":/res/add-circle-tool.png"), QKeySequence("A,C"), true);
486 connect(addCircleAct, SIGNAL(triggered()), this, SLOT(AddCircleTool()));
488 addArcAct = CreateAction(tr("Add &Arc"), tr("Add Arc"), tr("Adds arcs to the drawing."), QIcon(":/res/add-arc-tool.png"), QKeySequence("A,A"), true);
489 connect(addArcAct, SIGNAL(triggered()), this, SLOT(AddArcTool()));
491 addPolygonAct = CreateAction(tr("Add &Polygon"), tr("Add Polygon"), tr("Add polygons to the drawing."), QIcon(":/res/add-polygon-tool.png"), QKeySequence("A,P"), true);
492 connect(addPolygonAct, SIGNAL(triggered()), this, SLOT(AddPolygonTool()));
494 aboutAct = CreateAction(tr("About &Architektonas"), tr("About Architektonas"), tr("Gives information about this program."), QIcon(":/res/generic-tool.png"), QKeySequence());
495 connect(aboutAct, SIGNAL(triggered()), this, SLOT(HelpAbout()));
497 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);
498 connect(rotateAct, SIGNAL(triggered()), this, SLOT(RotateTool()));
500 zoomInAct = CreateAction(tr("Zoom &In"), tr("Zoom In"), tr("Zoom in on the document."), QIcon(":/res/zoom-in.png"), QKeySequence(tr("+")), QKeySequence(tr("=")));
501 connect(zoomInAct, SIGNAL(triggered()), this, SLOT(ZoomInTool()));
503 zoomOutAct = CreateAction(tr("Zoom &Out"), tr("Zoom Out"), tr("Zoom out of the document."), QIcon(":/res/zoom-out.png"), QKeySequence(tr("-")));
504 connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(ZoomOutTool()));
506 fileNewAct = CreateAction(tr("&New Drawing"), tr("New Drawing"), tr("Creates a new drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+n")));
507 connect(fileNewAct, SIGNAL(triggered()), this, SLOT(FileNew()));
509 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")));
510 connect(fileOpenAct, SIGNAL(triggered()), this, SLOT(FileOpen()));
512 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")));
513 connect(fileSaveAct, SIGNAL(triggered()), this, SLOT(FileSave()));
515 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")));
516 connect(fileSaveAsAct, SIGNAL(triggered()), this, SLOT(FileSaveAs()));
518 fileCloseAct = CreateAction(tr("&Close Drawing"), tr("Close Drawing"), tr("Closes the current drawing."), QIcon(":/res/generic-tool.png"), QKeySequence(tr("Ctrl+w")));
520 settingsAct = CreateAction(tr("&Settings"), tr("Settings"), tr("Change certain defaults for Architektonas."), QIcon(":/res/generic-tool.png"), QKeySequence());
521 connect(settingsAct, SIGNAL(triggered()), this, SLOT(Settings()));
523 groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
524 connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
526 //Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
527 // Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
528 /* QActionGroup * group = new QActionGroup(this);
529 group->addAction(deleteAct);
530 group->addAction(addDimensionAct);
531 group->addAction(addLineAct);
532 group->addAction(addCircleAct);
533 group->addAction(addArcAct);//*/
538 // Consolidates action creation from a multi-step process to a single-step one.
540 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
541 QIcon icon, QKeySequence key, bool checkable/*= false*/)
543 QAction * action = new QAction(icon, name, this);
544 action->setToolTip(tooltip);
545 action->setStatusTip(statustip);
546 action->setShortcut(key);
547 action->setCheckable(checkable);
554 // This is essentially the same as the previous function, but this allows more
555 // than one key sequence to be added as key shortcuts.
557 QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
558 QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
560 QAction * action = new QAction(icon, name, this);
561 action->setToolTip(tooltip);
562 action->setStatusTip(statustip);
563 QList<QKeySequence> keyList;
564 keyList.append(key1);
565 keyList.append(key2);
566 action->setShortcuts(keyList);
567 action->setCheckable(checkable);
573 void ApplicationWindow::CreateMenus(void)
575 QMenu * menu = menuBar()->addMenu(tr("&File"));
576 menu->addAction(fileNewAct);
577 menu->addAction(fileOpenAct);
578 menu->addAction(fileSaveAct);
579 menu->addAction(fileSaveAsAct);
580 menu->addAction(fileCloseAct);
581 menu->addSeparator();
582 menu->addAction(exitAct);
584 menu = menuBar()->addMenu(tr("&View"));
585 menu->addAction(zoomInAct);
586 menu->addAction(zoomOutAct);
588 menu = menuBar()->addMenu(tr("&Edit"));
589 menu->addAction(snapToGridAct);
590 menu->addAction(groupAct);
591 menu->addAction(fixAngleAct);
592 menu->addAction(fixLengthAct);
593 menu->addAction(rotateAct);
594 menu->addSeparator();
595 menu->addAction(deleteAct);
596 menu->addSeparator();
597 menu->addAction(addLineAct);
598 menu->addAction(addCircleAct);
599 menu->addAction(addArcAct);
600 menu->addAction(addPolygonAct);
601 menu->addAction(addDimensionAct);
602 menu->addSeparator();
603 menu->addAction(settingsAct);
605 menu = menuBar()->addMenu(tr("&Help"));
606 menu->addAction(aboutAct);
610 void ApplicationWindow::CreateToolbars(void)
612 QToolBar * toolbar = addToolBar(tr("File"));
613 toolbar->addAction(exitAct);
615 toolbar = addToolBar(tr("View"));
616 toolbar->addAction(zoomInAct);
617 toolbar->addAction(zoomOutAct);
619 toolbar = addToolBar(tr("Edit"));
620 toolbar->addAction(snapToGridAct);
621 toolbar->addAction(groupAct);
622 toolbar->addAction(fixAngleAct);
623 toolbar->addAction(fixLengthAct);
624 toolbar->addAction(rotateAct);
625 toolbar->addAction(deleteAct);
626 toolbar->addSeparator();
627 toolbar->addAction(addLineAct);
628 toolbar->addAction(addCircleAct);
629 toolbar->addAction(addArcAct);
630 toolbar->addAction(addPolygonAct);
631 toolbar->addAction(addDimensionAct);
635 void ApplicationWindow::ReadSettings(void)
637 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
638 QSize size = settings.value("size", QSize(400, 400)).toSize();
639 drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool();
640 snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool());
643 // pos = settings.value("charWndPos", QPoint(0, 0)).toPoint();
644 // size = settings.value("charWndSize", QSize(200, 200)).toSize();
645 // ((TTEdit *)qApp)->charWnd->resize(size);
646 // ((TTEdit *)qApp)->charWnd->move(pos);
650 void ApplicationWindow::WriteSettings(void)
652 settings.setValue("pos", pos());
653 settings.setValue("size", size());
654 settings.setValue("useAntialiasing", drawing->useAntialiasing);
655 settings.setValue("snapToGrid", snapToGridAct->isChecked());
656 // settings.setValue("charWndPos", ((TTEdit *)qApp)->charWnd->pos());
657 // settings.setValue("charWndSize", ((TTEdit *)qApp)->charWnd->size());