1
/***************************************************************************
2
* Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
26
# include <boost_signals2.hpp>
27
# include <boost/core/ignore_unused.hpp>
28
# include <QApplication>
30
# include <QCloseEvent>
31
# include <QMdiSubWindow>
32
# include <QPrintDialog>
33
# include <QPrintPreviewDialog>
35
# include <QPrinterInfo>
36
# include <QRegularExpression>
37
# include <QRegularExpressionMatch>
40
#include <Base/Interpreter.h>
41
#include <App/Document.h>
45
#include "Application.h"
47
#include "FileDialog.h"
48
#include "MainWindow.h"
49
#include "ViewProviderDocumentObject.h"
53
namespace sp = std::placeholders;
55
TYPESYSTEM_SOURCE_ABSTRACT(Gui::MDIView,Gui::BaseView)
58
MDIView::MDIView(Gui::Document* pcDocument,QWidget* parent, Qt::WindowFlags wflags)
59
: QMainWindow(parent, wflags)
60
, BaseView(pcDocument)
61
, pythonObject(nullptr)
63
, wstate(Qt::WindowNoState)
64
, ActiveObjects(pcDocument)
66
setAttribute(Qt::WA_DeleteOnClose);
71
connectDelObject = pcDocument->signalDeletedObject.connect
72
(std::bind(&ActiveObjectList::objectDeleted, &ActiveObjects, sp::_1));
73
assert(connectDelObject.connected());
80
//This view might be the focus widget of the main window. In this case we must
81
//clear the focus and e.g. set the focus directly to the main window, otherwise
82
//the application crashes when accessing this deleted view.
83
//This effect only occurs if this widget is not in Child mode, because otherwise
84
//the focus stuff is done correctly.
85
if (getMainWindow()) {
86
QWidget* foc = getMainWindow()->focusWidget();
91
getMainWindow()->setFocus();
94
par = par->parentWidget();
98
if (connectDelObject.connected())
99
connectDelObject.disconnect();
102
Base::PyGILStateLocker lock;
103
Py_DECREF(pythonObject);
104
pythonObject = nullptr;
108
void MDIView::deleteSelf()
110
// When using QMdiArea make sure to remove the QMdiSubWindow
111
// this view is associated with.
113
// #0001023: Crash when quitting after using Windows > Tile
114
// Use deleteLater() instead of delete operator.
115
QWidget* parent = this->parentWidget();
116
if (qobject_cast<QMdiSubWindow*>(parent)) {
117
// https://forum.freecad.org/viewtopic.php?f=22&t=23070
124
// detach from document
127
_pcDocument = nullptr;
130
PyObject* MDIView::getPyObject()
133
pythonObject = new MDIViewPy(this);
135
Py_INCREF(pythonObject);
139
void MDIView::setOverrideCursor(const QCursor& c)
144
void MDIView::restoreOverrideCursor()
148
void MDIView::onRelabel(Gui::Document *pDoc)
151
// Try to separate document name and view number if there is one
152
QString cap = windowTitle();
153
// Either with dirty flag ...
154
QRegularExpression rx(QLatin1String(R"((\s\:\s\d+\[\*\])$)"));
155
QRegularExpressionMatch match;
157
boost::ignore_unused(cap.lastIndexOf(rx, -1, &match));
158
if (!match.hasMatch()) {
160
rx.setPattern(QLatin1String(R"((\s\:\s\d+)$)"));
162
boost::ignore_unused(cap.lastIndexOf(rx, -1, &match));
164
if (match.hasMatch()) {
165
cap = QString::fromUtf8(pDoc->getDocument()->Label.getValue());
166
cap += match.captured();
170
cap = QString::fromUtf8(pDoc->getDocument()->Label.getValue());
171
cap = QString::fromLatin1("%1[*]").arg(cap);
177
void MDIView::viewAll()
182
bool MDIView::onMsg(const char* pMsg,const char** ppReturn)
189
bool MDIView::onHasMsg(const char* pMsg) const
195
bool MDIView::canClose()
197
if (getAppDocument() && getAppDocument()->testStatus(App::Document::TempDoc))
200
if (!bIsPassive && getGuiDocument() && getGuiDocument()->isLastView()) {
201
this->setFocus(); // raises the view to front
202
return (getGuiDocument()->canClose(true,true));
208
void MDIView::closeEvent(QCloseEvent *e)
213
// must be detached so that the last view can get asked
214
Document* doc = this->getGuiDocument();
215
if (doc && !doc->isLastView())
216
doc->detachView(this);
219
// Note: When using QMdiArea we must not use removeWindow()
220
// because otherwise the QMdiSubWindow will lose its parent
221
// and thus the notification in QMdiSubWindow::closeEvent of
222
// other mdi windows to get maximized if this window
223
// is maximized will fail.
224
// This odd behaviour is caused by the invocation of
225
// d->mdiArea->removeSubWindow(parent) which we must let there
226
// because otherwise other parts don't work as they should.
227
QMainWindow::closeEvent(e);
234
void MDIView::windowStateChanged(QWidget* view)
239
void MDIView::print(QPrinter* printer)
242
std::cerr << "Printing not implemented for " << this->metaObject()->className() << std::endl;
247
QPrinter printer(QPrinter::ScreenResolution);
248
printer.setFullPage(true);
249
QPrintDialog dlg(&printer, this);
250
if (dlg.exec() == QDialog::Accepted) {
255
void MDIView::printPdf()
257
QString filename = FileDialog::getSaveFileName(this, tr("Export PDF"), QString(),
258
QString::fromLatin1("%1 (*.pdf)").arg(tr("PDF file")));
259
if (!filename.isEmpty()) {
260
QPrinter printer(QPrinter::ScreenResolution);
261
// setPdfVersion sets the printied PDF Version to comply with PDF/A-1b, more details under: https://www.kdab.com/creating-pdfa-documents-qt/
262
printer.setPdfVersion(QPagedPaintDevice::PdfVersion_A1b);
263
printer.setOutputFormat(QPrinter::PdfFormat);
264
printer.setOutputFileName(filename);
269
void MDIView::printPreview()
271
QPrinter printer(QPrinter::ScreenResolution);
272
QPrintPreviewDialog dlg(&printer, this);
273
connect(&dlg, &QPrintPreviewDialog::paintRequested,
274
this, qOverload<QPrinter*>(&MDIView::print));
278
void MDIView::savePrinterSettings(QPrinter* printer)
280
auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Printer");
281
QString printerName = printer->printerName();
282
if (printerName.isEmpty()) {
283
// no printer defined
287
hGrp = hGrp->GetGroup(printerName.toUtf8());
289
hGrp->SetInt("DefaultPageSize", printer->pageLayout().pageSize().id());
290
hGrp->SetInt("DefaultPageOrientation", static_cast<int>(printer->pageLayout().orientation()));
291
hGrp->SetInt("DefaultColorMode", static_cast<int>(printer->colorMode()));
294
void MDIView::restorePrinterSettings(QPrinter* printer)
296
auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Printer");
297
QString printerName = printer->printerName();
298
if (printerName.isEmpty()) {
299
// no printer defined
303
hGrp = hGrp->GetGroup(printerName.toUtf8());
305
QPrinterInfo info = QPrinterInfo::defaultPrinter();
306
int initialDefaultPageSize = info.isNull() ? QPageSize::A4 : info.defaultPageSize().id();
307
int defaultPageSize = hGrp->GetInt("DefaultPageSize", initialDefaultPageSize);
308
int defaultPageOrientation = hGrp->GetInt("DefaultPageOrientation", QPageLayout::Portrait);
309
int defaultColorMode = hGrp->GetInt("DefaultColorMode", QPrinter::ColorMode::Color);
311
printer->setPageSize(QPageSize(static_cast<QPageSize::PageSizeId>(defaultPageSize)));
312
printer->setPageOrientation(static_cast<QPageLayout::Orientation>(defaultPageOrientation));
313
printer->setColorMode(static_cast<QPrinter::ColorMode>(defaultColorMode));
316
QStringList MDIView::undoActions() const
319
Gui::Document* doc = getGuiDocument();
321
std::vector<std::string> vecUndos = doc->getUndoVector();
322
for (const auto & vecUndo : vecUndos) {
323
actions << QCoreApplication::translate("Command", vecUndo.c_str());
330
QStringList MDIView::redoActions() const
333
Gui::Document* doc = getGuiDocument();
335
std::vector<std::string> vecRedos = doc->getRedoVector();
336
for (const auto & vecRedo : vecRedos) {
337
actions << QCoreApplication::translate("Command", vecRedo.c_str());
344
QSize MDIView::minimumSizeHint () const
349
void MDIView::changeEvent(QEvent *e)
352
case QEvent::ActivationChange:
354
// Forces this top-level window to be the active view of the main window
355
if (isActiveWindow()) {
356
if (getMainWindow()->activeWindow() != this)
357
getMainWindow()->setActiveWindow(this);
360
case QEvent::WindowTitleChange:
361
case QEvent::ModifiedChange:
363
// sets the appropriate tab of the tabbar
364
getMainWindow()->tabChanged(this);
368
QMainWindow::changeEvent(e);
374
// To fix bug #0000345 move function declaration to here
375
extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in qwidget_x11.cpp
378
void MDIView::setCurrentViewMode(ViewMode mode)
384
if (this->currentMode == FullScreen) {
386
setWindowFlags(windowFlags() & ~Qt::Window);
388
else if (this->currentMode == TopLevel) {
389
this->wstate = windowState();
390
setWindowFlags( windowFlags() & ~Qt::Window );
393
if (this->currentMode != Child) {
394
this->currentMode = Child;
395
getMainWindow()->addWindow(this);
396
getMainWindow()->activateWindow();
400
// go to top-level mode
403
if (this->currentMode == Child) {
404
if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
405
getMainWindow()->removeWindow(this,false);
406
setWindowFlags(windowFlags() | Qt::Window);
407
setParent(nullptr, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
408
Qt::WindowMinMaxButtonsHint);
409
if (this->wstate & Qt::WindowMaximized)
415
//extern void qt_x11_wait_for_window_manager( QWidget* w ); // defined in qwidget_x11.cpp
416
qt_x11_wait_for_window_manager(this);
420
else if (this->currentMode == FullScreen) {
421
if (this->wstate & Qt::WindowMaximized)
427
this->currentMode = TopLevel;
430
// go to fullscreen mode
433
if (this->currentMode == Child) {
434
if (qobject_cast<QMdiSubWindow*>(this->parentWidget()))
435
getMainWindow()->removeWindow(this,false);
436
setWindowFlags(windowFlags() | Qt::Window);
437
setParent(nullptr, Qt::Window);
440
else if (this->currentMode == TopLevel) {
441
this->wstate = windowState();
445
this->currentMode = FullScreen;
451
QString MDIView::buildWindowTitle() const
454
if (Gui::Document* document = getGuiDocument()) {
455
windowTitle.append(QString::fromStdString(getAppDocument()->Label.getStrValue()));
461
#include "moc_MDIView.cpp"