1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2019 Shawn Curry (noneyabiz@mail.wasent.cz)
6 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
7 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
8 **
9 **
10 ** This file may be distributed and/or modified under the terms of the
11 ** GNU General Public License version 2 as published by the Free Software
12 ** Foundation and appearing in the file gpl-2.0.txt included in the
13 ** packaging of this file.
14 **
15 ** This program is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ** GNU General Public License for more details.
19 **
20 ** You should have received a copy of the GNU General Public License
21 ** along with this program; if not, write to the Free Software
22 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23 **
24 ** This copyright notice MUST APPEAR in all copies of the script!
25 **
26 **********************************************************************/
27 #include<iostream>
28 #include "qc_mdiwindow.h"
29 
30 #include <QtPrintSupport/QPrinter>
31 #include <QtPrintSupport/QPrintDialog>
32 
33 #include <QApplication>
34 #include <QCloseEvent>
35 #include <QCursor>
36 #include <QMessageBox>
37 #include <QFileInfo>
38 #include <QMdiArea>
39 #include <QPainter>
40 
41 #include "rs_graphic.h"
42 #include "rs_settings.h"
43 #include "qg_exitdialog.h"
44 #include "qg_filedialog.h"
45 #include "rs_insert.h"
46 #include "rs_mtext.h"
47 #include "rs_pen.h"
48 #include "qg_graphicview.h"
49 #include "rs_debug.h"
50 
51 int QC_MDIWindow::idCounter = 0;
52 
53 /**
54  * Constructor.
55  *
56  * @param doc Pointer to an existing document of NULL if a new
57  *   document shall be created for this window.
58  * @param parent An instance of QMdiArea.
59  */
QC_MDIWindow(RS_Document * doc,QWidget * parent,Qt::WindowFlags wflags)60 QC_MDIWindow::QC_MDIWindow(RS_Document* doc, QWidget* parent, Qt::WindowFlags wflags)
61                             : QMdiSubWindow(parent, wflags)
62 {
63     setAttribute(Qt::WA_DeleteOnClose);
64     cadMdiArea=qobject_cast<QMdiArea*>(parent);
65 
66     if (doc==nullptr) {
67         document = new RS_Graphic();
68         document->newDoc();
69         owner = true;
70     } else {
71         document = doc;
72         owner = false;
73     }
74 
75     graphicView = new QG_GraphicView(this, 0, document);
76     graphicView->setObjectName("graphicview");
77 
78     connect(graphicView, SIGNAL(previous_zoom_state(bool)),
79             parent->window(), SLOT(setPreviousZoomEnable(bool)));
80 
81     setWidget(graphicView);
82 
83     id = idCounter++;
84     setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
85 	if (document) {
86 		if (document->getLayerList()) {
87             // Link the graphic view to the layer widget
88             document->getLayerList()->addListener(graphicView);
89             // Link this window to the layer widget
90             document->getLayerList()->addListener(this);
91         }
92 		if (document->getBlockList()) {
93             // Link the graphic view to the block widget
94             document->getBlockList()->addListener(graphicView);
95             // Link this window to the block widget
96             document->getBlockList()->addListener(this);
97         }
98     }
99 }
100 
101 /**
102  * Destructor.
103  *
104  * Deletes the document associated with this window.
105  */
~QC_MDIWindow()106 QC_MDIWindow::~QC_MDIWindow()
107 {
108     RS_DEBUG->print("~QC_MDIWindow");
109 	if(!(graphicView && graphicView->isCleanUp())){
110 
111 		//do not clear layer/block lists, if application is being closed
112 
113 		if (document->getLayerList()) {
114 			document->getLayerList()->removeListener(graphicView);
115 			document->getLayerList()->removeListener(this);
116 		}
117 
118 		if (document->getBlockList()) {
119 			document->getBlockList()->removeListener(graphicView);
120 			document->getBlockList()->removeListener(this);
121 		}
122 
123 		if (owner==true && document) {
124 			delete document;
125 		}
126 		document = nullptr;
127 	}
128 }
129 
getGraphicView() const130 QG_GraphicView* QC_MDIWindow::getGraphicView() const
131 {
132     return (graphicView) ? graphicView : nullptr;
133 }
134 
135 /** @return Pointer to document */
getDocument() const136 RS_Document* QC_MDIWindow::getDocument() const{
137 	return document;
138 }
139 
getId() const140 int QC_MDIWindow::getId() const{
141 	return id;
142 }
143 
getEventHandler() const144 RS_EventHandler* QC_MDIWindow::getEventHandler() const{
145 	if (graphicView) {
146 		return graphicView->getEventHandler();
147 	}
148 	else {
149 		return nullptr;
150 	}
151 }
152 
setParentWindow(QC_MDIWindow * p)153 void QC_MDIWindow::setParentWindow(QC_MDIWindow* p) {
154 	RS_DEBUG->print("QC_MDIWindow::setParentWindow");
155 	parentWindow = p;
156 }
157 
getParentWindow() const158 QC_MDIWindow* QC_MDIWindow::getParentWindow() const {
159 	RS_DEBUG->print("QC_MDIWindow::getParentWindow");
160 	return parentWindow;
161 }
162 
getGraphic() const163 RS_Graphic* QC_MDIWindow::getGraphic() const {
164 	return document->getGraphic();
165 }
166 
167 /**
168  * Adds another MDI window to the list of known windows that
169  * depend on this one. This can be another view or a view for
170  * a particular block.
171  */
addChildWindow(QC_MDIWindow * w)172 void QC_MDIWindow::addChildWindow(QC_MDIWindow* w) {
173     RS_DEBUG->print("RS_MDIWindow::addChildWindow()");
174 
175     childWindows.append(w);
176     w->setParentWindow(this);
177 
178     RS_DEBUG->print("children: %d", childWindows.count());
179 }
180 
181 
182 
183 /**
184  * Removes a child window.
185  *
186  * @see addChildWindow
187  */
removeChildWindow(QC_MDIWindow * w)188 void QC_MDIWindow::removeChildWindow(QC_MDIWindow* w) {
189 //    RS_DEBUG->print("%s %s()", __FILE__, __func__);
190     if(childWindows.size()>0 ){
191         if(childWindows.contains(w)){
192             childWindows.removeAll(w);
193 //            suc=true;
194         }
195     }
196 
197 //    bool suc = childWindows.removeAll(w);
198 //    RS_DEBUG->print("successfully removed child window: %d", (int)suc);
199 
200 //    RS_DEBUG->print("children: %d", childWindows.count());
201 
202 }
203 
getChildWindows()204 QList<QC_MDIWindow*>& QC_MDIWindow::getChildWindows()
205 {
206 	return childWindows;
207 }
208 
209 
210 
211 /**
212  * @return pointer to the print preview of this drawing or NULL.
213  */
getPrintPreview()214 QC_MDIWindow* QC_MDIWindow::getPrintPreview() {
215 	for(auto w: childWindows){
216 		if(w->getGraphicView()->isPrintPreview()){
217 			return w;
218 		}
219 	}
220 	return nullptr;
221 }
222 
223 
224 /**
225  * Called by Qt when the user closes this MDI window.
226  */
closeEvent(QCloseEvent * ce)227 void QC_MDIWindow::closeEvent(QCloseEvent* ce) {
228     RS_DEBUG->print("QC_MDIWindow::closeEvent begin");
229 
230     emit(signalClosing(this));
231     ce->accept(); // handling delegated to QApplication
232 
233     RS_DEBUG->print("QC_MDIWindow::closeEvent end");
234 }
235 
236 
237 /**
238  * Called when the current pen (color, style, width) has changed.
239  * Sets the active pen for the document in this MDI window.
240  */
slotPenChanged(const RS_Pen & pen)241 void QC_MDIWindow::slotPenChanged(const RS_Pen& pen) {
242     RS_DEBUG->print("QC_MDIWindow::slotPenChanged() begin");
243 	if (document) {
244         document->setActivePen(pen);
245     }
246     RS_DEBUG->print("QC_MDIWindow::slotPenChanged() end");
247 }
248 
249 
250 /**
251  * Creates a new empty document in this MDI window.
252  */
slotFileNew()253 void QC_MDIWindow::slotFileNew() {
254     RS_DEBUG->print("QC_MDIWindow::slotFileNew begin");
255 	if (document && graphicView) {
256         document->newDoc();
257         graphicView->redraw();
258     }
259     RS_DEBUG->print("QC_MDIWindow::slotFileNew end");
260 }
261 
262 
263 /**
264  * Creates a new document, loading template, in this MDI window.
265  */
slotFileNewTemplate(const QString & fileName,RS2::FormatType type)266 bool QC_MDIWindow::slotFileNewTemplate(const QString& fileName, RS2::FormatType type) {
267     RS_DEBUG->print("QC_MDIWindow::slotFileNewTemplate begin");
268 
269     bool ret = false;
270 
271     if (document==NULL || fileName.isEmpty())
272         return ret;
273 
274     document->newDoc();
275     ret = document->loadTemplate(fileName, type);
276     if (ret) {
277         RS_DEBUG->print("QC_MDIWindow::slotFileNewTemplate: autoZoom");
278         graphicView->zoomAuto(false);
279     } else
280         RS_DEBUG->print("QC_MDIWindow::slotFileNewTemplate: failed");
281 
282     RS_DEBUG->print("QC_MDIWindow::slotFileNewTemplate end");
283     return ret;
284 }
285 
286 /**
287  * Opens the given file in this MDI window.
288  */
slotFileOpen(const QString & fileName,RS2::FormatType type)289 bool QC_MDIWindow::slotFileOpen(const QString& fileName, RS2::FormatType type) {
290 
291     RS_DEBUG->print("QC_MDIWindow::slotFileOpen");
292     bool ret = false;
293 
294 	if (document && !fileName.isEmpty()) {
295         document->newDoc();
296 
297                 // cosmetics..
298                 // RVT_PORT qApp->processEvents(1000);
299                 qApp->processEvents(QEventLoop::AllEvents, 1000);
300 
301         ret = document->open(fileName, type);
302 
303         if (ret) {
304             //QString message=tr("Loaded document: ")+fileName;
305             //statusBar()->showMessage(message, 2000);
306 
307             if (fileName.endsWith(".lff") || fileName.endsWith(".cxf")) {
308                 drawChars();
309 
310                 RS_DEBUG->print("QC_MDIWindow::slotFileOpen: autoZoom");
311                 graphicView->zoomAuto(false);
312                 RS_DEBUG->print("QC_MDIWindow::slotFileOpen: autoZoom: OK");
313             } else
314                 graphicView->redraw();
315         } else {
316             RS_DEBUG->print("QC_MDIWindow::slotFileOpen: failed");
317         }
318     } else {
319         RS_DEBUG->print("QC_MDIWindow::slotFileOpen: cancelled");
320         //statusBar()->showMessage(tr("Opening aborted"), 2000);
321     }
322 
323     RS_DEBUG->print("QC_MDIWindow::slotFileOpen: OK");
324 
325     return ret;
326 }
327 
slotZoomAuto()328 void QC_MDIWindow::slotZoomAuto() {
329 	if(graphicView){
330         if(graphicView->isPrintPreview()){
331             graphicView->zoomPage();
332         }else{
333             graphicView->zoomAuto();
334         }
335     }
336 }
337 
drawChars()338 void QC_MDIWindow::drawChars() {
339 
340     RS_BlockList* bl = document->getBlockList();
341     double sep = document->getGraphic()->getVariableDouble("LetterSpacing", 3.0);
342     double h = sep/3;
343     sep = sep*3;
344     for (int i=0; i<bl->count(); ++i) {
345         RS_Block* ch = bl->at(i);
346         RS_InsertData data(ch->getName(), RS_Vector(i*sep,0), RS_Vector(1,1), 0, 1, 1, RS_Vector(0,0));
347         RS_Insert* in = new RS_Insert(document, data);
348         document->addEntity(in);
349         QFileInfo info(document->getFilename() );
350         QString uCode = (ch->getName()).mid(1,4);
351         RS_MTextData datatx(RS_Vector(i*sep,-h), h, 4*h, RS_MTextData::VATop,
352                            RS_MTextData::HALeft, RS_MTextData::ByStyle, RS_MTextData::AtLeast,
353                            1, uCode, "standard", 0);
354 /*        RS_MTextData datatx(RS_Vector(i*sep,-h), h, 4*h, RS2::VAlignTop,
355                            RS2::HAlignLeft, RS2::ByStyle, RS2::AtLeast,
356                            1, uCode, info.baseName(), 0);*/
357         RS_MText *tx = new RS_MText(document, datatx);
358         document->addEntity(tx);
359     }
360 
361 }
362 
363 
364 /**
365  * Saves the current file.
366  *
367  * @param  isAutoSave true if this is an "autosave" operation.
368  *                    false if this is "Save" operation requested
369  *                    by the user.
370  * @return true if the file was saved successfully.
371  *         false if the file could not be saved or the document
372  *         is invalid.
373  */
slotFileSave(bool & cancelled,bool isAutoSave)374 bool QC_MDIWindow::slotFileSave(bool &cancelled, bool isAutoSave) {
375     RS_DEBUG->print("QC_MDIWindow::slotFileSave()");
376     bool ret = false;
377     cancelled = false;
378 
379 	if (document) {
380         document->setGraphicView(graphicView);
381         if (isAutoSave) {
382             // Autosave filename is always supposed to be present.
383             // Autosave does not change the cursor.
384             ret = document->save(true);
385         } else {
386             if (document->getFilename().isEmpty()) {
387                 ret = slotFileSaveAs(cancelled);
388             } else {
389 				QFileInfo info(document->getFilename());
390 				if (!info.isWritable())
391 					return false;
392                 QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
393                 ret = document->save();
394                 QApplication::restoreOverrideCursor();
395             }
396         }
397     }
398 
399     return ret;
400 }
401 
402 
403 
404 /**
405  * Saves the current file. The user is asked for a new filename
406  * and format.
407  *
408  * @return true if the file was saved successfully or the user cancelled.
409  *         false if the file could not be saved or the document
410  *         is invalid.
411  */
slotFileSaveAs(bool & cancelled)412 bool QC_MDIWindow::slotFileSaveAs(bool &cancelled) {
413     RS_DEBUG->print("QC_MDIWindow::slotFileSaveAs");
414     bool ret = false;
415     cancelled = false;
416     RS2::FormatType t = RS2::FormatDXFRW;
417 
418     QG_FileDialog dlg(this);
419     QString fn = dlg.getSaveFile(&t);
420     if (document && !fn.isEmpty()) {
421         QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
422         document->setGraphicView(graphicView);
423         ret = document->saveAs(fn, t, true);
424         QApplication::restoreOverrideCursor();
425     } else {
426         // cancel is not an error - returns true
427         ret = true;
428         cancelled = true;
429     }
430 
431     return ret;
432 }
433 
434 
slotFilePrint()435 void QC_MDIWindow::slotFilePrint() {
436 
437     RS_DEBUG->print("QC_MDIWindow::slotFilePrint");
438 
439     //statusBar()->showMessage(tr("Printing..."));
440     QPrinter printer;
441     QPrintDialog dialog(&printer, this);
442     if (dialog.exec()) {
443         QPainter painter;
444         painter.begin(&printer);
445 
446         ///////////////////////////////////////////////////////////////////
447         // TODO: Define printing by using the QPainter methods here
448 
449         painter.end();
450     };
451 
452     //statusBar()->showMessage(tr("Ready."));
453 }
454 
455 
456 
457 /**
458  * Streams some info about an MDI window to stdout.
459  */
operator <<(std::ostream & os,QC_MDIWindow & w)460 std::ostream& operator << (std::ostream& os, QC_MDIWindow& w) {
461     os << "QC_MDIWindow[" << w.getId() << "]:\n";
462 	if (w.parentWindow) {
463         os << "  parentWindow: " << w.parentWindow->getId() << "\n";
464     } else {
465         os << "  parentWindow: NULL\n";
466     }
467 	int i=0;
468 	for(auto p: w.childWindows){
469 		os << "  childWindow[" << i++ << "]: "
470 		   << p->getId() << "\n";
471 	}
472 
473     return os;
474 }
475 
476 /**
477  * Return true if this window has children (QC_MDIWindow).
478  */
has_children()479 bool QC_MDIWindow::has_children()
480 {
481     return !childWindows.isEmpty();
482 }
483