mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Add support for SVG cursor: ZoomIn, ZoomOut, Identify, CrossHair, CapturePoint, Select, Sampler, Icons are provisional: they need some love from a decent graphics designer. Fixes #12671
3822 lines
133 KiB
C++
3822 lines
133 KiB
C++
/***************************************************************************
|
|
qgscomposer.cpp - description
|
|
-------------------
|
|
begin : January 2005
|
|
copyright : (C) 2005 by Radim Blazek
|
|
email : blazek@itc.it
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
#include "qgscomposer.h"
|
|
|
|
#include <stdexcept>
|
|
|
|
#include "qgisapp.h"
|
|
#include "qgsapplication.h"
|
|
#include "qgsbusyindicatordialog.h"
|
|
#include "qgscomposerruler.h"
|
|
#include "qgscomposerview.h"
|
|
#include "qgscomposition.h"
|
|
#include "qgscompositionwidget.h"
|
|
#include "qgscomposermodel.h"
|
|
#include "qgsdockwidget.h"
|
|
#include "qgsatlascompositionwidget.h"
|
|
#include "qgscomposerarrow.h"
|
|
#include "qgscomposerpolygon.h"
|
|
#include "qgscomposerpolyline.h"
|
|
#include "qgscomposerpolygonwidget.h"
|
|
#include "qgscomposerpolylinewidget.h"
|
|
#include "qgscomposerarrowwidget.h"
|
|
#include "qgscomposerattributetablewidget.h"
|
|
#include "qgscomposerframe.h"
|
|
#include "qgscomposerhtml.h"
|
|
#include "qgscomposerhtmlwidget.h"
|
|
#include "qgscomposerlabel.h"
|
|
#include "qgscomposerlabelwidget.h"
|
|
#include "qgscomposerlegend.h"
|
|
#include "qgscomposerlegendwidget.h"
|
|
#include "qgscomposermap.h"
|
|
#include "qgsatlascomposition.h"
|
|
#include "qgscomposermapwidget.h"
|
|
#include "qgscomposerpicture.h"
|
|
#include "qgscomposerpicturewidget.h"
|
|
#include "qgscomposerscalebar.h"
|
|
#include "qgscomposerscalebarwidget.h"
|
|
#include "qgscomposershape.h"
|
|
#include "qgscomposershapewidget.h"
|
|
#include "qgscomposerattributetablev2.h"
|
|
#include "qgsexception.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsproject.h"
|
|
#include "qgsmapcanvas.h"
|
|
#include "qgsmessageviewer.h"
|
|
#include "qgsmaplayeractionregistry.h"
|
|
#include "qgsgeometry.h"
|
|
#include "qgspaperitem.h"
|
|
#include "qgsprevieweffect.h"
|
|
#include "qgsvectorlayer.h"
|
|
#include "qgscomposerimageexportoptionsdialog.h"
|
|
#include "ui_qgssvgexportoptions.h"
|
|
#include "qgspanelwidgetstack.h"
|
|
#include "qgssettings.h"
|
|
#include "qgslayoutmanager.h"
|
|
|
|
#include <QCloseEvent>
|
|
#include <QCheckBox>
|
|
#include <QDesktopWidget>
|
|
#include <QDialog>
|
|
#include <QFileDialog>
|
|
#include <QFileInfo>
|
|
#include <QIcon>
|
|
#include <QImageWriter>
|
|
#include <QLabel>
|
|
#include <QMatrix>
|
|
#include <QMenuBar>
|
|
#include <QMessageBox>
|
|
#include <QPageSetupDialog>
|
|
#include <QPainter>
|
|
#include <QPixmap>
|
|
#include <QPrintDialog>
|
|
#include <QPrinter>
|
|
#include <QSizeGrip>
|
|
#include <QSvgGenerator>
|
|
#include <QTimer>
|
|
#include <QToolBar>
|
|
#include <QToolButton>
|
|
#include <QUndoView>
|
|
#include <QPaintEngine>
|
|
#include <QProgressBar>
|
|
#include <QProgressDialog>
|
|
#include <QShortcut>
|
|
|
|
#ifdef ENABLE_MODELTEST
|
|
#include "modeltest.h"
|
|
#endif
|
|
|
|
QgsComposer::QgsComposer( QgsComposition *composition )
|
|
: mInterface( new QgsAppComposerInterface( this ) )
|
|
, mComposition( composition )
|
|
, mQgis( QgisApp::instance() )
|
|
{
|
|
setupUi( this );
|
|
connect( mActionZoomAll, &QAction::triggered, this, &QgsComposer::mActionZoomAll_triggered );
|
|
connect( mActionZoomIn, &QAction::triggered, this, &QgsComposer::mActionZoomIn_triggered );
|
|
connect( mActionZoomOut, &QAction::triggered, this, &QgsComposer::mActionZoomOut_triggered );
|
|
connect( mActionZoomActual, &QAction::triggered, this, &QgsComposer::mActionZoomActual_triggered );
|
|
connect( mActionRefreshView, &QAction::triggered, this, &QgsComposer::mActionRefreshView_triggered );
|
|
connect( mActionPrint, &QAction::triggered, this, &QgsComposer::mActionPrint_triggered );
|
|
connect( mActionPageSetup, &QAction::triggered, this, &QgsComposer::mActionPageSetup_triggered );
|
|
connect( mActionExportAsImage, &QAction::triggered, this, &QgsComposer::mActionExportAsImage_triggered );
|
|
connect( mActionExportAsSVG, &QAction::triggered, this, &QgsComposer::mActionExportAsSVG_triggered );
|
|
connect( mActionExportAsPDF, &QAction::triggered, this, &QgsComposer::mActionExportAsPDF_triggered );
|
|
connect( mActionSelectMoveItem, &QAction::triggered, this, &QgsComposer::mActionSelectMoveItem_triggered );
|
|
connect( mActionAddArrow, &QAction::triggered, this, &QgsComposer::mActionAddArrow_triggered );
|
|
connect( mActionAddNewMap, &QAction::triggered, this, &QgsComposer::mActionAddNewMap_triggered );
|
|
connect( mActionAddNewLegend, &QAction::triggered, this, &QgsComposer::mActionAddNewLegend_triggered );
|
|
connect( mActionAddNewLabel, &QAction::triggered, this, &QgsComposer::mActionAddNewLabel_triggered );
|
|
connect( mActionAddNewScalebar, &QAction::triggered, this, &QgsComposer::mActionAddNewScalebar_triggered );
|
|
connect( mActionAddImage, &QAction::triggered, this, &QgsComposer::mActionAddImage_triggered );
|
|
connect( mActionAddRectangle, &QAction::triggered, this, &QgsComposer::mActionAddRectangle_triggered );
|
|
connect( mActionAddTriangle, &QAction::triggered, this, &QgsComposer::mActionAddTriangle_triggered );
|
|
connect( mActionAddEllipse, &QAction::triggered, this, &QgsComposer::mActionAddEllipse_triggered );
|
|
connect( mActionEditNodesItem, &QAction::triggered, this, &QgsComposer::mActionEditNodesItem_triggered );
|
|
connect( mActionAddPolygon, &QAction::triggered, this, &QgsComposer::mActionAddPolygon_triggered );
|
|
connect( mActionAddPolyline, &QAction::triggered, this, &QgsComposer::mActionAddPolyline_triggered );
|
|
connect( mActionAddTable, &QAction::triggered, this, &QgsComposer::mActionAddTable_triggered );
|
|
connect( mActionAddAttributeTable, &QAction::triggered, this, &QgsComposer::mActionAddAttributeTable_triggered );
|
|
connect( mActionAddHtml, &QAction::triggered, this, &QgsComposer::mActionAddHtml_triggered );
|
|
connect( mActionSaveProject, &QAction::triggered, this, &QgsComposer::mActionSaveProject_triggered );
|
|
connect( mActionNewComposer, &QAction::triggered, this, &QgsComposer::mActionNewComposer_triggered );
|
|
connect( mActionDuplicateComposer, &QAction::triggered, this, &QgsComposer::mActionDuplicateComposer_triggered );
|
|
connect( mActionComposerManager, &QAction::triggered, this, &QgsComposer::mActionComposerManager_triggered );
|
|
connect( mActionSaveAsTemplate, &QAction::triggered, this, &QgsComposer::mActionSaveAsTemplate_triggered );
|
|
connect( mActionLoadFromTemplate, &QAction::triggered, this, &QgsComposer::mActionLoadFromTemplate_triggered );
|
|
connect( mActionMoveItemContent, &QAction::triggered, this, &QgsComposer::mActionMoveItemContent_triggered );
|
|
connect( mActionPan, &QAction::triggered, this, &QgsComposer::mActionPan_triggered );
|
|
connect( mActionMouseZoom, &QAction::triggered, this, &QgsComposer::mActionMouseZoom_triggered );
|
|
connect( mActionGroupItems, &QAction::triggered, this, &QgsComposer::mActionGroupItems_triggered );
|
|
connect( mActionPasteInPlace, &QAction::triggered, this, &QgsComposer::mActionPasteInPlace_triggered );
|
|
connect( mActionDeleteSelection, &QAction::triggered, this, &QgsComposer::mActionDeleteSelection_triggered );
|
|
connect( mActionSelectAll, &QAction::triggered, this, &QgsComposer::mActionSelectAll_triggered );
|
|
connect( mActionDeselectAll, &QAction::triggered, this, &QgsComposer::mActionDeselectAll_triggered );
|
|
connect( mActionInvertSelection, &QAction::triggered, this, &QgsComposer::mActionInvertSelection_triggered );
|
|
connect( mActionUngroupItems, &QAction::triggered, this, &QgsComposer::mActionUngroupItems_triggered );
|
|
connect( mActionLockItems, &QAction::triggered, this, &QgsComposer::mActionLockItems_triggered );
|
|
connect( mActionUnlockAll, &QAction::triggered, this, &QgsComposer::mActionUnlockAll_triggered );
|
|
connect( mActionSelectNextAbove, &QAction::triggered, this, &QgsComposer::mActionSelectNextAbove_triggered );
|
|
connect( mActionSelectNextBelow, &QAction::triggered, this, &QgsComposer::mActionSelectNextBelow_triggered );
|
|
connect( mActionRaiseItems, &QAction::triggered, this, &QgsComposer::mActionRaiseItems_triggered );
|
|
connect( mActionLowerItems, &QAction::triggered, this, &QgsComposer::mActionLowerItems_triggered );
|
|
connect( mActionMoveItemsToTop, &QAction::triggered, this, &QgsComposer::mActionMoveItemsToTop_triggered );
|
|
connect( mActionMoveItemsToBottom, &QAction::triggered, this, &QgsComposer::mActionMoveItemsToBottom_triggered );
|
|
connect( mActionAlignLeft, &QAction::triggered, this, &QgsComposer::mActionAlignLeft_triggered );
|
|
connect( mActionAlignHCenter, &QAction::triggered, this, &QgsComposer::mActionAlignHCenter_triggered );
|
|
connect( mActionAlignRight, &QAction::triggered, this, &QgsComposer::mActionAlignRight_triggered );
|
|
connect( mActionAlignTop, &QAction::triggered, this, &QgsComposer::mActionAlignTop_triggered );
|
|
connect( mActionAlignVCenter, &QAction::triggered, this, &QgsComposer::mActionAlignVCenter_triggered );
|
|
connect( mActionAlignBottom, &QAction::triggered, this, &QgsComposer::mActionAlignBottom_triggered );
|
|
connect( mActionUndo, &QAction::triggered, this, &QgsComposer::mActionUndo_triggered );
|
|
connect( mActionRedo, &QAction::triggered, this, &QgsComposer::mActionRedo_triggered );
|
|
connect( mActionShowGrid, &QAction::triggered, this, &QgsComposer::mActionShowGrid_triggered );
|
|
connect( mActionSnapGrid, &QAction::triggered, this, &QgsComposer::mActionSnapGrid_triggered );
|
|
connect( mActionShowGuides, &QAction::triggered, this, &QgsComposer::mActionShowGuides_triggered );
|
|
connect( mActionSnapGuides, &QAction::triggered, this, &QgsComposer::mActionSnapGuides_triggered );
|
|
connect( mActionSmartGuides, &QAction::triggered, this, &QgsComposer::mActionSmartGuides_triggered );
|
|
connect( mActionShowBoxes, &QAction::triggered, this, &QgsComposer::mActionShowBoxes_triggered );
|
|
connect( mActionShowPage, &QAction::triggered, this, &QgsComposer::mActionShowPage_triggered );
|
|
connect( mActionClearGuides, &QAction::triggered, this, &QgsComposer::mActionClearGuides_triggered );
|
|
connect( mActionOptions, &QAction::triggered, this, &QgsComposer::mActionOptions_triggered );
|
|
connect( mActionAtlasPreview, &QAction::triggered, this, &QgsComposer::mActionAtlasPreview_triggered );
|
|
connect( mActionAtlasNext, &QAction::triggered, this, &QgsComposer::mActionAtlasNext_triggered );
|
|
connect( mActionAtlasPrev, &QAction::triggered, this, &QgsComposer::mActionAtlasPrev_triggered );
|
|
connect( mActionAtlasFirst, &QAction::triggered, this, &QgsComposer::mActionAtlasFirst_triggered );
|
|
connect( mActionAtlasLast, &QAction::triggered, this, &QgsComposer::mActionAtlasLast_triggered );
|
|
connect( mActionPrintAtlas, &QAction::triggered, this, &QgsComposer::mActionPrintAtlas_triggered );
|
|
connect( mActionExportAtlasAsImage, &QAction::triggered, this, &QgsComposer::mActionExportAtlasAsImage_triggered );
|
|
connect( mActionExportAtlasAsSVG, &QAction::triggered, this, &QgsComposer::mActionExportAtlasAsSVG_triggered );
|
|
connect( mActionExportAtlasAsPDF, &QAction::triggered, this, &QgsComposer::mActionExportAtlasAsPDF_triggered );
|
|
connect( mActionAtlasSettings, &QAction::triggered, this, &QgsComposer::mActionAtlasSettings_triggered );
|
|
connect( mActionToggleFullScreen, &QAction::triggered, this, &QgsComposer::mActionToggleFullScreen_triggered );
|
|
connect( mActionHidePanels, &QAction::triggered, this, &QgsComposer::mActionHidePanels_triggered );
|
|
setWindowTitle( mComposition->name() );
|
|
setAttribute( Qt::WA_DeleteOnClose );
|
|
#if QT_VERSION >= 0x050600
|
|
setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
|
|
#endif
|
|
setupTheme();
|
|
|
|
QgsSettings settings;
|
|
setStyleSheet( mQgis->styleSheet() );
|
|
|
|
int size = settings.value( QStringLiteral( "IconSize" ), QGIS_ICON_SIZE ).toInt();
|
|
setIconSize( QSize( size, size ) );
|
|
|
|
QToolButton *orderingToolButton = new QToolButton( this );
|
|
orderingToolButton->setPopupMode( QToolButton::InstantPopup );
|
|
orderingToolButton->setAutoRaise( true );
|
|
orderingToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
|
|
orderingToolButton->addAction( mActionRaiseItems );
|
|
orderingToolButton->addAction( mActionLowerItems );
|
|
orderingToolButton->addAction( mActionMoveItemsToTop );
|
|
orderingToolButton->addAction( mActionMoveItemsToBottom );
|
|
orderingToolButton->setDefaultAction( mActionRaiseItems );
|
|
mItemActionToolbar->addWidget( orderingToolButton );
|
|
|
|
QToolButton *alignToolButton = new QToolButton( this );
|
|
alignToolButton->setPopupMode( QToolButton::InstantPopup );
|
|
alignToolButton->setAutoRaise( true );
|
|
alignToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
|
|
|
|
alignToolButton->addAction( mActionAlignLeft );
|
|
alignToolButton->addAction( mActionAlignHCenter );
|
|
alignToolButton->addAction( mActionAlignRight );
|
|
alignToolButton->addAction( mActionAlignTop );
|
|
alignToolButton->addAction( mActionAlignVCenter );
|
|
alignToolButton->addAction( mActionAlignBottom );
|
|
alignToolButton->setDefaultAction( mActionAlignLeft );
|
|
mItemActionToolbar->addWidget( alignToolButton );
|
|
|
|
QToolButton *shapeToolButton = new QToolButton( mItemToolbar );
|
|
shapeToolButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicShape.svg" ) ) );
|
|
shapeToolButton->setCheckable( true );
|
|
shapeToolButton->setPopupMode( QToolButton::InstantPopup );
|
|
shapeToolButton->setAutoRaise( true );
|
|
shapeToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
|
|
shapeToolButton->addAction( mActionAddRectangle );
|
|
shapeToolButton->addAction( mActionAddTriangle );
|
|
shapeToolButton->addAction( mActionAddEllipse );
|
|
shapeToolButton->setToolTip( tr( "Add Shape" ) );
|
|
mItemToolbar->insertWidget( mActionAddArrow, shapeToolButton );
|
|
|
|
QToolButton *nodesItemButton = new QToolButton( mItemToolbar );
|
|
nodesItemButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddNodesItem.svg" ) ) );
|
|
nodesItemButton->setCheckable( true );
|
|
nodesItemButton->setPopupMode( QToolButton::InstantPopup );
|
|
nodesItemButton->setAutoRaise( true );
|
|
nodesItemButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
|
|
nodesItemButton->addAction( mActionAddPolygon );
|
|
nodesItemButton->addAction( mActionAddPolyline );
|
|
nodesItemButton->setToolTip( tr( "Add Nodes item" ) );
|
|
mItemToolbar->insertWidget( mActionAddArrow, nodesItemButton );
|
|
|
|
QActionGroup *toggleActionGroup = new QActionGroup( this );
|
|
toggleActionGroup->addAction( mActionMoveItemContent );
|
|
toggleActionGroup->addAction( mActionEditNodesItem );
|
|
toggleActionGroup->addAction( mActionPan );
|
|
toggleActionGroup->addAction( mActionMouseZoom );
|
|
toggleActionGroup->addAction( mActionAddNewMap );
|
|
toggleActionGroup->addAction( mActionAddNewLabel );
|
|
toggleActionGroup->addAction( mActionAddNewLegend );
|
|
toggleActionGroup->addAction( mActionAddNewScalebar );
|
|
toggleActionGroup->addAction( mActionAddImage );
|
|
toggleActionGroup->addAction( mActionSelectMoveItem );
|
|
toggleActionGroup->addAction( mActionAddRectangle );
|
|
toggleActionGroup->addAction( mActionAddTriangle );
|
|
toggleActionGroup->addAction( mActionAddEllipse );
|
|
toggleActionGroup->addAction( mActionAddPolygon );
|
|
toggleActionGroup->addAction( mActionAddPolyline );
|
|
toggleActionGroup->addAction( mActionAddArrow );
|
|
//toggleActionGroup->addAction( mActionAddTable );
|
|
toggleActionGroup->addAction( mActionAddAttributeTable );
|
|
toggleActionGroup->addAction( mActionAddHtml );
|
|
toggleActionGroup->setExclusive( true );
|
|
|
|
mActionAddNewMap->setCheckable( true );
|
|
mActionAddNewLabel->setCheckable( true );
|
|
mActionAddNewLegend->setCheckable( true );
|
|
mActionSelectMoveItem->setCheckable( true );
|
|
mActionAddNewScalebar->setCheckable( true );
|
|
mActionAddImage->setCheckable( true );
|
|
mActionMoveItemContent->setCheckable( true );
|
|
mActionEditNodesItem->setCheckable( true );
|
|
mActionPan->setCheckable( true );
|
|
mActionMouseZoom->setCheckable( true );
|
|
mActionAddArrow->setCheckable( true );
|
|
mActionAddHtml->setCheckable( true );
|
|
|
|
mActionShowGrid->setCheckable( true );
|
|
mActionSnapGrid->setCheckable( true );
|
|
mActionShowGuides->setCheckable( true );
|
|
mActionSnapGuides->setCheckable( true );
|
|
mActionSmartGuides->setCheckable( true );
|
|
mActionShowRulers->setCheckable( true );
|
|
mActionShowBoxes->setCheckable( true );
|
|
|
|
mActionAtlasPreview->setCheckable( true );
|
|
|
|
#ifdef Q_OS_MAC
|
|
mActionQuit->setText( tr( "Close" ) );
|
|
mActionQuit->setShortcut( QKeySequence::Close );
|
|
QMenu *appMenu = menuBar()->addMenu( tr( "QGIS" ) );
|
|
appMenu->addAction( mQgis->actionAbout() );
|
|
appMenu->addAction( mQgis->actionOptions() );
|
|
#endif
|
|
|
|
QMenu *composerMenu = menuBar()->addMenu( tr( "&Composer" ) );
|
|
composerMenu->addAction( mActionSaveProject );
|
|
composerMenu->addSeparator();
|
|
composerMenu->addAction( mActionNewComposer );
|
|
composerMenu->addAction( mActionDuplicateComposer );
|
|
composerMenu->addAction( mActionComposerManager );
|
|
|
|
mPrintComposersMenu = new QMenu( tr( "Print &Composers" ), this );
|
|
mPrintComposersMenu->setObjectName( QStringLiteral( "mPrintComposersMenu" ) );
|
|
connect( mPrintComposersMenu, &QMenu::aboutToShow, this, &QgsComposer::populatePrintComposersMenu );
|
|
composerMenu->addMenu( mPrintComposersMenu );
|
|
|
|
composerMenu->addSeparator();
|
|
composerMenu->addAction( mActionLoadFromTemplate );
|
|
composerMenu->addAction( mActionSaveAsTemplate );
|
|
composerMenu->addSeparator();
|
|
composerMenu->addAction( mActionExportAsImage );
|
|
composerMenu->addAction( mActionExportAsPDF );
|
|
composerMenu->addAction( mActionExportAsSVG );
|
|
composerMenu->addSeparator();
|
|
composerMenu->addAction( mActionPageSetup );
|
|
composerMenu->addAction( mActionPrint );
|
|
composerMenu->addSeparator();
|
|
composerMenu->addAction( mActionQuit );
|
|
connect( mActionQuit, &QAction::triggered, this, &QWidget::close );
|
|
|
|
//cut/copy/paste actions. Note these are not included in the ui file
|
|
//as ui files have no support for QKeySequence shortcuts
|
|
mActionCut = new QAction( tr( "Cu&t" ), this );
|
|
mActionCut->setShortcuts( QKeySequence::Cut );
|
|
mActionCut->setStatusTip( tr( "Cut" ) );
|
|
mActionCut->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCut.svg" ) ) );
|
|
connect( mActionCut, &QAction::triggered, this, &QgsComposer::actionCutTriggered );
|
|
|
|
mActionCopy = new QAction( tr( "&Copy" ), this );
|
|
mActionCopy->setShortcuts( QKeySequence::Copy );
|
|
mActionCopy->setStatusTip( tr( "Copy" ) );
|
|
mActionCopy->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) );
|
|
connect( mActionCopy, &QAction::triggered, this, &QgsComposer::actionCopyTriggered );
|
|
|
|
mActionPaste = new QAction( tr( "&Paste" ), this );
|
|
mActionPaste->setShortcuts( QKeySequence::Paste );
|
|
mActionPaste->setStatusTip( tr( "Paste" ) );
|
|
mActionPaste->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ) );
|
|
connect( mActionPaste, &QAction::triggered, this, &QgsComposer::actionPasteTriggered );
|
|
|
|
QMenu *editMenu = menuBar()->addMenu( tr( "&Edit" ) );
|
|
editMenu->addAction( mActionUndo );
|
|
editMenu->addAction( mActionRedo );
|
|
editMenu->addSeparator();
|
|
|
|
//Backspace should also trigger delete selection
|
|
QShortcut *backSpace = new QShortcut( QKeySequence( QStringLiteral( "Backspace" ) ), this );
|
|
connect( backSpace, &QShortcut::activated, mActionDeleteSelection, &QAction::trigger );
|
|
editMenu->addAction( mActionDeleteSelection );
|
|
editMenu->addSeparator();
|
|
|
|
editMenu->addAction( mActionCut );
|
|
editMenu->addAction( mActionCopy );
|
|
editMenu->addAction( mActionPaste );
|
|
//TODO : "Ctrl+Shift+V" is one way to paste in place, but on some platforms you can use Shift+Ins and F18
|
|
editMenu->addAction( mActionPasteInPlace );
|
|
editMenu->addSeparator();
|
|
editMenu->addAction( mActionSelectAll );
|
|
editMenu->addAction( mActionDeselectAll );
|
|
editMenu->addAction( mActionInvertSelection );
|
|
editMenu->addAction( mActionSelectNextBelow );
|
|
editMenu->addAction( mActionSelectNextAbove );
|
|
|
|
mActionPreviewModeOff = new QAction( tr( "&Normal" ), this );
|
|
mActionPreviewModeOff->setStatusTip( tr( "Normal" ) );
|
|
mActionPreviewModeOff->setCheckable( true );
|
|
mActionPreviewModeOff->setChecked( true );
|
|
connect( mActionPreviewModeOff, &QAction::triggered, this, &QgsComposer::disablePreviewMode );
|
|
mActionPreviewModeGrayscale = new QAction( tr( "Simulate Photocopy (&Grayscale)" ), this );
|
|
mActionPreviewModeGrayscale->setStatusTip( tr( "Simulate photocopy (grayscale)" ) );
|
|
mActionPreviewModeGrayscale->setCheckable( true );
|
|
connect( mActionPreviewModeGrayscale, &QAction::triggered, this, &QgsComposer::activateGrayscalePreview );
|
|
mActionPreviewModeMono = new QAction( tr( "Simulate Fax (&Mono)" ), this );
|
|
mActionPreviewModeMono->setStatusTip( tr( "Simulate fax (mono)" ) );
|
|
mActionPreviewModeMono->setCheckable( true );
|
|
connect( mActionPreviewModeMono, &QAction::triggered, this, &QgsComposer::activateMonoPreview );
|
|
mActionPreviewProtanope = new QAction( tr( "Simulate Color Blindness (&Protanope)" ), this );
|
|
mActionPreviewProtanope->setStatusTip( tr( "Simulate color blindness (Protanope)" ) );
|
|
mActionPreviewProtanope->setCheckable( true );
|
|
connect( mActionPreviewProtanope, &QAction::triggered, this, &QgsComposer::activateProtanopePreview );
|
|
mActionPreviewDeuteranope = new QAction( tr( "Simulate Color Blindness (&Deuteranope)" ), this );
|
|
mActionPreviewDeuteranope->setStatusTip( tr( "Simulate color blindness (Deuteranope)" ) );
|
|
mActionPreviewDeuteranope->setCheckable( true );
|
|
connect( mActionPreviewDeuteranope, &QAction::triggered, this, &QgsComposer::activateDeuteranopePreview );
|
|
|
|
QActionGroup *mPreviewGroup = new QActionGroup( this );
|
|
mPreviewGroup->setExclusive( true );
|
|
mActionPreviewModeOff->setActionGroup( mPreviewGroup );
|
|
mActionPreviewModeGrayscale->setActionGroup( mPreviewGroup );
|
|
mActionPreviewModeMono->setActionGroup( mPreviewGroup );
|
|
mActionPreviewProtanope->setActionGroup( mPreviewGroup );
|
|
mActionPreviewDeuteranope->setActionGroup( mPreviewGroup );
|
|
|
|
QMenu *viewMenu = menuBar()->addMenu( tr( "&View" ) );
|
|
//Ctrl+= should also trigger zoom in
|
|
QShortcut *ctrlEquals = new QShortcut( QKeySequence( QStringLiteral( "Ctrl+=" ) ), this );
|
|
connect( ctrlEquals, &QShortcut::activated, mActionZoomIn, &QAction::trigger );
|
|
|
|
QMenu *previewMenu = viewMenu->addMenu( QStringLiteral( "&Preview" ) );
|
|
previewMenu->addAction( mActionPreviewModeOff );
|
|
previewMenu->addAction( mActionPreviewModeGrayscale );
|
|
previewMenu->addAction( mActionPreviewModeMono );
|
|
previewMenu->addAction( mActionPreviewProtanope );
|
|
previewMenu->addAction( mActionPreviewDeuteranope );
|
|
|
|
viewMenu->addSeparator();
|
|
viewMenu->addAction( mActionZoomIn );
|
|
viewMenu->addAction( mActionZoomOut );
|
|
viewMenu->addAction( mActionZoomAll );
|
|
viewMenu->addAction( mActionZoomActual );
|
|
viewMenu->addSeparator();
|
|
viewMenu->addAction( mActionRefreshView );
|
|
viewMenu->addSeparator();
|
|
viewMenu->addAction( mActionShowGrid );
|
|
viewMenu->addAction( mActionSnapGrid );
|
|
viewMenu->addSeparator();
|
|
viewMenu->addAction( mActionShowGuides );
|
|
viewMenu->addAction( mActionSnapGuides );
|
|
viewMenu->addAction( mActionSmartGuides );
|
|
viewMenu->addAction( mActionClearGuides );
|
|
viewMenu->addSeparator();
|
|
viewMenu->addAction( mActionShowBoxes );
|
|
viewMenu->addAction( mActionShowRulers );
|
|
viewMenu->addAction( mActionShowPage );
|
|
|
|
// Panel and toolbar submenus
|
|
mPanelMenu = new QMenu( tr( "P&anels" ), this );
|
|
mPanelMenu->setObjectName( QStringLiteral( "mPanelMenu" ) );
|
|
mToolbarMenu = new QMenu( tr( "&Toolbars" ), this );
|
|
mToolbarMenu->setObjectName( QStringLiteral( "mToolbarMenu" ) );
|
|
viewMenu->addSeparator();
|
|
viewMenu->addMenu( mPanelMenu );
|
|
viewMenu->addMenu( mToolbarMenu );
|
|
viewMenu->addAction( mActionToggleFullScreen );
|
|
viewMenu->addAction( mActionHidePanels );
|
|
// toolBar already exists, add other widgets as they are created
|
|
mToolbarMenu->addAction( mComposerToolbar->toggleViewAction() );
|
|
mToolbarMenu->addAction( mPaperNavToolbar->toggleViewAction() );
|
|
mToolbarMenu->addAction( mItemActionToolbar->toggleViewAction() );
|
|
mToolbarMenu->addAction( mItemToolbar->toggleViewAction() );
|
|
|
|
QMenu *layoutMenu = menuBar()->addMenu( tr( "&Layout" ) );
|
|
layoutMenu->addAction( mActionAddNewMap );
|
|
layoutMenu->addAction( mActionAddNewLabel );
|
|
layoutMenu->addAction( mActionAddNewScalebar );
|
|
layoutMenu->addAction( mActionAddNewLegend );
|
|
layoutMenu->addAction( mActionAddImage );
|
|
QMenu *shapeMenu = layoutMenu->addMenu( QStringLiteral( "Add Shape" ) );
|
|
shapeMenu->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicShape.svg" ) ) );
|
|
shapeMenu->addAction( mActionAddRectangle );
|
|
shapeMenu->addAction( mActionAddTriangle );
|
|
shapeMenu->addAction( mActionAddEllipse );
|
|
|
|
QMenu *nodesItemMenu = layoutMenu->addMenu( QStringLiteral( "Add Nodes Item" ) );
|
|
nodesItemMenu->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddNodesItem.svg" ) ) );
|
|
nodesItemMenu->addAction( mActionAddPolygon );
|
|
nodesItemMenu->addAction( mActionAddPolyline );
|
|
|
|
layoutMenu->addAction( mActionAddArrow );
|
|
//layoutMenu->addAction( mActionAddTable );
|
|
layoutMenu->addAction( mActionAddAttributeTable );
|
|
layoutMenu->addAction( mActionAddHtml );
|
|
layoutMenu->addSeparator();
|
|
layoutMenu->addAction( mActionSelectMoveItem );
|
|
layoutMenu->addAction( mActionMoveItemContent );
|
|
layoutMenu->addAction( mActionEditNodesItem );
|
|
layoutMenu->addSeparator();
|
|
layoutMenu->addAction( mActionGroupItems );
|
|
layoutMenu->addAction( mActionUngroupItems );
|
|
layoutMenu->addSeparator();
|
|
layoutMenu->addAction( mActionRaiseItems );
|
|
layoutMenu->addAction( mActionLowerItems );
|
|
layoutMenu->addAction( mActionMoveItemsToTop );
|
|
layoutMenu->addAction( mActionMoveItemsToBottom );
|
|
layoutMenu->addAction( mActionLockItems );
|
|
layoutMenu->addAction( mActionUnlockAll );
|
|
|
|
QMenu *atlasMenu = menuBar()->addMenu( tr( "&Atlas" ) );
|
|
atlasMenu->addAction( mActionAtlasPreview );
|
|
atlasMenu->addAction( mActionAtlasFirst );
|
|
atlasMenu->addAction( mActionAtlasPrev );
|
|
atlasMenu->addAction( mActionAtlasNext );
|
|
atlasMenu->addAction( mActionAtlasLast );
|
|
atlasMenu->addSeparator();
|
|
atlasMenu->addAction( mActionPrintAtlas );
|
|
atlasMenu->addAction( mActionExportAtlasAsImage );
|
|
atlasMenu->addAction( mActionExportAtlasAsSVG );
|
|
atlasMenu->addAction( mActionExportAtlasAsPDF );
|
|
atlasMenu->addSeparator();
|
|
atlasMenu->addAction( mActionAtlasSettings );
|
|
|
|
QToolButton *atlasExportToolButton = new QToolButton( mAtlasToolbar );
|
|
atlasExportToolButton->setPopupMode( QToolButton::InstantPopup );
|
|
atlasExportToolButton->setAutoRaise( true );
|
|
atlasExportToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
|
|
atlasExportToolButton->addAction( mActionExportAtlasAsImage );
|
|
atlasExportToolButton->addAction( mActionExportAtlasAsSVG );
|
|
atlasExportToolButton->addAction( mActionExportAtlasAsPDF );
|
|
atlasExportToolButton->setDefaultAction( mActionExportAtlasAsImage );
|
|
mAtlasToolbar->insertWidget( mActionAtlasSettings, atlasExportToolButton );
|
|
mAtlasPageComboBox = new QComboBox();
|
|
mAtlasPageComboBox->setEditable( true );
|
|
mAtlasPageComboBox->addItem( QString::number( 1 ) );
|
|
mAtlasPageComboBox->setCurrentIndex( 0 );
|
|
mAtlasPageComboBox->setMinimumHeight( mAtlasToolbar->height() );
|
|
mAtlasPageComboBox->setMinimumContentsLength( 6 );
|
|
mAtlasPageComboBox->setMaxVisibleItems( 20 );
|
|
mAtlasPageComboBox->setSizeAdjustPolicy( QComboBox::AdjustToContents );
|
|
mAtlasPageComboBox->setInsertPolicy( QComboBox::NoInsert );
|
|
connect( mAtlasPageComboBox->lineEdit(), &QLineEdit::editingFinished, this, &QgsComposer::atlasPageComboEditingFinished );
|
|
connect( mAtlasPageComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsComposer::atlasPageComboEditingFinished );
|
|
mAtlasToolbar->insertWidget( mActionAtlasNext, mAtlasPageComboBox );
|
|
|
|
QMenu *settingsMenu = menuBar()->addMenu( tr( "&Settings" ) );
|
|
settingsMenu->addAction( mActionOptions );
|
|
|
|
#ifdef Q_OS_MAC
|
|
// this doesn't work on Mac anymore: menuBar()->addMenu( mQgis->windowMenu() );
|
|
// QgsComposer::populateWithOtherMenu should work recursively with submenus and regardless of Qt version
|
|
mWindowMenu = new QMenu( tr( "Window" ), this );
|
|
mWindowMenu->setObjectName( "mWindowMenu" );
|
|
connect( mWindowMenu, SIGNAL( aboutToShow() ), this, SLOT( populateWindowMenu() ) );
|
|
menuBar()->addMenu( mWindowMenu );
|
|
|
|
mHelpMenu = new QMenu( tr( "Help" ), this );
|
|
mHelpMenu->setObjectName( "mHelpMenu" );
|
|
connect( mHelpMenu, SIGNAL( aboutToShow() ), this, SLOT( populateHelpMenu() ) );
|
|
menuBar()->addMenu( mHelpMenu );
|
|
#endif
|
|
|
|
setMouseTracking( true );
|
|
mViewFrame->setMouseTracking( true );
|
|
|
|
mStatusZoomCombo = new QComboBox( mStatusBar );
|
|
mStatusZoomCombo->setEditable( true );
|
|
mStatusZoomCombo->setInsertPolicy( QComboBox::NoInsert );
|
|
mStatusZoomCombo->setCompleter( nullptr );
|
|
mStatusZoomCombo->setMinimumWidth( 100 );
|
|
//zoom combo box accepts decimals in the range 1-9999, with an optional decimal point and "%" sign
|
|
QRegExp zoomRx( "\\s*\\d{1,4}(\\.\\d?)?\\s*%?" );
|
|
QValidator *zoomValidator = new QRegExpValidator( zoomRx, mStatusZoomCombo );
|
|
mStatusZoomCombo->lineEdit()->setValidator( zoomValidator );
|
|
|
|
//add some nice zoom levels to the zoom combobox
|
|
mStatusZoomLevelsList << 0.125 << 0.25 << 0.5 << 1.0 << 2.0 << 4.0 << 8.0;
|
|
QList<double>::iterator zoom_it;
|
|
for ( zoom_it = mStatusZoomLevelsList.begin(); zoom_it != mStatusZoomLevelsList.end(); ++zoom_it )
|
|
{
|
|
mStatusZoomCombo->insertItem( 0, tr( "%1%" ).arg( *zoom_it * 100.0, 0, 'f', 1 ) );
|
|
}
|
|
connect( mStatusZoomCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsComposer::statusZoomCombo_currentIndexChanged );
|
|
connect( mStatusZoomCombo->lineEdit(), &QLineEdit::returnPressed, this, &QgsComposer::statusZoomCombo_zoomEntered );
|
|
|
|
//create status bar labels
|
|
mStatusCursorXLabel = new QLabel( mStatusBar );
|
|
mStatusCursorXLabel->setMinimumWidth( 100 );
|
|
mStatusCursorYLabel = new QLabel( mStatusBar );
|
|
mStatusCursorYLabel->setMinimumWidth( 100 );
|
|
mStatusCursorPageLabel = new QLabel( mStatusBar );
|
|
mStatusCursorPageLabel->setMinimumWidth( 100 );
|
|
mStatusCompositionLabel = new QLabel( mStatusBar );
|
|
mStatusCompositionLabel->setMinimumWidth( 350 );
|
|
mStatusAtlasLabel = new QLabel( mStatusBar );
|
|
|
|
//hide borders from child items in status bar under Windows
|
|
mStatusBar->setStyleSheet( QStringLiteral( "QStatusBar::item {border: none;}" ) );
|
|
|
|
mStatusBar->addWidget( mStatusCursorXLabel );
|
|
mStatusBar->addWidget( mStatusCursorYLabel );
|
|
mStatusBar->addWidget( mStatusCursorPageLabel );
|
|
mStatusBar->addWidget( mStatusZoomCombo );
|
|
mStatusBar->addWidget( mStatusCompositionLabel );
|
|
mStatusBar->addWidget( mStatusAtlasLabel );
|
|
|
|
//create composer view and layout with rulers
|
|
mView = nullptr;
|
|
mViewLayout = new QGridLayout();
|
|
mViewLayout->setSpacing( 0 );
|
|
mViewLayout->setMargin( 0 );
|
|
mHorizontalRuler = new QgsComposerRuler( QgsComposerRuler::Horizontal );
|
|
mVerticalRuler = new QgsComposerRuler( QgsComposerRuler::Vertical );
|
|
mRulerLayoutFix = new QWidget();
|
|
mRulerLayoutFix->setAttribute( Qt::WA_NoMousePropagation );
|
|
mRulerLayoutFix->setBackgroundRole( QPalette::Window );
|
|
mRulerLayoutFix->setFixedSize( mVerticalRuler->rulerSize(), mHorizontalRuler->rulerSize() );
|
|
mViewLayout->addWidget( mRulerLayoutFix, 0, 0 );
|
|
mViewLayout->addWidget( mHorizontalRuler, 0, 1 );
|
|
mViewLayout->addWidget( mVerticalRuler, 1, 0 );
|
|
createComposerView();
|
|
mViewFrame->setLayout( mViewLayout );
|
|
|
|
//initial state of rulers
|
|
QgsSettings myQSettings;
|
|
bool showRulers = myQSettings.value( QStringLiteral( "Composer/showRulers" ), true ).toBool();
|
|
mActionShowRulers->blockSignals( true );
|
|
mActionShowRulers->setChecked( showRulers );
|
|
mHorizontalRuler->setVisible( showRulers );
|
|
mVerticalRuler->setVisible( showRulers );
|
|
mRulerLayoutFix->setVisible( showRulers );
|
|
mActionShowRulers->blockSignals( false );
|
|
connect( mActionShowRulers, &QAction::triggered, this, &QgsComposer::toggleRulers );
|
|
|
|
mActionUndo->setEnabled( false );
|
|
mActionRedo->setEnabled( false );
|
|
if ( mComposition->undoStack() )
|
|
{
|
|
connect( mComposition->undoStack(), &QUndoStack::canUndoChanged, mActionUndo, &QAction::setEnabled );
|
|
connect( mComposition->undoStack(), &QUndoStack::canRedoChanged, mActionRedo, &QAction::setEnabled );
|
|
}
|
|
|
|
mActionShowPage->setChecked( mComposition->pagesVisible() );
|
|
restoreGridSettings();
|
|
connectViewSlots();
|
|
connectCompositionSlots();
|
|
connectOtherSlots();
|
|
|
|
mView->setComposition( mComposition );
|
|
|
|
int minDockWidth( 335 );
|
|
|
|
setTabPosition( Qt::AllDockWidgetAreas, QTabWidget::North );
|
|
mGeneralDock = new QgsDockWidget( tr( "Composition" ), this );
|
|
mGeneralDock->setObjectName( QStringLiteral( "CompositionDock" ) );
|
|
mGeneralDock->setMinimumWidth( minDockWidth );
|
|
mGeneralPropertiesStack = new QgsPanelWidgetStack();
|
|
mGeneralDock->setWidget( mGeneralPropertiesStack );
|
|
mPanelMenu->addAction( mGeneralDock->toggleViewAction() );
|
|
mItemDock = new QgsDockWidget( tr( "Item properties" ), this );
|
|
mItemDock->setObjectName( QStringLiteral( "ItemDock" ) );
|
|
mItemDock->setMinimumWidth( minDockWidth );
|
|
mItemPropertiesStack = new QgsPanelWidgetStack();
|
|
mItemDock->setWidget( mItemPropertiesStack );
|
|
mPanelMenu->addAction( mItemDock->toggleViewAction() );
|
|
mUndoDock = new QgsDockWidget( tr( "Command history" ), this );
|
|
mUndoDock->setObjectName( QStringLiteral( "CommandDock" ) );
|
|
mPanelMenu->addAction( mUndoDock->toggleViewAction() );
|
|
mAtlasDock = new QgsDockWidget( tr( "Atlas generation" ), this );
|
|
mAtlasDock->setObjectName( QStringLiteral( "AtlasDock" ) );
|
|
mPanelMenu->addAction( mAtlasDock->toggleViewAction() );
|
|
mItemsDock = new QgsDockWidget( tr( "Items" ), this );
|
|
mItemsDock->setObjectName( QStringLiteral( "ItemsDock" ) );
|
|
mPanelMenu->addAction( mItemsDock->toggleViewAction() );
|
|
|
|
QList<QDockWidget *> docks = findChildren<QDockWidget *>();
|
|
Q_FOREACH ( QDockWidget *dock, docks )
|
|
{
|
|
connect( dock, &QDockWidget::visibilityChanged, this, &QgsComposer::dockVisibilityChanged );
|
|
}
|
|
|
|
createCompositionWidget();
|
|
|
|
//undo widget
|
|
mUndoView = new QUndoView( mComposition->undoStack(), this );
|
|
mUndoDock->setWidget( mUndoView );
|
|
|
|
//items tree widget
|
|
mItemsTreeView = new QTreeView( mItemsDock );
|
|
mItemsTreeView->setModel( mComposition->itemsModel() );
|
|
#ifdef ENABLE_MODELTEST
|
|
new ModelTest( mComposition->itemsModel(), this );
|
|
#endif
|
|
|
|
mItemsTreeView->setColumnWidth( 0, 30 );
|
|
mItemsTreeView->setColumnWidth( 1, 30 );
|
|
mItemsTreeView->header()->setSectionResizeMode( 0, QHeaderView::Fixed );
|
|
mItemsTreeView->header()->setSectionResizeMode( 1, QHeaderView::Fixed );
|
|
mItemsTreeView->header()->setSectionsMovable( false );
|
|
|
|
mItemsTreeView->setDragEnabled( true );
|
|
mItemsTreeView->setAcceptDrops( true );
|
|
mItemsTreeView->setDropIndicatorShown( true );
|
|
mItemsTreeView->setDragDropMode( QAbstractItemView::InternalMove );
|
|
|
|
mItemsTreeView->setIndentation( 0 );
|
|
mItemsDock->setWidget( mItemsTreeView );
|
|
connect( mItemsTreeView->selectionModel(), &QItemSelectionModel::currentChanged, mComposition->itemsModel(), &QgsComposerModel::setSelected );
|
|
|
|
addDockWidget( Qt::RightDockWidgetArea, mItemDock );
|
|
addDockWidget( Qt::RightDockWidgetArea, mGeneralDock );
|
|
addDockWidget( Qt::RightDockWidgetArea, mUndoDock );
|
|
addDockWidget( Qt::RightDockWidgetArea, mAtlasDock );
|
|
addDockWidget( Qt::RightDockWidgetArea, mItemsDock );
|
|
|
|
QgsAtlasCompositionWidget *atlasWidget = new QgsAtlasCompositionWidget( mGeneralDock, mComposition );
|
|
mAtlasDock->setWidget( atlasWidget );
|
|
|
|
mItemDock->show();
|
|
mGeneralDock->show();
|
|
mUndoDock->show();
|
|
mAtlasDock->show();
|
|
mItemsDock->show();
|
|
|
|
tabifyDockWidget( mGeneralDock, mUndoDock );
|
|
tabifyDockWidget( mItemDock, mUndoDock );
|
|
tabifyDockWidget( mGeneralDock, mItemDock );
|
|
tabifyDockWidget( mItemDock, mAtlasDock );
|
|
tabifyDockWidget( mItemDock, mItemsDock );
|
|
|
|
mGeneralDock->raise();
|
|
|
|
//set initial state of atlas controls
|
|
mActionAtlasPreview->setEnabled( false );
|
|
mActionAtlasPreview->setChecked( false );
|
|
mActionAtlasFirst->setEnabled( false );
|
|
mActionAtlasLast->setEnabled( false );
|
|
mActionAtlasNext->setEnabled( false );
|
|
mActionAtlasPrev->setEnabled( false );
|
|
mActionPrintAtlas->setEnabled( false );
|
|
mAtlasPageComboBox->setEnabled( false );
|
|
mActionExportAtlasAsImage->setEnabled( false );
|
|
mActionExportAtlasAsSVG->setEnabled( false );
|
|
mActionExportAtlasAsPDF->setEnabled( false );
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
connect( atlasMap, &QgsAtlasComposition::toggled, this, &QgsComposer::toggleAtlasControls );
|
|
connect( atlasMap, &QgsAtlasComposition::numberFeaturesChanged, this, &QgsComposer::updateAtlasPageComboBox );
|
|
connect( atlasMap, &QgsAtlasComposition::featureChanged, this, &QgsComposer::atlasFeatureChanged );
|
|
toggleAtlasControls( atlasMap->enabled() && atlasMap->coverageLayer() );
|
|
|
|
// Create size grip (needed by Mac OS X for QMainWindow if QStatusBar is not visible)
|
|
//should not be needed now that composer has a status bar?
|
|
#if 0
|
|
mSizeGrip = new QSizeGrip( this );
|
|
mSizeGrip->resize( mSizeGrip->sizeHint() );
|
|
mSizeGrip->move( rect().bottomRight() - mSizeGrip->rect().bottomRight() );
|
|
#endif
|
|
|
|
restoreWindowState();
|
|
setSelectionTool();
|
|
|
|
mView->setFocus();
|
|
|
|
#if defined(ANDROID)
|
|
// fix for Qt Ministro hiding app's menubar in favor of native Android menus
|
|
menuBar()->setNativeMenuBar( false );
|
|
menuBar()->setVisible( true );
|
|
#endif
|
|
}
|
|
|
|
QgsComposer::~QgsComposer()
|
|
{
|
|
mComposition->setAllDeselected();
|
|
delete mPrinter;
|
|
}
|
|
|
|
QgsComposerInterface *QgsComposer::iface()
|
|
{
|
|
return mInterface;
|
|
}
|
|
|
|
void QgsComposer::setupTheme()
|
|
{
|
|
//now set all the icons - getThemeIcon will fall back to default theme if its
|
|
//missing from active theme
|
|
mActionQuit->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileExit.png" ) ) );
|
|
mActionSaveProject->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileSave.svg" ) ) );
|
|
mActionNewComposer->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewComposer.svg" ) ) );
|
|
mActionDuplicateComposer->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateComposer.svg" ) ) );
|
|
mActionComposerManager->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionComposerManager.svg" ) ) );
|
|
mActionLoadFromTemplate->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileOpen.svg" ) ) );
|
|
mActionSaveAsTemplate->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileSaveAs.svg" ) ) );
|
|
mActionExportAsImage->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveMapAsImage.svg" ) ) );
|
|
mActionExportAsSVG->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveAsSVG.svg" ) ) );
|
|
mActionExportAsPDF->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveAsPDF.svg" ) ) );
|
|
mActionPrint->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFilePrint.svg" ) ) );
|
|
mActionZoomAll->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomFullExtent.svg" ) ) );
|
|
mActionZoomIn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomIn.svg" ) ) );
|
|
mActionZoomOut->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomOut.svg" ) ) );
|
|
mActionZoomActual->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomActual.svg" ) ) );
|
|
mActionMouseZoom->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToArea.svg" ) ) );
|
|
mActionRefreshView->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDraw.svg" ) ) );
|
|
mActionUndo->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUndo.svg" ) ) );
|
|
mActionRedo->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRedo.svg" ) ) );
|
|
mActionAddImage->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddImage.svg" ) ) );
|
|
mActionAddNewMap->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddMap.svg" ) ) );
|
|
mActionAddNewLabel->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLabel.svg" ) ) );
|
|
mActionAddNewLegend->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddLegend.svg" ) ) );
|
|
mActionAddNewScalebar->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionScaleBar.svg" ) ) );
|
|
mActionAddRectangle->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicRectangle.svg" ) ) );
|
|
mActionAddTriangle->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicTriangle.svg" ) ) );
|
|
mActionAddEllipse->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicCircle.svg" ) ) );
|
|
mActionAddPolygon->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddPolygon.svg" ) ) );
|
|
mActionAddPolyline->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddPolyline.svg" ) ) );
|
|
mActionAddArrow->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddArrow.svg" ) ) );
|
|
mActionAddTable->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddTable.svg" ) ) );
|
|
mActionAddAttributeTable->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddTable.svg" ) ) );
|
|
mActionAddHtml->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddHtml.svg" ) ) );
|
|
mActionSelectMoveItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSelect.svg" ) ) );
|
|
mActionMoveItemContent->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMoveItemContent.svg" ) ) );
|
|
mActionEditNodesItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditNodesItem.svg" ) ) );
|
|
mActionGroupItems->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionGroupItems.svg" ) ) );
|
|
mActionUngroupItems->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUngroupItems.svg" ) ) );
|
|
mActionRaiseItems->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRaiseItems.svg" ) ) );
|
|
mActionLowerItems->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLowerItems.svg" ) ) );
|
|
mActionMoveItemsToTop->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMoveItemsToTop.svg" ) ) );
|
|
mActionMoveItemsToBottom->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMoveItemsToBottom.svg" ) ) );
|
|
mActionAlignLeft->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAlignLeft.svg" ) ) );
|
|
mActionAlignHCenter->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAlignHCenter.svg" ) ) );
|
|
mActionAlignRight->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAlignRight.svg" ) ) );
|
|
mActionAlignTop->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAlignTop.svg" ) ) );
|
|
mActionAlignVCenter->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAlignVCenter.svg" ) ) );
|
|
mActionAlignBottom->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAlignBottom.svg" ) ) );
|
|
}
|
|
|
|
void QgsComposer::setIconSizes( int size )
|
|
{
|
|
//Set the icon size of for all the toolbars created in the future.
|
|
setIconSize( QSize( size, size ) );
|
|
|
|
//Change all current icon sizes.
|
|
QList<QToolBar *> toolbars = findChildren<QToolBar *>();
|
|
Q_FOREACH ( QToolBar *toolbar, toolbars )
|
|
{
|
|
toolbar->setIconSize( QSize( size, size ) );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::connectViewSlots()
|
|
{
|
|
if ( !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
connect( mView, &QgsComposerView::selectedItemChanged, this, &QgsComposer::showItemOptions );
|
|
connect( mView, &QgsComposerView::itemRemoved, this, &QgsComposer::deleteItem );
|
|
connect( mView, &QgsComposerView::actionFinished, this, &QgsComposer::setSelectionTool );
|
|
|
|
//listen out for position updates from the QgsComposerView
|
|
connect( mView, &QgsComposerView::cursorPosChanged, this, &QgsComposer::updateStatusCursorPos );
|
|
connect( mView, &QgsComposerView::zoomLevelChanged, this, &QgsComposer::updateStatusZoom );
|
|
|
|
connect( mView, &QgsComposerView::zoomLevelChanged, this, &QgsComposer::invalidateCachedRenders );
|
|
}
|
|
|
|
void QgsComposer::connectCompositionSlots()
|
|
{
|
|
if ( !mComposition )
|
|
{
|
|
return;
|
|
}
|
|
|
|
connect( mComposition, &QgsComposition::nameChanged, this, &QgsComposer::setWindowTitle );
|
|
connect( mComposition, &QgsComposition::selectedItemChanged, this, &QgsComposer::showItemOptions );
|
|
connect( mComposition, &QgsComposition::itemRemoved, this, &QgsComposer::deleteItem );
|
|
connect( mComposition, &QgsComposition::paperSizeChanged, this, [ = ]
|
|
{
|
|
mHorizontalRuler->update();
|
|
mVerticalRuler->update();
|
|
} );
|
|
connect( mComposition, &QgsComposition::nPagesChanged, this, [ = ]
|
|
{
|
|
mHorizontalRuler->update();
|
|
mVerticalRuler->update();
|
|
} );
|
|
|
|
//listen out to status bar updates from the atlas
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
connect( atlasMap, &QgsAtlasComposition::statusMsgChanged, this, &QgsComposer::updateStatusAtlasMsg );
|
|
|
|
//listen out to status bar updates from the composition
|
|
connect( mComposition, &QgsComposition::statusMsgChanged, this, &QgsComposer::updateStatusCompositionMsg );
|
|
}
|
|
|
|
void QgsComposer::connectOtherSlots()
|
|
{
|
|
//also listen out for position updates from the horizontal/vertical rulers
|
|
connect( mHorizontalRuler, &QgsComposerRuler::cursorPosChanged, this, &QgsComposer::updateStatusCursorPos );
|
|
connect( mVerticalRuler, &QgsComposerRuler::cursorPosChanged, this, &QgsComposer::updateStatusCursorPos );
|
|
//listen out for zoom updates
|
|
connect( this, &QgsComposer::zoomLevelChanged, this, &QgsComposer::updateStatusZoom );
|
|
connect( this, &QgsComposer::zoomLevelChanged, this, &QgsComposer::invalidateCachedRenders );
|
|
}
|
|
|
|
void QgsComposer::open()
|
|
{
|
|
show();
|
|
activate();
|
|
zoomFull(); // zoomFull() does not work properly until we have called show()
|
|
if ( mView )
|
|
{
|
|
mView->updateRulers();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::activate()
|
|
{
|
|
bool shown = isVisible();
|
|
show();
|
|
raise();
|
|
setWindowState( windowState() & ~Qt::WindowMinimized );
|
|
activateWindow();
|
|
if ( !shown )
|
|
{
|
|
mActionZoomAll_triggered();
|
|
}
|
|
}
|
|
|
|
bool QgsComposer::loadFromTemplate( const QDomDocument &templateDoc, bool clearExisting )
|
|
{
|
|
// provide feedback, since composer will be hidden when loading template (much faster)
|
|
std::unique_ptr< QDialog > dlg( new QgsBusyIndicatorDialog( tr( "Loading template into composer..." ), this ) );
|
|
dlg->setStyleSheet( mQgis->styleSheet() );
|
|
dlg->show();
|
|
|
|
setUpdatesEnabled( false );
|
|
bool result = mComposition->loadFromTemplate( templateDoc, nullptr, false, clearExisting );
|
|
cleanupAfterTemplateRead();
|
|
setUpdatesEnabled( true );
|
|
|
|
dlg->close();
|
|
|
|
if ( result )
|
|
{
|
|
// update composition widget
|
|
QgsCompositionWidget *oldCompositionWidget = qobject_cast<QgsCompositionWidget *>( mGeneralPropertiesStack->takeMainPanel() );
|
|
delete oldCompositionWidget;
|
|
createCompositionWidget();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void QgsComposer::updateStatusCursorPos( QPointF cursorPosition )
|
|
{
|
|
if ( !mComposition )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//convert cursor position to position on current page
|
|
QPointF pagePosition = mComposition->positionOnPage( cursorPosition );
|
|
int currentPage = mComposition->pageNumberForPoint( cursorPosition );
|
|
|
|
mStatusCursorXLabel->setText( QString( tr( "x: %1 mm" ) ).arg( pagePosition.x() ) );
|
|
mStatusCursorYLabel->setText( QString( tr( "y: %1 mm" ) ).arg( pagePosition.y() ) );
|
|
mStatusCursorPageLabel->setText( QString( tr( "page: %3" ) ).arg( currentPage ) );
|
|
}
|
|
|
|
void QgsComposer::updateStatusZoom()
|
|
{
|
|
double dpi = QgsApplication::desktop()->logicalDpiX();
|
|
//monitor dpi is not always correct - so make sure the value is sane
|
|
if ( ( dpi < 60 ) || ( dpi > 250 ) )
|
|
dpi = 72;
|
|
|
|
//pixel width for 1mm on screen
|
|
double scale100 = dpi / 25.4;
|
|
//current zoomLevel
|
|
double zoomLevel = mView->transform().m11() * 100 / scale100;
|
|
|
|
whileBlocking( mStatusZoomCombo )->lineEdit()->setText( tr( "%1%" ).arg( zoomLevel, 0, 'f', 1 ) );
|
|
}
|
|
|
|
void QgsComposer::statusZoomCombo_currentIndexChanged( int index )
|
|
{
|
|
double selectedZoom = mStatusZoomLevelsList.at( mStatusZoomLevelsList.count() - index - 1 );
|
|
if ( mView )
|
|
{
|
|
mView->setZoomLevel( selectedZoom );
|
|
//update zoom combobox text for correct format (one decimal place, trailing % sign)
|
|
whileBlocking( mStatusZoomCombo )->lineEdit()->setText( tr( "%1%" ).arg( selectedZoom * 100.0, 0, 'f', 1 ) );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::statusZoomCombo_zoomEntered()
|
|
{
|
|
if ( !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//need to remove spaces and "%" characters from input text
|
|
QString zoom = mStatusZoomCombo->currentText().remove( QChar( '%' ) ).trimmed();
|
|
mView->setZoomLevel( zoom.toDouble() / 100 );
|
|
}
|
|
|
|
void QgsComposer::updateStatusCompositionMsg( const QString &message )
|
|
{
|
|
mStatusCompositionLabel->setText( message );
|
|
}
|
|
|
|
void QgsComposer::updateStatusAtlasMsg( const QString &message )
|
|
{
|
|
mStatusAtlasLabel->setText( message );
|
|
}
|
|
|
|
void QgsComposer::showItemOptions( QgsComposerItem *item )
|
|
{
|
|
if ( !item )
|
|
{
|
|
delete mItemPropertiesStack->takeMainPanel();
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr< QgsPanelWidget > widget( createItemWidget( item ) );
|
|
if ( ! widget )
|
|
{
|
|
return;
|
|
}
|
|
|
|
delete mItemPropertiesStack->takeMainPanel();
|
|
widget->setDockMode( true );
|
|
mItemPropertiesStack->setMainPanel( widget.release() );
|
|
}
|
|
|
|
void QgsComposer::mActionOptions_triggered()
|
|
{
|
|
mQgis->showOptionsDialog( this, QStringLiteral( "mOptionsPageComposer" ) );
|
|
}
|
|
|
|
void QgsComposer::toggleAtlasControls( bool atlasEnabled )
|
|
{
|
|
//preview defaults to unchecked
|
|
mActionAtlasPreview->blockSignals( true );
|
|
mActionAtlasPreview->setChecked( false );
|
|
mActionAtlasFirst->setEnabled( false );
|
|
mActionAtlasLast->setEnabled( false );
|
|
mActionAtlasNext->setEnabled( false );
|
|
mActionAtlasPrev->setEnabled( false );
|
|
mAtlasPageComboBox->setEnabled( false );
|
|
mActionAtlasPreview->blockSignals( false );
|
|
mActionAtlasPreview->setEnabled( atlasEnabled );
|
|
mActionPrintAtlas->setEnabled( atlasEnabled );
|
|
mActionExportAtlasAsImage->setEnabled( atlasEnabled );
|
|
mActionExportAtlasAsSVG->setEnabled( atlasEnabled );
|
|
mActionExportAtlasAsPDF->setEnabled( atlasEnabled );
|
|
}
|
|
|
|
void QgsComposer::updateAtlasPageComboBox( int pageCount )
|
|
{
|
|
if ( !mComposition )
|
|
return;
|
|
|
|
mAtlasPageComboBox->blockSignals( true );
|
|
mAtlasPageComboBox->clear();
|
|
for ( int i = 1; i <= pageCount && i < 500; ++i )
|
|
{
|
|
QString name = mComposition->atlasComposition().nameForPage( i - 1 );
|
|
QString fullName = ( !name.isEmpty() ? QStringLiteral( "%1: %2" ).arg( i ).arg( name ) : QString::number( i ) );
|
|
|
|
mAtlasPageComboBox->addItem( fullName, i );
|
|
mAtlasPageComboBox->setItemData( i - 1, name, Qt::UserRole + 1 );
|
|
mAtlasPageComboBox->setItemData( i - 1, fullName, Qt::UserRole + 2 );
|
|
}
|
|
mAtlasPageComboBox->blockSignals( false );
|
|
}
|
|
|
|
void QgsComposer::atlasFeatureChanged( QgsFeature *feature )
|
|
{
|
|
Q_UNUSED( feature );
|
|
|
|
if ( !mComposition )
|
|
return;
|
|
|
|
mAtlasPageComboBox->blockSignals( true );
|
|
//prefer to set index of current atlas page, if combo box is showing enough page items
|
|
if ( mComposition->atlasComposition().currentFeatureNumber() < mAtlasPageComboBox->count() )
|
|
{
|
|
mAtlasPageComboBox->setCurrentIndex( mComposition->atlasComposition().currentFeatureNumber() );
|
|
}
|
|
else
|
|
{
|
|
//fallback to setting the combo text to the page number
|
|
mAtlasPageComboBox->setEditText( QString::number( mComposition->atlasComposition().currentFeatureNumber() + 1 ) );
|
|
}
|
|
mAtlasPageComboBox->blockSignals( false );
|
|
|
|
//update expression context variables in map canvas to allow for previewing atlas feature based rendering
|
|
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_featurenumber" ), mComposition->atlasComposition().currentFeatureNumber() + 1, true ) );
|
|
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_pagename" ), mComposition->atlasComposition().currentPageName(), true ) );
|
|
QgsFeature atlasFeature = mComposition->atlasComposition().feature();
|
|
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_feature" ), QVariant::fromValue( atlasFeature ), true ) );
|
|
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_featureid" ), atlasFeature.id(), true ) );
|
|
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "atlas_geometry" ), QVariant::fromValue( atlasFeature.geometry() ), true ) );
|
|
}
|
|
|
|
void QgsComposer::invalidateCachedRenders()
|
|
{
|
|
//redraw cached map items
|
|
QList< QgsComposerMap *> maps;
|
|
mComposition->composerItems( maps );
|
|
|
|
Q_FOREACH ( QgsComposerMap *map, maps )
|
|
{
|
|
map->invalidateCache();
|
|
}
|
|
}
|
|
|
|
QgsPanelWidget *QgsComposer::createItemWidget( QgsComposerItem *item )
|
|
{
|
|
if ( !item )
|
|
return nullptr;
|
|
|
|
switch ( item->type() )
|
|
{
|
|
case QgsComposerItem::ComposerArrow:
|
|
return new QgsComposerArrowWidget( static_cast< QgsComposerArrow * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerPolygon:
|
|
return new QgsComposerPolygonWidget( static_cast< QgsComposerPolygon * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerPolyline:
|
|
return new QgsComposerPolylineWidget( static_cast< QgsComposerPolyline * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerLabel:
|
|
return new QgsComposerLabelWidget( static_cast< QgsComposerLabel * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerMap:
|
|
return new QgsComposerMapWidget( static_cast< QgsComposerMap * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerScaleBar:
|
|
return new QgsComposerScaleBarWidget( static_cast< QgsComposerScaleBar * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerLegend:
|
|
return new QgsComposerLegendWidget( static_cast< QgsComposerLegend * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerPicture:
|
|
return new QgsComposerPictureWidget( static_cast< QgsComposerPicture * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerShape:
|
|
return new QgsComposerShapeWidget( static_cast< QgsComposerShape * >( item ) );
|
|
|
|
case QgsComposerItem::ComposerFrame:
|
|
{
|
|
QgsComposerFrame *frame = static_cast< QgsComposerFrame * >( item );
|
|
if ( QgsComposerHtml *html = dynamic_cast< QgsComposerHtml * >( frame->multiFrame() ) )
|
|
{
|
|
return new QgsComposerHtmlWidget( html, frame );
|
|
}
|
|
else if ( QgsComposerAttributeTableV2 *table = dynamic_cast< QgsComposerAttributeTableV2 * >( frame->multiFrame() ) )
|
|
{
|
|
return new QgsComposerAttributeTableWidget( table, frame );
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
return nullptr; // no warnings!
|
|
}
|
|
|
|
void QgsComposer::mActionAtlasPreview_triggered( bool checked )
|
|
{
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
|
|
//check if composition has an atlas map enabled
|
|
if ( checked && !atlasMap->enabled() )
|
|
{
|
|
//no atlas current enabled
|
|
QMessageBox::warning( nullptr, tr( "Enable atlas preview" ),
|
|
tr( "Atlas in not currently enabled for this composition!" ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
whileBlocking( mActionAtlasPreview )->setChecked( false );
|
|
mStatusAtlasLabel->setText( QString() );
|
|
return;
|
|
}
|
|
|
|
//toggle other controls depending on whether atlas preview is active
|
|
mActionAtlasFirst->setEnabled( checked );
|
|
mActionAtlasLast->setEnabled( checked );
|
|
mActionAtlasNext->setEnabled( checked );
|
|
mActionAtlasPrev->setEnabled( checked );
|
|
mAtlasPageComboBox->setEnabled( checked );
|
|
|
|
if ( checked )
|
|
{
|
|
loadAtlasPredefinedScalesFromProject();
|
|
}
|
|
|
|
bool previewEnabled = mComposition->setAtlasMode( checked ? QgsComposition::PreviewAtlas : QgsComposition::AtlasOff );
|
|
if ( !previewEnabled )
|
|
{
|
|
//something went wrong, e.g., no matching features
|
|
QMessageBox::warning( nullptr, tr( "Enable atlas preview" ),
|
|
tr( "No matching atlas features found!" ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mActionAtlasPreview->blockSignals( true );
|
|
mActionAtlasPreview->setChecked( false );
|
|
mActionAtlasFirst->setEnabled( false );
|
|
mActionAtlasLast->setEnabled( false );
|
|
mActionAtlasNext->setEnabled( false );
|
|
mActionAtlasPrev->setEnabled( false );
|
|
mAtlasPageComboBox->setEnabled( false );
|
|
mActionAtlasPreview->blockSignals( false );
|
|
mStatusAtlasLabel->setText( QString() );
|
|
return;
|
|
}
|
|
|
|
if ( checked )
|
|
{
|
|
mapCanvas()->stopRendering();
|
|
emit atlasPreviewFeatureChanged();
|
|
}
|
|
else
|
|
{
|
|
mStatusAtlasLabel->setText( QString() );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAtlasNext_triggered()
|
|
{
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
if ( !atlasMap->enabled() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mapCanvas()->stopRendering();
|
|
|
|
loadAtlasPredefinedScalesFromProject();
|
|
atlasMap->nextFeature();
|
|
emit atlasPreviewFeatureChanged();
|
|
}
|
|
|
|
void QgsComposer::mActionAtlasPrev_triggered()
|
|
{
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
if ( !atlasMap->enabled() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mapCanvas()->stopRendering();
|
|
|
|
loadAtlasPredefinedScalesFromProject();
|
|
atlasMap->prevFeature();
|
|
emit atlasPreviewFeatureChanged();
|
|
}
|
|
|
|
void QgsComposer::mActionAtlasFirst_triggered()
|
|
{
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
if ( !atlasMap->enabled() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mapCanvas()->stopRendering();
|
|
|
|
loadAtlasPredefinedScalesFromProject();
|
|
atlasMap->firstFeature();
|
|
emit atlasPreviewFeatureChanged();
|
|
}
|
|
|
|
void QgsComposer::mActionAtlasLast_triggered()
|
|
{
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
if ( !atlasMap->enabled() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mapCanvas()->stopRendering();
|
|
|
|
loadAtlasPredefinedScalesFromProject();
|
|
atlasMap->lastFeature();
|
|
emit atlasPreviewFeatureChanged();
|
|
}
|
|
|
|
void QgsComposer::atlasPageComboEditingFinished()
|
|
{
|
|
QString text = mAtlasPageComboBox->lineEdit()->text();
|
|
|
|
//find matching record in combo box
|
|
int page = -1; //note - first page starts at 1, not 0
|
|
for ( int i = 0; i < mAtlasPageComboBox->count(); ++i )
|
|
{
|
|
if ( text.compare( mAtlasPageComboBox->itemData( i, Qt::UserRole + 1 ).toString(), Qt::CaseInsensitive ) == 0
|
|
|| text.compare( mAtlasPageComboBox->itemData( i, Qt::UserRole + 2 ).toString(), Qt::CaseInsensitive ) == 0
|
|
|| QString::number( i + 1 ) == text )
|
|
{
|
|
page = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
bool ok = ( page > 0 );
|
|
|
|
if ( !ok || page > mComposition->atlasComposition().numFeatures() || page < 1 )
|
|
{
|
|
whileBlocking( mAtlasPageComboBox )->setCurrentIndex( mComposition->atlasComposition().currentFeatureNumber() );
|
|
}
|
|
else if ( page != mComposition->atlasComposition().currentFeatureNumber() + 1 )
|
|
{
|
|
mapCanvas()->stopRendering();
|
|
loadAtlasPredefinedScalesFromProject();
|
|
mComposition->atlasComposition().prepareForFeature( page - 1 );
|
|
emit atlasPreviewFeatureChanged();
|
|
}
|
|
}
|
|
|
|
QgsMapCanvas *QgsComposer::mapCanvas()
|
|
{
|
|
return mQgis->mapCanvas();
|
|
}
|
|
|
|
QgsComposerView *QgsComposer::view()
|
|
{
|
|
return mView;
|
|
}
|
|
|
|
void QgsComposer::zoomFull()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->fitInView( mComposition->sceneRect(), Qt::KeepAspectRatio );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionZoomAll_triggered()
|
|
{
|
|
zoomFull();
|
|
mView->updateRulers();
|
|
mView->update();
|
|
emit zoomLevelChanged();
|
|
}
|
|
|
|
void QgsComposer::mActionZoomIn_triggered()
|
|
{
|
|
mView->scaleSafe( 2 );
|
|
mView->updateRulers();
|
|
mView->update();
|
|
emit zoomLevelChanged();
|
|
}
|
|
|
|
void QgsComposer::mActionZoomOut_triggered()
|
|
{
|
|
mView->scaleSafe( 0.5 );
|
|
mView->updateRulers();
|
|
mView->update();
|
|
emit zoomLevelChanged();
|
|
}
|
|
|
|
void QgsComposer::mActionZoomActual_triggered()
|
|
{
|
|
mView->setZoomLevel( 1.0 );
|
|
}
|
|
|
|
void QgsComposer::mActionMouseZoom_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::Zoom );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionRefreshView_triggered()
|
|
{
|
|
if ( !mComposition )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//refresh atlas feature first, to update attributes
|
|
if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
|
|
{
|
|
//block signals from atlas, since the later call to mComposition->refreshItems() will
|
|
//also trigger items to refresh atlas dependent properties
|
|
mComposition->atlasComposition().blockSignals( true );
|
|
mComposition->atlasComposition().refreshFeature();
|
|
mComposition->atlasComposition().blockSignals( false );
|
|
}
|
|
|
|
mComposition->refreshItems();
|
|
mComposition->update();
|
|
}
|
|
|
|
void QgsComposer::mActionShowGrid_triggered( bool checked )
|
|
{
|
|
//show or hide grid
|
|
if ( mComposition )
|
|
{
|
|
mComposition->setGridVisible( checked );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionSnapGrid_triggered( bool checked )
|
|
{
|
|
//enable or disable snap items to grid
|
|
if ( mComposition )
|
|
{
|
|
mComposition->setSnapToGridEnabled( checked );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionShowGuides_triggered( bool checked )
|
|
{
|
|
//show or hide guide lines
|
|
if ( mComposition )
|
|
{
|
|
mComposition->setSnapLinesVisible( checked );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionSnapGuides_triggered( bool checked )
|
|
{
|
|
//enable or disable snap items to guides
|
|
if ( mComposition )
|
|
{
|
|
mComposition->setAlignmentSnap( checked );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionSmartGuides_triggered( bool checked )
|
|
{
|
|
//enable or disable smart snapping guides
|
|
if ( mComposition )
|
|
{
|
|
mComposition->setSmartGuidesEnabled( checked );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionShowBoxes_triggered( bool checked )
|
|
{
|
|
//show or hide bounding boxes
|
|
if ( mComposition )
|
|
{
|
|
mComposition->setBoundingBoxesVisible( checked );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionShowPage_triggered( bool checked )
|
|
{
|
|
//toggle page display
|
|
if ( mComposition )
|
|
{
|
|
mComposition->setPagesVisible( checked );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionClearGuides_triggered()
|
|
{
|
|
//clear guide lines
|
|
if ( mComposition )
|
|
{
|
|
mComposition->clearSnapLines();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::toggleRulers( bool checked )
|
|
{
|
|
//show or hide rulers
|
|
mHorizontalRuler->setVisible( checked );
|
|
mVerticalRuler->setVisible( checked );
|
|
mRulerLayoutFix->setVisible( checked );
|
|
|
|
QgsSettings myQSettings;
|
|
myQSettings.setValue( QStringLiteral( "Composer/showRulers" ), checked );
|
|
}
|
|
|
|
void QgsComposer::mActionAtlasSettings_triggered()
|
|
{
|
|
if ( !mAtlasDock->isVisible() )
|
|
{
|
|
mAtlasDock->show();
|
|
}
|
|
|
|
mAtlasDock->raise();
|
|
}
|
|
|
|
void QgsComposer::mActionToggleFullScreen_triggered()
|
|
{
|
|
if ( mActionToggleFullScreen->isChecked() )
|
|
{
|
|
showFullScreen();
|
|
}
|
|
else
|
|
{
|
|
showNormal();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionHidePanels_triggered()
|
|
{
|
|
/*
|
|
workaround the limited Qt dock widget API
|
|
see http://qt-project.org/forums/viewthread/1141/
|
|
and http://qt-project.org/faq/answer/how_can_i_check_which_tab_is_the_current_one_in_a_tabbed_qdockwidget
|
|
*/
|
|
|
|
bool showPanels = !mActionHidePanels->isChecked();
|
|
QList<QDockWidget *> docks = findChildren<QDockWidget *>();
|
|
QList<QTabBar *> tabBars = findChildren<QTabBar *>();
|
|
|
|
if ( !showPanels )
|
|
{
|
|
mPanelStatus.clear();
|
|
//record status of all docks
|
|
|
|
Q_FOREACH ( QDockWidget *dock, docks )
|
|
{
|
|
mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(), false ) );
|
|
dock->setVisible( false );
|
|
}
|
|
|
|
//record active dock tabs
|
|
Q_FOREACH ( QTabBar *tabBar, tabBars )
|
|
{
|
|
QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
|
|
mPanelStatus[ currentTabTitle ].isActive = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//restore visibility of all docks
|
|
Q_FOREACH ( QDockWidget *dock, docks )
|
|
{
|
|
if ( ! mPanelStatus.contains( dock->windowTitle() ) )
|
|
{
|
|
dock->setVisible( true );
|
|
continue;
|
|
}
|
|
dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
|
|
}
|
|
|
|
//restore previously active dock tabs
|
|
Q_FOREACH ( QTabBar *tabBar, tabBars )
|
|
{
|
|
//loop through all tabs in tab bar
|
|
for ( int i = 0; i < tabBar->count(); ++i )
|
|
{
|
|
QString tabTitle = tabBar->tabText( i );
|
|
if ( mPanelStatus.value( tabTitle ).isActive )
|
|
{
|
|
tabBar->setCurrentIndex( i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsComposer::disablePreviewMode()
|
|
{
|
|
if ( !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mView->setPreviewModeEnabled( false );
|
|
}
|
|
|
|
void QgsComposer::activateGrayscalePreview()
|
|
{
|
|
if ( !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mView->setPreviewMode( QgsPreviewEffect::PreviewGrayscale );
|
|
mView->setPreviewModeEnabled( true );
|
|
}
|
|
|
|
void QgsComposer::activateMonoPreview()
|
|
{
|
|
if ( !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mView->setPreviewMode( QgsPreviewEffect::PreviewMono );
|
|
mView->setPreviewModeEnabled( true );
|
|
}
|
|
|
|
void QgsComposer::activateProtanopePreview()
|
|
{
|
|
if ( !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mView->setPreviewMode( QgsPreviewEffect::PreviewProtanope );
|
|
mView->setPreviewModeEnabled( true );
|
|
}
|
|
|
|
void QgsComposer::activateDeuteranopePreview()
|
|
{
|
|
if ( !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
mView->setPreviewMode( QgsPreviewEffect::PreviewDeuteranope );
|
|
mView->setPreviewModeEnabled( true );
|
|
}
|
|
|
|
void QgsComposer::dockVisibilityChanged( bool visible )
|
|
{
|
|
if ( visible )
|
|
{
|
|
whileBlocking( mActionHidePanels )->setChecked( false );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionExportAtlasAsPDF_triggered()
|
|
{
|
|
QgsComposition::AtlasMode previousMode = mComposition->atlasMode();
|
|
mComposition->setAtlasMode( QgsComposition::ExportAtlas );
|
|
exportCompositionAsPDF( QgsComposer::Atlas );
|
|
mComposition->setAtlasMode( previousMode );
|
|
|
|
if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
|
|
{
|
|
//after atlas output, jump back to preview first feature
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
atlasMap->firstFeature();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionExportAsPDF_triggered()
|
|
{
|
|
exportCompositionAsPDF( QgsComposer::Single );
|
|
}
|
|
|
|
void QgsComposer::exportCompositionAsPDF( QgsComposer::OutputMode mode )
|
|
{
|
|
if ( !mComposition || !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( containsWmsLayer() )
|
|
{
|
|
showWmsPrintingWarning();
|
|
}
|
|
|
|
if ( containsAdvancedEffects() )
|
|
{
|
|
showAdvancedEffectsWarning();
|
|
}
|
|
|
|
// If we are not printing as raster, temporarily disable advanced effects
|
|
// as QPrinter does not support composition modes and can result
|
|
// in items missing from the output
|
|
if ( mComposition->printAsRaster() )
|
|
{
|
|
mComposition->setUseAdvancedEffects( true );
|
|
}
|
|
else
|
|
{
|
|
mComposition->setUseAdvancedEffects( false );
|
|
}
|
|
|
|
bool hasAnAtlas = mComposition->atlasComposition().enabled();
|
|
bool atlasOnASingleFile = hasAnAtlas && mComposition->atlasComposition().singleFile();
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
|
|
QString outputFileName;
|
|
QString outputDir;
|
|
|
|
if ( mode == QgsComposer::Single || ( mode == QgsComposer::Atlas && atlasOnASingleFile ) )
|
|
{
|
|
QgsSettings myQSettings; // where we keep last used filter in persistent state
|
|
QString lastUsedFile = myQSettings.value( QStringLiteral( "UI/lastSaveAsPdfFile" ), "qgis.pdf" ).toString();
|
|
QFileInfo file( lastUsedFile );
|
|
|
|
if ( hasAnAtlas && !atlasOnASingleFile &&
|
|
( mode == QgsComposer::Atlas || mComposition->atlasMode() == QgsComposition::PreviewAtlas ) )
|
|
{
|
|
outputFileName = QDir( file.path() ).filePath( atlasMap->currentFilename() ) + ".pdf";
|
|
}
|
|
else
|
|
{
|
|
outputFileName = file.path();
|
|
}
|
|
#ifdef Q_OS_MAC
|
|
mQgis->activateWindow();
|
|
this->raise();
|
|
#endif
|
|
outputFileName = QFileDialog::getSaveFileName(
|
|
this,
|
|
tr( "Save composition as" ),
|
|
outputFileName,
|
|
tr( "PDF Format" ) + " (*.pdf *.PDF)" );
|
|
this->activateWindow();
|
|
if ( outputFileName.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !outputFileName.endsWith( QLatin1String( ".pdf" ), Qt::CaseInsensitive ) )
|
|
{
|
|
outputFileName += QLatin1String( ".pdf" );
|
|
}
|
|
|
|
myQSettings.setValue( QStringLiteral( "UI/lastSaveAsPdfFile" ), outputFileName );
|
|
}
|
|
// else, we need to choose a directory
|
|
else
|
|
{
|
|
if ( atlasMap->filenamePattern().isEmpty() )
|
|
{
|
|
int res = QMessageBox::warning( nullptr, tr( "Empty filename pattern" ),
|
|
tr( "The filename pattern is empty. A default one will be used." ),
|
|
QMessageBox::Ok | QMessageBox::Cancel,
|
|
QMessageBox::Ok );
|
|
if ( res == QMessageBox::Cancel )
|
|
{
|
|
return;
|
|
}
|
|
atlasMap->setFilenamePattern( QStringLiteral( "'output_'||@atlas_featurenumber" ) );
|
|
}
|
|
|
|
QgsSettings myQSettings;
|
|
QString lastUsedDir = myQSettings.value( QStringLiteral( "UI/lastSaveAtlasAsPdfDir" ), QDir::homePath() ).toString();
|
|
outputDir = QFileDialog::getExistingDirectory( this,
|
|
tr( "Export atlas to directory" ),
|
|
lastUsedDir,
|
|
QFileDialog::ShowDirsOnly );
|
|
if ( outputDir.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
// test directory (if it exists and is writable)
|
|
if ( !QDir( outputDir ).exists() || !QFileInfo( outputDir ).isWritable() )
|
|
{
|
|
QMessageBox::warning( nullptr, tr( "Unable to write into the directory" ),
|
|
tr( "The given output directory is not writable. Canceling." ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
return;
|
|
}
|
|
|
|
myQSettings.setValue( QStringLiteral( "UI/lastSaveAtlasAsPdfDir" ), outputDir );
|
|
}
|
|
|
|
mView->setPaintingEnabled( false );
|
|
|
|
if ( mode == QgsComposer::Atlas )
|
|
{
|
|
QPrinter printer;
|
|
|
|
QPainter painter;
|
|
|
|
loadAtlasPredefinedScalesFromProject();
|
|
if ( ! atlasMap->beginRender() && !atlasMap->featureFilterErrorString().isEmpty() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Feature filter parser error: %1" ).arg( atlasMap->featureFilterErrorString() ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
return;
|
|
}
|
|
if ( atlasOnASingleFile )
|
|
{
|
|
//prepare for first feature, so that we know paper size to begin with
|
|
atlasMap->prepareForFeature( 0 );
|
|
mComposition->beginPrintAsPDF( printer, outputFileName );
|
|
// set the correct resolution
|
|
mComposition->beginPrint( printer );
|
|
bool printReady = painter.begin( &printer );
|
|
if ( !printReady )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( outputFileName ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
return;
|
|
}
|
|
}
|
|
|
|
QProgressDialog progress( tr( "Rendering maps..." ), tr( "Abort" ), 0, atlasMap->numFeatures(), this );
|
|
progress.setWindowTitle( tr( "Exporting Atlas" ) );
|
|
QApplication::setOverrideCursor( Qt::BusyCursor );
|
|
|
|
for ( int featureI = 0; featureI < atlasMap->numFeatures(); ++featureI )
|
|
{
|
|
progress.setValue( featureI );
|
|
// process input events in order to allow aborting
|
|
QCoreApplication::processEvents();
|
|
if ( progress.wasCanceled() )
|
|
{
|
|
atlasMap->endRender();
|
|
break;
|
|
}
|
|
if ( !atlasMap->prepareForFeature( featureI ) )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Atlas processing error" ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
if ( !atlasOnASingleFile )
|
|
{
|
|
// bugs #7263 and #6856
|
|
// QPrinter does not seem to be reset correctly and may cause generated PDFs (all except the first) corrupted
|
|
// when transparent objects are rendered. We thus use a new QPrinter object here
|
|
QPrinter multiFilePrinter;
|
|
outputFileName = QDir( outputDir ).filePath( atlasMap->currentFilename() ) + ".pdf";
|
|
mComposition->beginPrintAsPDF( multiFilePrinter, outputFileName );
|
|
// set the correct resolution
|
|
mComposition->beginPrint( multiFilePrinter );
|
|
bool printReady = painter.begin( &multiFilePrinter );
|
|
if ( !printReady )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( outputFileName ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
mComposition->doPrint( multiFilePrinter, painter );
|
|
painter.end();
|
|
mComposition->georeferenceOutput( outputFileName );
|
|
}
|
|
else
|
|
{
|
|
//start print on a new page if we're not on the first feature
|
|
mComposition->doPrint( printer, painter, featureI > 0 );
|
|
}
|
|
}
|
|
atlasMap->endRender();
|
|
if ( atlasOnASingleFile )
|
|
{
|
|
painter.end();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool exportOk = mComposition->exportAsPDF( outputFileName );
|
|
mComposition->georeferenceOutput( outputFileName );
|
|
|
|
if ( !exportOk )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( outputFileName ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( ! mComposition->useAdvancedEffects() )
|
|
{
|
|
//Switch advanced effects back on
|
|
mComposition->setUseAdvancedEffects( true );
|
|
}
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
|
|
void QgsComposer::mActionPrint_triggered()
|
|
{
|
|
//print only current feature
|
|
printComposition( QgsComposer::Single );
|
|
}
|
|
|
|
void QgsComposer::mActionPrintAtlas_triggered()
|
|
{
|
|
//print whole atlas
|
|
QgsComposition::AtlasMode previousMode = mComposition->atlasMode();
|
|
mComposition->setAtlasMode( QgsComposition::ExportAtlas );
|
|
printComposition( QgsComposer::Atlas );
|
|
mComposition->setAtlasMode( previousMode );
|
|
}
|
|
|
|
void QgsComposer::printComposition( QgsComposer::OutputMode mode )
|
|
{
|
|
if ( !mComposition || !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( containsWmsLayer() )
|
|
{
|
|
showWmsPrintingWarning();
|
|
}
|
|
|
|
if ( containsAdvancedEffects() )
|
|
{
|
|
showAdvancedEffectsWarning();
|
|
}
|
|
|
|
// If we are not printing as raster, temporarily disable advanced effects
|
|
// as QPrinter does not support composition modes and can result
|
|
// in items missing from the output
|
|
if ( mComposition->printAsRaster() )
|
|
{
|
|
mComposition->setUseAdvancedEffects( true );
|
|
}
|
|
else
|
|
{
|
|
mComposition->setUseAdvancedEffects( false );
|
|
}
|
|
|
|
//set printer page orientation
|
|
setPrinterPageOrientation();
|
|
|
|
QPrintDialog printDialog( printer(), nullptr );
|
|
if ( printDialog.exec() != QDialog::Accepted )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QApplication::setOverrideCursor( Qt::BusyCursor );
|
|
mView->setPaintingEnabled( false );
|
|
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
if ( mode == QgsComposer::Single )
|
|
{
|
|
mComposition->print( *printer(), true );
|
|
}
|
|
else
|
|
{
|
|
//prepare for first feature, so that we know paper size to begin with
|
|
atlasMap->prepareForFeature( 0 );
|
|
|
|
mComposition->beginPrint( *printer(), true );
|
|
QPainter painter( printer() );
|
|
|
|
loadAtlasPredefinedScalesFromProject();
|
|
if ( ! atlasMap->beginRender() && !atlasMap->featureFilterErrorString().isEmpty() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Feature filter parser error: %1" ).arg( atlasMap->featureFilterErrorString() ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
QProgressDialog progress( tr( "Rendering maps..." ), tr( "Abort" ), 0, atlasMap->numFeatures(), this );
|
|
progress.setWindowTitle( tr( "Exporting Atlas" ) );
|
|
|
|
for ( int i = 0; i < atlasMap->numFeatures(); ++i )
|
|
{
|
|
progress.setValue( i );
|
|
// process input events in order to allow canceling
|
|
QCoreApplication::processEvents();
|
|
|
|
if ( progress.wasCanceled() )
|
|
{
|
|
atlasMap->endRender();
|
|
break;
|
|
}
|
|
if ( !atlasMap->prepareForFeature( i ) )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Atlas processing error" ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
|
|
//start print on a new page if we're not on the first feature
|
|
mComposition->doPrint( *printer(), painter, i > 0 );
|
|
}
|
|
atlasMap->endRender();
|
|
painter.end();
|
|
}
|
|
|
|
if ( ! mComposition->useAdvancedEffects() )
|
|
{
|
|
//Switch advanced effects back on
|
|
mComposition->setUseAdvancedEffects( true );
|
|
}
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
|
|
void QgsComposer::mActionExportAtlasAsImage_triggered()
|
|
{
|
|
//print whole atlas
|
|
QgsComposition::AtlasMode previousMode = mComposition->atlasMode();
|
|
mComposition->setAtlasMode( QgsComposition::ExportAtlas );
|
|
exportCompositionAsImage( QgsComposer::Atlas );
|
|
mComposition->setAtlasMode( previousMode );
|
|
|
|
if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
|
|
{
|
|
//after atlas output, jump back to preview first feature
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
atlasMap->firstFeature();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionExportAsImage_triggered()
|
|
{
|
|
exportCompositionAsImage( QgsComposer::Single );
|
|
}
|
|
|
|
void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
|
|
{
|
|
if ( !mComposition || !mView )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( containsWmsLayer() )
|
|
{
|
|
showWmsPrintingWarning();
|
|
}
|
|
|
|
QgsSettings settings;
|
|
|
|
// Image size
|
|
int width = ( int )( mComposition->printResolution() * mComposition->paperWidth() / 25.4 );
|
|
int height = ( int )( mComposition-> printResolution() * mComposition->paperHeight() / 25.4 );
|
|
int dpi = mComposition->printResolution();
|
|
|
|
int memuse = width * height * 3 / 1000000; // pixmap + image
|
|
QgsDebugMsg( QString( "Image %1x%2" ).arg( width ).arg( height ) );
|
|
QgsDebugMsg( QString( "memuse = %1" ).arg( memuse ) );
|
|
|
|
if ( memuse > 200 ) // about 4500x4500
|
|
{
|
|
int answer = QMessageBox::warning( nullptr, tr( "Big image" ),
|
|
tr( "To create image %1x%2 requires about %3 MB of memory. Proceed?" )
|
|
.arg( width ).arg( height ).arg( memuse ),
|
|
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok );
|
|
|
|
raise();
|
|
if ( answer == QMessageBox::Cancel )
|
|
return;
|
|
}
|
|
|
|
//get some defaults from the composition
|
|
bool cropToContents = mComposition->customProperty( QStringLiteral( "imageCropToContents" ), false ).toBool();
|
|
int marginTop = mComposition->customProperty( QStringLiteral( "imageCropMarginTop" ), 0 ).toInt();
|
|
int marginRight = mComposition->customProperty( QStringLiteral( "imageCropMarginRight" ), 0 ).toInt();
|
|
int marginBottom = mComposition->customProperty( QStringLiteral( "imageCropMarginBottom" ), 0 ).toInt();
|
|
int marginLeft = mComposition->customProperty( QStringLiteral( "imageCropMarginLeft" ), 0 ).toInt();
|
|
|
|
QgsComposerImageExportOptionsDialog imageDlg( this );
|
|
imageDlg.setImageSize( QSizeF( mComposition->paperWidth(), mComposition->paperHeight() ) );
|
|
imageDlg.setResolution( mComposition->printResolution() );
|
|
imageDlg.setCropToContents( cropToContents );
|
|
imageDlg.setCropMargins( marginTop, marginRight, marginBottom, marginLeft );
|
|
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
if ( mode == QgsComposer::Single )
|
|
{
|
|
QString outputFileName = QString();
|
|
|
|
if ( atlasMap->enabled() && mComposition->atlasMode() == QgsComposition::PreviewAtlas )
|
|
{
|
|
QString lastUsedDir = settings.value( QStringLiteral( "UI/lastSaveAsImageDir" ), QDir::homePath() ).toString();
|
|
outputFileName = QDir( lastUsedDir ).filePath( atlasMap->currentFilename() );
|
|
}
|
|
|
|
#ifdef Q_OS_MAC
|
|
mQgis->activateWindow();
|
|
this->raise();
|
|
#endif
|
|
QPair<QString, QString> fileNExt = QgsGuiUtils::getSaveAsImageName( this, tr( "Save composition as" ), outputFileName );
|
|
this->activateWindow();
|
|
|
|
if ( fileNExt.first.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !imageDlg.exec() )
|
|
return;
|
|
|
|
cropToContents = imageDlg.cropToContents();
|
|
imageDlg.getCropMargins( marginTop, marginRight, marginBottom, marginLeft );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropToContents" ), cropToContents );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginTop" ), marginTop );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginRight" ), marginRight );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginBottom" ), marginBottom );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginLeft" ), marginLeft );
|
|
|
|
mView->setPaintingEnabled( false );
|
|
|
|
int worldFilePageNo = -1;
|
|
if ( mComposition->referenceMap() )
|
|
{
|
|
worldFilePageNo = mComposition->referenceMap()->page() - 1;
|
|
}
|
|
|
|
for ( int i = 0; i < mComposition->numPages(); ++i )
|
|
{
|
|
if ( !mComposition->shouldExportPage( i + 1 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QImage image;
|
|
QRectF bounds;
|
|
if ( cropToContents )
|
|
{
|
|
if ( mComposition->numPages() == 1 )
|
|
{
|
|
// single page, so include everything
|
|
bounds = mComposition->compositionBounds( true );
|
|
}
|
|
else
|
|
{
|
|
// multi page, so just clip to items on current page
|
|
bounds = mComposition->pageItemBounds( i, true );
|
|
}
|
|
if ( bounds.width() <= 0 || bounds.height() <= 0 )
|
|
{
|
|
//invalid size, skip page
|
|
continue;
|
|
}
|
|
double pixelToMm = 25.4 / mComposition->printResolution();
|
|
bounds = bounds.adjusted( -marginLeft * pixelToMm,
|
|
-marginTop * pixelToMm,
|
|
marginRight * pixelToMm,
|
|
marginBottom * pixelToMm );
|
|
image = mComposition->renderRectAsRaster( bounds, QSize(), imageDlg.resolution() );
|
|
}
|
|
else
|
|
{
|
|
image = mComposition->printPageAsRaster( i, QSize( imageDlg.imageWidth(), imageDlg.imageHeight() ) );
|
|
}
|
|
|
|
if ( image.isNull() )
|
|
{
|
|
QMessageBox::warning( nullptr, tr( "Memory Allocation Error" ),
|
|
tr( "Trying to create image #%1( %2x%3 @ %4dpi ) "
|
|
"may result in a memory overflow.\n"
|
|
"Please try a lower resolution or a smaller papersize" )
|
|
.arg( i + 1 ).arg( width ).arg( height ).arg( dpi ),
|
|
QMessageBox::Ok, QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
return;
|
|
}
|
|
bool saveOk;
|
|
QString outputFilePath;
|
|
if ( i == 0 )
|
|
{
|
|
outputFilePath = fileNExt.first;
|
|
}
|
|
else
|
|
{
|
|
QFileInfo fi( fileNExt.first );
|
|
outputFilePath = fi.absolutePath() + '/' + fi.baseName() + '_' + QString::number( i + 1 ) + '.' + fi.suffix();
|
|
}
|
|
|
|
saveOk = saveImage( image, outputFilePath, fileNExt.second );
|
|
|
|
if ( !saveOk )
|
|
{
|
|
QMessageBox::warning( this, tr( "Image export error" ),
|
|
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( fileNExt.first ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
return;
|
|
}
|
|
|
|
if ( i == worldFilePageNo )
|
|
{
|
|
mComposition->georeferenceOutput( outputFilePath, nullptr, bounds, imageDlg.resolution() );
|
|
|
|
if ( mComposition->generateWorldFile() )
|
|
{
|
|
// should generate world file for this page
|
|
double a, b, c, d, e, f;
|
|
if ( bounds.isValid() )
|
|
mComposition->computeWorldFileParameters( bounds, a, b, c, d, e, f );
|
|
else
|
|
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
|
|
|
|
QFileInfo fi( outputFilePath );
|
|
// build the world file name
|
|
QString outputSuffix = fi.suffix();
|
|
QString worldFileName = fi.absolutePath() + '/' + fi.baseName() + '.'
|
|
+ outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) + 'w';
|
|
|
|
writeWorldFile( worldFileName, a, b, c, d, e, f );
|
|
}
|
|
}
|
|
}
|
|
|
|
mView->setPaintingEnabled( true );
|
|
}
|
|
else
|
|
{
|
|
// else, it has an atlas to render, so a directory must first be selected
|
|
if ( atlasMap->filenamePattern().isEmpty() )
|
|
{
|
|
int res = QMessageBox::warning( nullptr, tr( "Empty filename pattern" ),
|
|
tr( "The filename pattern is empty. A default one will be used." ),
|
|
QMessageBox::Ok | QMessageBox::Cancel,
|
|
QMessageBox::Ok );
|
|
if ( res == QMessageBox::Cancel )
|
|
{
|
|
return;
|
|
}
|
|
atlasMap->setFilenamePattern( QStringLiteral( "'output_'||@atlas_featurenumber" ) );
|
|
}
|
|
|
|
QgsSettings myQSettings;
|
|
QString lastUsedDir = myQSettings.value( QStringLiteral( "UI/lastSaveAtlasAsImagesDir" ), QDir::homePath() ).toString();
|
|
|
|
QFileDialog dlg( this, tr( "Export atlas to directory" ) );
|
|
dlg.setFileMode( QFileDialog::Directory );
|
|
dlg.setOption( QFileDialog::ShowDirsOnly, true );
|
|
dlg.setDirectory( lastUsedDir );
|
|
|
|
if ( !dlg.exec() )
|
|
{
|
|
return;
|
|
}
|
|
QStringList s = dlg.selectedFiles();
|
|
if ( s.empty() || s.at( 0 ).isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
QString dir = s.at( 0 );
|
|
QString format = atlasMap->fileFormat();
|
|
QString fileExt = '.' + format;
|
|
|
|
if ( dir.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
// test directory (if it exists and is writable)
|
|
if ( !QDir( dir ).exists() || !QFileInfo( dir ).isWritable() )
|
|
{
|
|
QMessageBox::warning( nullptr, tr( "Unable to write into the directory" ),
|
|
tr( "The given output directory is not writable. Canceling." ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
return;
|
|
}
|
|
|
|
if ( !imageDlg.exec() )
|
|
return;
|
|
|
|
cropToContents = imageDlg.cropToContents();
|
|
imageDlg.getCropMargins( marginTop, marginRight, marginBottom, marginLeft );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropToContents" ), cropToContents );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginTop" ), marginTop );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginRight" ), marginRight );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginBottom" ), marginBottom );
|
|
mComposition->setCustomProperty( QStringLiteral( "imageCropMarginLeft" ), marginLeft );
|
|
|
|
myQSettings.setValue( QStringLiteral( "UI/lastSaveAtlasAsImagesDir" ), dir );
|
|
|
|
// So, now we can render the atlas
|
|
mView->setPaintingEnabled( false );
|
|
QApplication::setOverrideCursor( Qt::BusyCursor );
|
|
|
|
loadAtlasPredefinedScalesFromProject();
|
|
if ( ! atlasMap->beginRender() && !atlasMap->featureFilterErrorString().isEmpty() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Feature filter parser error: %1" ).arg( atlasMap->featureFilterErrorString() ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
|
|
QProgressDialog progress( tr( "Rendering maps..." ), tr( "Abort" ), 0, atlasMap->numFeatures(), this );
|
|
progress.setWindowTitle( tr( "Exporting Atlas" ) );
|
|
|
|
for ( int feature = 0; feature < atlasMap->numFeatures(); ++feature )
|
|
{
|
|
progress.setValue( feature );
|
|
// process input events in order to allow canceling
|
|
QCoreApplication::processEvents();
|
|
|
|
if ( progress.wasCanceled() )
|
|
{
|
|
atlasMap->endRender();
|
|
break;
|
|
}
|
|
if ( ! atlasMap->prepareForFeature( feature ) )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Atlas processing error" ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
|
|
QString filename = QDir( dir ).filePath( atlasMap->currentFilename() ) + fileExt;
|
|
|
|
int worldFilePageNo = -1;
|
|
if ( mComposition->referenceMap() )
|
|
{
|
|
worldFilePageNo = mComposition->referenceMap()->page() - 1;
|
|
}
|
|
|
|
for ( int i = 0; i < mComposition->numPages(); ++i )
|
|
{
|
|
if ( !mComposition->shouldExportPage( i + 1 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QImage image;
|
|
QRectF bounds;
|
|
if ( cropToContents )
|
|
{
|
|
if ( mComposition->numPages() == 1 )
|
|
{
|
|
// single page, so include everything
|
|
bounds = mComposition->compositionBounds( true );
|
|
}
|
|
else
|
|
{
|
|
// multi page, so just clip to items on current page
|
|
bounds = mComposition->pageItemBounds( i, true );
|
|
}
|
|
if ( bounds.width() <= 0 || bounds.height() <= 0 )
|
|
{
|
|
//invalid size, skip page
|
|
continue;
|
|
}
|
|
double pixelToMm = 25.4 / mComposition->printResolution();
|
|
bounds = bounds.adjusted( -marginLeft * pixelToMm,
|
|
-marginTop * pixelToMm,
|
|
marginRight * pixelToMm,
|
|
marginBottom * pixelToMm );
|
|
image = mComposition->renderRectAsRaster( bounds, QSize(), imageDlg.resolution() );
|
|
}
|
|
else
|
|
{
|
|
//note - we can't safely use the preset width/height set in imageDlg here,
|
|
//as the atlas may have differing page size. So use resolution instead.
|
|
image = mComposition->printPageAsRaster( i, QSize(), imageDlg.resolution() );
|
|
}
|
|
|
|
QString imageFilename = filename;
|
|
|
|
if ( i != 0 )
|
|
{
|
|
//append page number
|
|
QFileInfo fi( filename );
|
|
imageFilename = fi.absolutePath() + '/' + fi.baseName() + '_' + QString::number( i + 1 ) + '.' + fi.suffix();
|
|
}
|
|
|
|
bool saveOk = saveImage( image, imageFilename, format );
|
|
if ( !saveOk )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( imageFilename ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
return;
|
|
}
|
|
|
|
if ( i == worldFilePageNo )
|
|
{
|
|
mComposition->georeferenceOutput( imageFilename, nullptr, bounds, imageDlg.resolution() );
|
|
|
|
if ( mComposition->generateWorldFile() )
|
|
{
|
|
// should generate world file for this page
|
|
double a, b, c, d, e, f;
|
|
if ( bounds.isValid() )
|
|
mComposition->computeWorldFileParameters( bounds, a, b, c, d, e, f );
|
|
else
|
|
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
|
|
|
|
QFileInfo fi( imageFilename );
|
|
// build the world file name
|
|
QString outputSuffix = fi.suffix();
|
|
QString worldFileName = fi.absolutePath() + '/' + fi.baseName() + '.'
|
|
+ outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) + 'w';
|
|
|
|
writeWorldFile( worldFileName, a, b, c, d, e, f );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
atlasMap->endRender();
|
|
mView->setPaintingEnabled( true );
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
}
|
|
|
|
bool QgsComposer::saveImage( const QImage &img, const QString &imageFilename, const QString &imageFormat )
|
|
{
|
|
QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
|
|
if ( imageFormat.compare( QLatin1String( "tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String( "tif" ), Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
w.setCompression( 1 ); //use LZW compression
|
|
}
|
|
return w.write( img );
|
|
}
|
|
|
|
void QgsComposer::mActionExportAtlasAsSVG_triggered()
|
|
{
|
|
QgsComposition::AtlasMode previousMode = mComposition->atlasMode();
|
|
mComposition->setAtlasMode( QgsComposition::ExportAtlas );
|
|
exportCompositionAsSVG( QgsComposer::Atlas );
|
|
mComposition->setAtlasMode( previousMode );
|
|
|
|
if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
|
|
{
|
|
//after atlas output, jump back to preview first feature
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
atlasMap->firstFeature();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionExportAsSVG_triggered()
|
|
{
|
|
exportCompositionAsSVG( QgsComposer::Single );
|
|
}
|
|
|
|
// utility class that will hide all items until it's destroyed
|
|
struct QgsItemTempHider
|
|
{
|
|
explicit QgsItemTempHider( const QList<QGraphicsItem *> &items )
|
|
{
|
|
QList<QGraphicsItem *>::const_iterator it = items.begin();
|
|
for ( ; it != items.end(); ++it )
|
|
{
|
|
mItemVisibility[*it] = ( *it )->isVisible();
|
|
( *it )->hide();
|
|
}
|
|
}
|
|
void hideAll()
|
|
{
|
|
QgsItemVisibilityHash::const_iterator it = mItemVisibility.constBegin();
|
|
for ( ; it != mItemVisibility.constEnd(); ++it ) it.key()->hide();
|
|
}
|
|
~QgsItemTempHider()
|
|
{
|
|
QgsItemVisibilityHash::const_iterator it = mItemVisibility.constBegin();
|
|
for ( ; it != mItemVisibility.constEnd(); ++it )
|
|
{
|
|
it.key()->setVisible( it.value() );
|
|
}
|
|
}
|
|
private:
|
|
Q_DISABLE_COPY( QgsItemTempHider )
|
|
typedef QHash<QGraphicsItem *, bool> QgsItemVisibilityHash;
|
|
QgsItemVisibilityHash mItemVisibility;
|
|
};
|
|
|
|
void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
|
|
{
|
|
if ( containsWmsLayer() )
|
|
{
|
|
showWmsPrintingWarning();
|
|
}
|
|
|
|
QString settingsLabel = QStringLiteral( "/UI/displaySVGWarning" );
|
|
QgsSettings settings;
|
|
|
|
bool displaySVGWarning = settings.value( settingsLabel, true ).toBool();
|
|
|
|
if ( displaySVGWarning )
|
|
{
|
|
QgsMessageViewer *m = new QgsMessageViewer( this );
|
|
m->setWindowTitle( tr( "SVG Warning" ) );
|
|
m->setCheckBoxText( tr( "Don't show this message again" ) );
|
|
m->setCheckBoxState( Qt::Unchecked );
|
|
m->setCheckBoxVisible( true );
|
|
m->setCheckBoxQgsSettingsLabel( settingsLabel );
|
|
m->setMessageAsHtml( tr( "<p>The SVG export function in QGIS has several "
|
|
"problems due to bugs and deficiencies in the " )
|
|
+ tr( "Qt4 svg code. In particular, there are problems "
|
|
"with layers not being clipped to the map "
|
|
"bounding box.</p>" )
|
|
+ tr( "If you require a vector-based output file from "
|
|
"QGIS it is suggested that you try printing "
|
|
"to PostScript if the SVG output is not "
|
|
"satisfactory."
|
|
"</p>" ) );
|
|
m->exec();
|
|
}
|
|
|
|
QgsAtlasComposition *atlasMap = &mComposition->atlasComposition();
|
|
|
|
QString outputFileName;
|
|
QString outputDir;
|
|
bool groupLayers = false;
|
|
bool prevSettingLabelsAsOutlines = QgsProject::instance()->readBoolEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ), true );
|
|
bool clipToContent = false;
|
|
double marginTop = 0.0;
|
|
double marginRight = 0.0;
|
|
double marginBottom = 0.0;
|
|
double marginLeft = 0.0;
|
|
|
|
if ( mode == QgsComposer::Single )
|
|
{
|
|
QString lastUsedFile = settings.value( QStringLiteral( "UI/lastSaveAsSvgFile" ), "qgis.svg" ).toString();
|
|
QFileInfo file( lastUsedFile );
|
|
|
|
if ( atlasMap->enabled() && mComposition->atlasMode() == QgsComposition::PreviewAtlas )
|
|
{
|
|
outputFileName = QDir( file.path() ).filePath( atlasMap->currentFilename() ) + ".svg";
|
|
}
|
|
else
|
|
{
|
|
outputFileName = file.path();
|
|
}
|
|
|
|
// open file dialog
|
|
#ifdef Q_OS_MAC
|
|
mQgis->activateWindow();
|
|
this->raise();
|
|
#endif
|
|
outputFileName = QFileDialog::getSaveFileName(
|
|
this,
|
|
tr( "Save composition as" ),
|
|
outputFileName,
|
|
tr( "SVG Format" ) + " (*.svg *.SVG)" );
|
|
this->activateWindow();
|
|
|
|
if ( outputFileName.isEmpty() )
|
|
return;
|
|
|
|
if ( !outputFileName.endsWith( QLatin1String( ".svg" ), Qt::CaseInsensitive ) )
|
|
{
|
|
outputFileName += QLatin1String( ".svg" );
|
|
}
|
|
|
|
settings.setValue( QStringLiteral( "UI/lastSaveAsSvgFile" ), outputFileName );
|
|
}
|
|
else
|
|
{
|
|
// If we have an Atlas
|
|
if ( atlasMap->filenamePattern().isEmpty() )
|
|
{
|
|
int res = QMessageBox::warning( nullptr, tr( "Empty filename pattern" ),
|
|
tr( "The filename pattern is empty. A default one will be used." ),
|
|
QMessageBox::Ok | QMessageBox::Cancel,
|
|
QMessageBox::Ok );
|
|
if ( res == QMessageBox::Cancel )
|
|
{
|
|
return;
|
|
}
|
|
atlasMap->setFilenamePattern( QStringLiteral( "'output_'||@atlas_featurenumber" ) );
|
|
}
|
|
|
|
QgsSettings myQSettings;
|
|
QString lastUsedDir = myQSettings.value( QStringLiteral( "UI/lastSaveAtlasAsSvgDir" ), QDir::homePath() ).toString();
|
|
|
|
// open file dialog
|
|
outputDir = QFileDialog::getExistingDirectory( this,
|
|
tr( "Export atlas to directory" ),
|
|
lastUsedDir,
|
|
QFileDialog::ShowDirsOnly );
|
|
|
|
if ( outputDir.isEmpty() )
|
|
{
|
|
return;
|
|
}
|
|
// test directory (if it exists and is writable)
|
|
if ( !QDir( outputDir ).exists() || !QFileInfo( outputDir ).isWritable() )
|
|
{
|
|
QMessageBox::warning( nullptr, tr( "Unable to write into the directory" ),
|
|
tr( "The given output directory is not writable. Canceling." ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
return;
|
|
}
|
|
myQSettings.setValue( QStringLiteral( "UI/lastSaveAtlasAsSvgDir" ), outputDir );
|
|
}
|
|
|
|
// open options dialog
|
|
QDialog dialog;
|
|
Ui::QgsSvgExportOptionsDialog options;
|
|
options.setupUi( &dialog );
|
|
options.chkTextAsOutline->setChecked( prevSettingLabelsAsOutlines );
|
|
options.chkMapLayersAsGroup->setChecked( mComposition->customProperty( QStringLiteral( "svgGroupLayers" ), false ).toBool() );
|
|
options.mClipToContentGroupBox->setChecked( mComposition->customProperty( QStringLiteral( "svgCropToContents" ), false ).toBool() );
|
|
options.mTopMarginSpinBox->setValue( mComposition->customProperty( QStringLiteral( "svgCropMarginTop" ), 0 ).toInt() );
|
|
options.mRightMarginSpinBox->setValue( mComposition->customProperty( QStringLiteral( "svgCropMarginRight" ), 0 ).toInt() );
|
|
options.mBottomMarginSpinBox->setValue( mComposition->customProperty( QStringLiteral( "svgCropMarginBottom" ), 0 ).toInt() );
|
|
options.mLeftMarginSpinBox->setValue( mComposition->customProperty( QStringLiteral( "svgCropMarginLeft" ), 0 ).toInt() );
|
|
|
|
if ( dialog.exec() != QDialog::Accepted )
|
|
return;
|
|
|
|
groupLayers = options.chkMapLayersAsGroup->isChecked();
|
|
clipToContent = options.mClipToContentGroupBox->isChecked();
|
|
marginTop = options.mTopMarginSpinBox->value();
|
|
marginRight = options.mRightMarginSpinBox->value();
|
|
marginBottom = options.mBottomMarginSpinBox->value();
|
|
marginLeft = options.mLeftMarginSpinBox->value();
|
|
|
|
//save dialog settings
|
|
mComposition->setCustomProperty( QStringLiteral( "svgGroupLayers" ), groupLayers );
|
|
mComposition->setCustomProperty( QStringLiteral( "svgCropToContents" ), clipToContent );
|
|
mComposition->setCustomProperty( QStringLiteral( "svgCropMarginTop" ), marginTop );
|
|
mComposition->setCustomProperty( QStringLiteral( "svgCropMarginRight" ), marginRight );
|
|
mComposition->setCustomProperty( QStringLiteral( "svgCropMarginBottom" ), marginBottom );
|
|
mComposition->setCustomProperty( QStringLiteral( "svgCropMarginLeft" ), marginLeft );
|
|
|
|
//temporarily override label draw outlines setting
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ), options.chkTextAsOutline->isChecked() );
|
|
|
|
mView->setPaintingEnabled( false );
|
|
|
|
int featureI = 0;
|
|
if ( mode == QgsComposer::Atlas )
|
|
{
|
|
loadAtlasPredefinedScalesFromProject();
|
|
if ( ! atlasMap->beginRender() && !atlasMap->featureFilterErrorString().isEmpty() )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Feature filter parser error: %1" ).arg( atlasMap->featureFilterErrorString() ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ), prevSettingLabelsAsOutlines );
|
|
return;
|
|
}
|
|
}
|
|
QProgressDialog progress( tr( "Rendering maps..." ), tr( "Abort" ), 0, atlasMap->numFeatures(), this );
|
|
progress.setWindowTitle( tr( "Exporting Atlas" ) );
|
|
|
|
do
|
|
{
|
|
if ( mode == QgsComposer::Atlas )
|
|
{
|
|
if ( atlasMap->numFeatures() == 0 )
|
|
break;
|
|
|
|
progress.setValue( featureI );
|
|
// process input events in order to allow aborting
|
|
QCoreApplication::processEvents();
|
|
if ( progress.wasCanceled() )
|
|
{
|
|
atlasMap->endRender();
|
|
break;
|
|
}
|
|
if ( !atlasMap->prepareForFeature( featureI ) )
|
|
{
|
|
QMessageBox::warning( this, tr( "Atlas processing error" ),
|
|
tr( "Atlas processing error" ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ), prevSettingLabelsAsOutlines );
|
|
return;
|
|
}
|
|
outputFileName = QDir( outputDir ).filePath( atlasMap->currentFilename() ) + ".svg";
|
|
}
|
|
|
|
if ( !groupLayers )
|
|
{
|
|
for ( int i = 0; i < mComposition->numPages(); ++i )
|
|
{
|
|
if ( !mComposition->shouldExportPage( i + 1 ) )
|
|
{
|
|
continue;
|
|
}
|
|
QSvgGenerator generator;
|
|
generator.setTitle( QgsProject::instance()->title() );
|
|
QString currentFileName = outputFileName;
|
|
if ( i == 0 )
|
|
{
|
|
generator.setFileName( outputFileName );
|
|
}
|
|
else
|
|
{
|
|
QFileInfo fi( outputFileName );
|
|
currentFileName = fi.absolutePath() + '/' + fi.baseName() + '_' + QString::number( i + 1 ) + '.' + fi.suffix();
|
|
generator.setFileName( currentFileName );
|
|
}
|
|
|
|
QRectF bounds;
|
|
if ( clipToContent )
|
|
{
|
|
if ( mComposition->numPages() == 1 )
|
|
{
|
|
// single page, so include everything
|
|
bounds = mComposition->compositionBounds( true );
|
|
}
|
|
else
|
|
{
|
|
// multi page, so just clip to items on current page
|
|
bounds = mComposition->pageItemBounds( i, true );
|
|
}
|
|
bounds = bounds.adjusted( -marginLeft, -marginTop, marginRight, marginBottom );
|
|
}
|
|
else
|
|
bounds = QRectF( 0, 0, mComposition->paperWidth(), mComposition->paperHeight() );
|
|
|
|
//width in pixel
|
|
int width = ( int )( bounds.width() * mComposition->printResolution() / 25.4 );
|
|
//height in pixel
|
|
int height = ( int )( bounds.height() * mComposition->printResolution() / 25.4 );
|
|
if ( width == 0 || height == 0 )
|
|
{
|
|
//invalid size, skip this page
|
|
continue;
|
|
}
|
|
generator.setSize( QSize( width, height ) );
|
|
generator.setViewBox( QRect( 0, 0, width, height ) );
|
|
generator.setResolution( mComposition->printResolution() ); //because the rendering is done in mm, convert the dpi
|
|
|
|
QPainter p;
|
|
bool createOk = p.begin( &generator );
|
|
if ( !createOk )
|
|
{
|
|
QMessageBox::warning( this, tr( "SVG export error" ),
|
|
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( currentFileName ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ), prevSettingLabelsAsOutlines );
|
|
return;
|
|
}
|
|
|
|
if ( clipToContent )
|
|
mComposition->renderRect( &p, bounds );
|
|
else
|
|
mComposition->renderPage( &p, i );
|
|
p.end();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//width and height in pixel
|
|
const int pageWidth = ( int )( mComposition->paperWidth() * mComposition->printResolution() / 25.4 );
|
|
const int pageHeight = ( int )( mComposition->paperHeight() * mComposition->printResolution() / 25.4 );
|
|
QList< QgsPaperItem * > paperItems( mComposition->pages() );
|
|
|
|
for ( int i = 0; i < mComposition->numPages(); ++i )
|
|
{
|
|
if ( !mComposition->shouldExportPage( i + 1 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int width = pageWidth;
|
|
int height = pageHeight;
|
|
|
|
QRectF bounds;
|
|
if ( clipToContent )
|
|
{
|
|
if ( mComposition->numPages() == 1 )
|
|
{
|
|
// single page, so include everything
|
|
bounds = mComposition->compositionBounds( true );
|
|
}
|
|
else
|
|
{
|
|
// multi page, so just clip to items on current page
|
|
bounds = mComposition->pageItemBounds( i, true );
|
|
}
|
|
bounds = bounds.adjusted( -marginLeft, -marginTop, marginRight, marginBottom );
|
|
width = bounds.width() * mComposition->printResolution() / 25.4;
|
|
height = bounds.height() * mComposition->printResolution() / 25.4;
|
|
}
|
|
|
|
if ( width == 0 || height == 0 )
|
|
{
|
|
//invalid size, skip this page
|
|
continue;
|
|
}
|
|
|
|
QDomDocument svg;
|
|
QDomNode svgDocRoot;
|
|
QgsPaperItem *paperItem = paperItems[i];
|
|
const QRectF paperRect = QRectF( paperItem->pos().x(),
|
|
paperItem->pos().y(),
|
|
paperItem->rect().width(),
|
|
paperItem->rect().height() );
|
|
|
|
QList<QGraphicsItem *> items = mComposition->items( paperRect,
|
|
Qt::IntersectsItemBoundingRect,
|
|
Qt::AscendingOrder );
|
|
if ( ! items.isEmpty()
|
|
&& dynamic_cast<QgsPaperGrid *>( items.last() )
|
|
&& !mComposition->gridVisible() ) items.pop_back();
|
|
QgsItemTempHider itemsHider( items );
|
|
int composerItemLayerIdx = 0;
|
|
QList<QGraphicsItem *>::const_iterator it = items.constBegin();
|
|
for ( unsigned svgLayerId = 1; it != items.constEnd(); ++svgLayerId )
|
|
{
|
|
itemsHider.hideAll();
|
|
QgsComposerItem *composerItem = dynamic_cast<QgsComposerItem *>( *it );
|
|
QString layerName( "Layer " + QString::number( svgLayerId ) );
|
|
if ( composerItem && composerItem->numberExportLayers() )
|
|
{
|
|
composerItem->show();
|
|
composerItem->setCurrentExportLayer( composerItemLayerIdx );
|
|
++composerItemLayerIdx;
|
|
}
|
|
else
|
|
{
|
|
// show all items until the next item that renders on a separate layer
|
|
for ( ; it != items.constEnd(); ++it )
|
|
{
|
|
composerItem = dynamic_cast<QgsComposerMap *>( *it );
|
|
if ( composerItem && composerItem->numberExportLayers() )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
( *it )->show();
|
|
}
|
|
}
|
|
}
|
|
|
|
QBuffer svgBuffer;
|
|
{
|
|
QSvgGenerator generator;
|
|
generator.setTitle( QgsProject::instance()->title() );
|
|
generator.setOutputDevice( &svgBuffer );
|
|
generator.setSize( QSize( width, height ) );
|
|
generator.setViewBox( QRect( 0, 0, width, height ) );
|
|
generator.setResolution( mComposition->printResolution() ); //because the rendering is done in mm, convert the dpi
|
|
|
|
QPainter p( &generator );
|
|
if ( clipToContent )
|
|
mComposition->renderRect( &p, bounds );
|
|
else
|
|
mComposition->renderPage( &p, i );
|
|
}
|
|
// post-process svg output to create groups in a single svg file
|
|
// we create inkscape layers since it's nice and clean and free
|
|
// and fully svg compatible
|
|
{
|
|
svgBuffer.close();
|
|
svgBuffer.open( QIODevice::ReadOnly );
|
|
QDomDocument doc;
|
|
QString errorMsg;
|
|
int errorLine;
|
|
if ( ! doc.setContent( &svgBuffer, false, &errorMsg, &errorLine ) )
|
|
QMessageBox::warning( nullptr, tr( "SVG error" ), tr( "There was an error in SVG output for SVG layer " ) + layerName + tr( " on page " ) + QString::number( i + 1 ) + '(' + errorMsg + ')' );
|
|
if ( 1 == svgLayerId )
|
|
{
|
|
svg = QDomDocument( doc.doctype() );
|
|
svg.appendChild( svg.importNode( doc.firstChild(), false ) );
|
|
svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral( "svg" ) ).at( 0 ), false );
|
|
svgDocRoot.toElement().setAttribute( QStringLiteral( "xmlns:inkscape" ), QStringLiteral( "http://www.inkscape.org/namespaces/inkscape" ) );
|
|
svg.appendChild( svgDocRoot );
|
|
}
|
|
QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral( "g" ) ).at( 0 ), true );
|
|
mainGroup.toElement().setAttribute( QStringLiteral( "id" ), layerName );
|
|
mainGroup.toElement().setAttribute( QStringLiteral( "inkscape:label" ), layerName );
|
|
mainGroup.toElement().setAttribute( QStringLiteral( "inkscape:groupmode" ), QStringLiteral( "layer" ) );
|
|
QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral( "defs" ) ).at( 0 ), true );
|
|
svgDocRoot.appendChild( defs );
|
|
svgDocRoot.appendChild( mainGroup );
|
|
}
|
|
|
|
if ( composerItem && composerItem->numberExportLayers() && composerItem->numberExportLayers() == composerItemLayerIdx ) // restore and pass to next item
|
|
{
|
|
composerItem->setCurrentExportLayer();
|
|
composerItemLayerIdx = 0;
|
|
++it;
|
|
}
|
|
}
|
|
QFileInfo fi( outputFileName );
|
|
QString currentFileName = i == 0 ? outputFileName : fi.absolutePath() + '/' + fi.baseName() + '_' + QString::number( i + 1 ) + '.' + fi.suffix();
|
|
QFile out( currentFileName );
|
|
bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
|
|
if ( !openOk )
|
|
{
|
|
QMessageBox::warning( this, tr( "SVG export error" ),
|
|
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( currentFileName ),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok );
|
|
mView->setPaintingEnabled( true );
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ), prevSettingLabelsAsOutlines );
|
|
return;
|
|
}
|
|
|
|
out.write( svg.toByteArray() );
|
|
}
|
|
}
|
|
featureI++;
|
|
}
|
|
while ( mode == QgsComposer::Atlas && featureI < atlasMap->numFeatures() );
|
|
|
|
if ( mode == QgsComposer::Atlas )
|
|
atlasMap->endRender();
|
|
|
|
mView->setPaintingEnabled( true );
|
|
QgsProject::instance()->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ), prevSettingLabelsAsOutlines );
|
|
}
|
|
|
|
void QgsComposer::mActionSelectMoveItem_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::Select );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddNewMap_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddMap );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddNewLegend_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddLegend );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddNewLabel_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddLabel );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddNewScalebar_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddScalebar );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddImage_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddPicture );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddRectangle_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddRectangle );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddTriangle_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddTriangle );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddEllipse_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddEllipse );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddPolygon_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddPolygon );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddPolyline_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddPolyline );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddTable_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddTable );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddAttributeTable_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddAttributeTable );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddHtml_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddHtml );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAddArrow_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::AddArrow );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionSaveProject_triggered()
|
|
{
|
|
mQgis->actionSaveProject()->trigger();
|
|
}
|
|
|
|
void QgsComposer::mActionNewComposer_triggered()
|
|
{
|
|
QString title;
|
|
if ( !mQgis->uniqueComposerTitle( this, title, true ) )
|
|
{
|
|
return;
|
|
}
|
|
mQgis->createNewComposer( title );
|
|
}
|
|
|
|
void QgsComposer::mActionDuplicateComposer_triggered()
|
|
{
|
|
QString newTitle;
|
|
if ( !mQgis->uniqueComposerTitle( this, newTitle, false, mComposition->name() + tr( " copy" ) ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// provide feedback, since loading of template into duplicate composer will be hidden
|
|
QDialog *dlg = new QgsBusyIndicatorDialog( tr( "Duplicating composer..." ) );
|
|
dlg->setStyleSheet( mQgis->styleSheet() );
|
|
dlg->show();
|
|
|
|
QgsComposer *newComposer = mQgis->duplicateComposer( this, newTitle );
|
|
|
|
dlg->close();
|
|
delete dlg;
|
|
dlg = nullptr;
|
|
|
|
if ( !newComposer )
|
|
{
|
|
QMessageBox::warning( this, tr( "Duplicate Composer" ),
|
|
tr( "Composer duplication failed." ) );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionComposerManager_triggered()
|
|
{
|
|
// NOTE: Avoid crash where composer that spawned modal manager from toolbar ends up
|
|
// being deleted by user, but event loop tries to return to composer on manager close
|
|
// (does not seem to be an issue for menu action)
|
|
QTimer::singleShot( 0, mQgis->actionShowComposerManager(), SLOT( trigger() ) );
|
|
}
|
|
|
|
void QgsComposer::mActionSaveAsTemplate_triggered()
|
|
{
|
|
//show file dialog
|
|
QgsSettings settings;
|
|
QString lastSaveDir = settings.value( QStringLiteral( "UI/lastComposerTemplateDir" ), QDir::homePath() ).toString();
|
|
#ifdef Q_OS_MAC
|
|
mQgis->activateWindow();
|
|
this->raise();
|
|
#endif
|
|
QString saveFileName = QFileDialog::getSaveFileName(
|
|
this,
|
|
tr( "Save template" ),
|
|
lastSaveDir,
|
|
tr( "Composer templates" ) + " (*.qpt *.QPT)" );
|
|
if ( saveFileName.isEmpty() )
|
|
return;
|
|
|
|
QFileInfo saveFileInfo( saveFileName );
|
|
//check if suffix has been added
|
|
if ( saveFileInfo.suffix().isEmpty() )
|
|
{
|
|
QString saveFileNameWithSuffix = saveFileName.append( ".qpt" );
|
|
saveFileInfo = QFileInfo( saveFileNameWithSuffix );
|
|
}
|
|
settings.setValue( QStringLiteral( "UI/lastComposerTemplateDir" ), saveFileInfo.absolutePath() );
|
|
|
|
QFile templateFile( saveFileName );
|
|
if ( !templateFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QDomDocument saveDocument;
|
|
QgsProject::instance()->layoutManager()->saveAsTemplate( mComposition->name(), saveDocument );
|
|
|
|
if ( templateFile.write( saveDocument.toByteArray() ) == -1 )
|
|
{
|
|
QMessageBox::warning( nullptr, tr( "Save error" ), tr( "Error, could not save file" ) );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionLoadFromTemplate_triggered()
|
|
{
|
|
if ( !mComposition )
|
|
return;
|
|
|
|
QgsSettings settings;
|
|
QString openFileDir = settings.value( QStringLiteral( "UI/lastComposerTemplateDir" ), QDir::homePath() ).toString();
|
|
QString openFileString = QFileDialog::getOpenFileName( nullptr, tr( "Load template" ), openFileDir, QStringLiteral( "*.qpt" ) );
|
|
|
|
if ( openFileString.isEmpty() )
|
|
{
|
|
return; //canceled by the user
|
|
}
|
|
|
|
QFileInfo openFileInfo( openFileString );
|
|
settings.setValue( QStringLiteral( "UI/LastComposerTemplateDir" ), openFileInfo.absolutePath() );
|
|
|
|
QFile templateFile( openFileString );
|
|
if ( !templateFile.open( QIODevice::ReadOnly ) )
|
|
{
|
|
QMessageBox::warning( this, tr( "Read error" ), tr( "Error, could not read file" ) );
|
|
return;
|
|
}
|
|
|
|
QDomDocument templateDoc;
|
|
if ( templateDoc.setContent( &templateFile ) )
|
|
{
|
|
loadFromTemplate( templateDoc, false );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionMoveItemContent_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::MoveItemContent );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionEditNodesItem_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::EditNodesItem );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionPan_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->setCurrentTool( QgsComposerView::Pan );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionGroupItems_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->groupItems();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionUngroupItems_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->ungroupItems();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionLockItems_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->lockSelectedItems();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionUnlockAll_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->unlockAllItems();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::actionCutTriggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->copyItems( QgsComposerView::ClipboardModeCut );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::actionCopyTriggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->copyItems( QgsComposerView::ClipboardModeCopy );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::actionPasteTriggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
QPointF pt = mView->mapToScene( mView->mapFromGlobal( QCursor::pos() ) );
|
|
//TODO - use a better way of determining whether paste was triggered by keystroke
|
|
//or menu item
|
|
if ( ( pt.x() < 0 ) || ( pt.y() < 0 ) )
|
|
{
|
|
//action likely triggered by menu, paste items in center of screen
|
|
mView->pasteItems( QgsComposerView::PasteModeCenter );
|
|
}
|
|
else
|
|
{
|
|
//action likely triggered by keystroke, paste items at cursor position
|
|
mView->pasteItems( QgsComposerView::PasteModeCursor );
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionPasteInPlace_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->pasteItems( QgsComposerView::PasteModeInPlace );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionDeleteSelection_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->deleteSelectedItems();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionSelectAll_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->selectAll();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionDeselectAll_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->selectNone();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionInvertSelection_triggered()
|
|
{
|
|
if ( mView )
|
|
{
|
|
mView->selectInvert();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionSelectNextAbove_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->selectNextByZOrder( QgsComposition::ZValueAbove );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionSelectNextBelow_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->selectNextByZOrder( QgsComposition::ZValueBelow );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionRaiseItems_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->raiseSelectedItems();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionLowerItems_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->lowerSelectedItems();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionMoveItemsToTop_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->moveSelectedItemsToTop();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionMoveItemsToBottom_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->moveSelectedItemsToBottom();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAlignLeft_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->alignSelectedItemsLeft();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAlignHCenter_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->alignSelectedItemsHCenter();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAlignRight_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->alignSelectedItemsRight();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAlignTop_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->alignSelectedItemsTop();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAlignVCenter_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->alignSelectedItemsVCenter();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionAlignBottom_triggered()
|
|
{
|
|
if ( mComposition )
|
|
{
|
|
mComposition->alignSelectedItemsBottom();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionUndo_triggered()
|
|
{
|
|
if ( mComposition && mComposition->undoStack() )
|
|
{
|
|
mComposition->undoStack()->undo();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionRedo_triggered()
|
|
{
|
|
if ( mComposition && mComposition->undoStack() )
|
|
{
|
|
mComposition->undoStack()->redo();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::closeEvent( QCloseEvent *e )
|
|
{
|
|
Q_UNUSED( e );
|
|
emit aboutToClose();
|
|
saveWindowState();
|
|
}
|
|
|
|
void QgsComposer::moveEvent( QMoveEvent *e )
|
|
{
|
|
Q_UNUSED( e );
|
|
saveWindowState();
|
|
}
|
|
|
|
void QgsComposer::resizeEvent( QResizeEvent *e )
|
|
{
|
|
Q_UNUSED( e );
|
|
|
|
// Move size grip when window is resized
|
|
#if 0
|
|
mSizeGrip->move( rect().bottomRight() - mSizeGrip->rect().bottomRight() );
|
|
#endif
|
|
|
|
saveWindowState();
|
|
}
|
|
|
|
void QgsComposer::saveWindowState()
|
|
{
|
|
QgsSettings settings;
|
|
settings.setValue( QStringLiteral( "Composer/geometry" ), saveGeometry() );
|
|
// store the toolbar/dock widget settings using Qt4 settings API
|
|
settings.setValue( QStringLiteral( "ComposerUI/state" ), saveState() );
|
|
}
|
|
|
|
#include "ui_defaults.h"
|
|
|
|
void QgsComposer::restoreWindowState()
|
|
{
|
|
// restore the toolbar and dock widgets positions using Qt4 settings API
|
|
QgsSettings settings;
|
|
|
|
if ( !restoreState( settings.value( QStringLiteral( "ComposerUI/state" ), QByteArray::fromRawData( ( char * )defaultComposerUIstate, sizeof defaultComposerUIstate ) ).toByteArray() ) )
|
|
{
|
|
QgsDebugMsg( "restore of composer UI state failed" );
|
|
}
|
|
// restore window geometry
|
|
if ( !restoreGeometry( settings.value( QStringLiteral( "Composer/geometry" ), QByteArray::fromRawData( ( char * )defaultComposerUIgeometry, sizeof defaultComposerUIgeometry ) ).toByteArray() ) )
|
|
{
|
|
QgsDebugMsg( "restore of composer UI geometry failed" );
|
|
}
|
|
}
|
|
|
|
void QgsComposer::createCompositionWidget()
|
|
{
|
|
if ( !mComposition )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsCompositionWidget *compositionWidget = new QgsCompositionWidget( mGeneralDock, mComposition );
|
|
compositionWidget->setDockMode( true );
|
|
connect( mComposition, &QgsComposition::paperSizeChanged, compositionWidget, &QgsCompositionWidget::displayCompositionWidthHeight );
|
|
connect( this, &QgsComposer::printAsRasterChanged, compositionWidget, &QgsCompositionWidget::setPrintAsRasterCheckBox );
|
|
connect( compositionWidget, &QgsCompositionWidget::pageOrientationChanged, this, &QgsComposer::pageOrientationChanged );
|
|
mGeneralPropertiesStack->setMainPanel( compositionWidget );
|
|
}
|
|
|
|
void QgsComposer::restoreGridSettings()
|
|
{
|
|
//restore grid settings
|
|
mActionSnapGrid->setChecked( mComposition->snapToGridEnabled() );
|
|
mActionShowGrid->setChecked( mComposition->gridVisible() );
|
|
//restore guide settings
|
|
mActionShowGuides->setChecked( mComposition->snapLinesVisible() );
|
|
mActionSnapGuides->setChecked( mComposition->alignmentSnap() );
|
|
mActionSmartGuides->setChecked( mComposition->smartGuidesEnabled() );
|
|
//general view settings
|
|
mActionShowBoxes->setChecked( mComposition->boundingBoxesVisible() );
|
|
}
|
|
|
|
void QgsComposer::deleteItem( QgsComposerItem * )
|
|
{
|
|
showItemOptions( nullptr );
|
|
}
|
|
|
|
void QgsComposer::setSelectionTool()
|
|
{
|
|
mActionSelectMoveItem->setChecked( true );
|
|
mActionSelectMoveItem_triggered();
|
|
}
|
|
|
|
bool QgsComposer::containsWmsLayer() const
|
|
{
|
|
QList< QgsComposerMap *> maps;
|
|
mComposition->composerItems( maps );
|
|
|
|
Q_FOREACH ( QgsComposerMap *map, maps )
|
|
{
|
|
if ( map->containsWmsLayer() )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool QgsComposer::containsAdvancedEffects() const
|
|
{
|
|
QList< QgsComposerItem *> items;
|
|
mComposition->composerItems( items );
|
|
|
|
Q_FOREACH ( QgsComposerItem *currentItem, items )
|
|
{
|
|
// Check composer item's blend mode
|
|
if ( currentItem->blendMode() != QPainter::CompositionMode_SourceOver )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// If item is a composer map, check if it contains any advanced effects
|
|
if ( QgsComposerMap *currentMap = dynamic_cast<QgsComposerMap *>( currentItem ) )
|
|
{
|
|
if ( currentMap->containsAdvancedEffects() )
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void QgsComposer::showWmsPrintingWarning()
|
|
{
|
|
QString myQSettingsLabel = QStringLiteral( "/UI/displayComposerWMSWarning" );
|
|
QgsSettings myQSettings;
|
|
|
|
bool displayWMSWarning = myQSettings.value( myQSettingsLabel, true ).toBool();
|
|
if ( displayWMSWarning )
|
|
{
|
|
QgsMessageViewer *m = new QgsMessageViewer( this );
|
|
m->setWindowTitle( tr( "Project Contains WMS Layers" ) );
|
|
m->setMessage( tr( "Some WMS servers (e.g. UMN mapserver) have a limit for the WIDTH and HEIGHT parameter. Printing layers from such servers may exceed this limit. If this is the case, the WMS layer will not be printed" ), QgsMessageOutput::MessageText );
|
|
m->setCheckBoxText( tr( "Don't show this message again" ) );
|
|
m->setCheckBoxState( Qt::Unchecked );
|
|
m->setCheckBoxVisible( true );
|
|
m->setCheckBoxQgsSettingsLabel( myQSettingsLabel );
|
|
m->exec();
|
|
}
|
|
}
|
|
|
|
void QgsComposer::showAdvancedEffectsWarning()
|
|
{
|
|
if ( ! mComposition->printAsRaster() )
|
|
{
|
|
QgsMessageViewer *m = new QgsMessageViewer( this, QgsGuiUtils::ModalDialogFlags, false );
|
|
m->setWindowTitle( tr( "Project Contains Composition Effects" ) );
|
|
m->setMessage( tr( "Advanced composition effects such as blend modes or vector layer transparency are enabled in this project, which cannot be printed as vectors. Printing as a raster is recommended." ), QgsMessageOutput::MessageText );
|
|
m->setCheckBoxText( tr( "Print as raster" ) );
|
|
m->setCheckBoxState( Qt::Checked );
|
|
m->setCheckBoxVisible( true );
|
|
m->showMessage( true );
|
|
|
|
if ( m->checkBoxState() == Qt::Checked )
|
|
{
|
|
mComposition->setPrintAsRaster( true );
|
|
//make sure print as raster checkbox is updated
|
|
emit printAsRasterChanged( true );
|
|
}
|
|
else
|
|
{
|
|
mComposition->setPrintAsRaster( false );
|
|
emit printAsRasterChanged( false );
|
|
}
|
|
|
|
delete m;
|
|
}
|
|
}
|
|
|
|
void QgsComposer::cleanupAfterTemplateRead()
|
|
{
|
|
Q_FOREACH ( QGraphicsItem *item, mComposition->items() )
|
|
{
|
|
//update all legends completely
|
|
QgsComposerLegend *legendItem = dynamic_cast<QgsComposerLegend *>( item );
|
|
if ( legendItem )
|
|
{
|
|
legendItem->updateLegend();
|
|
continue;
|
|
}
|
|
|
|
//update composer map extent if it does not intersect the full extent of all layers
|
|
QgsComposerMap *mapItem = dynamic_cast<QgsComposerMap *>( item );
|
|
if ( mapItem )
|
|
{
|
|
//test if composer map extent intersects extent of all layers
|
|
bool intersects = false;
|
|
QgsMapCanvas *canvas = mQgis && mQgis->mapCanvas() ? mQgis->mapCanvas() : nullptr;
|
|
|
|
QgsRectangle composerMapExtent = mapItem->extent();
|
|
if ( canvas )
|
|
{
|
|
QgsRectangle mapCanvasExtent = mQgis->mapCanvas()->fullExtent();
|
|
if ( composerMapExtent.intersects( mapCanvasExtent ) )
|
|
{
|
|
intersects = true;
|
|
}
|
|
}
|
|
|
|
//if not: apply current canvas extent
|
|
if ( canvas && !intersects )
|
|
{
|
|
double currentWidth = mapItem->rect().width();
|
|
double currentHeight = mapItem->rect().height();
|
|
if ( currentWidth - 0 > 0.0 ) //don't divide through zero
|
|
{
|
|
QgsRectangle canvasExtent = canvas->mapSettings().visibleExtent();
|
|
//adapt min y of extent such that the size of the map item stays the same
|
|
double newCanvasExtentHeight = currentHeight / currentWidth * canvasExtent.width();
|
|
canvasExtent.setYMinimum( canvasExtent.yMaximum() - newCanvasExtentHeight );
|
|
mapItem->setNewExtent( canvasExtent );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void QgsComposer::mActionPageSetup_triggered()
|
|
{
|
|
if ( !mComposition )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//set printer page orientation
|
|
setPrinterPageOrientation();
|
|
QPageSetupDialog pageSetupDialog( printer(), this );
|
|
pageSetupDialog.exec();
|
|
}
|
|
|
|
void QgsComposer::populatePrintComposersMenu()
|
|
{
|
|
mQgis->populateComposerMenu( mPrintComposersMenu );
|
|
}
|
|
|
|
void QgsComposer::populateWindowMenu()
|
|
{
|
|
populateWithOtherMenu( mWindowMenu, mQgis->windowMenu() );
|
|
}
|
|
|
|
void QgsComposer::populateHelpMenu()
|
|
{
|
|
populateWithOtherMenu( mHelpMenu, mQgis->helpMenu() );
|
|
}
|
|
|
|
void QgsComposer::populateWithOtherMenu( QMenu *thisMenu, QMenu *otherMenu )
|
|
{
|
|
thisMenu->clear();
|
|
Q_FOREACH ( QAction *act, otherMenu->actions() )
|
|
{
|
|
if ( act->menu() )
|
|
{
|
|
thisMenu->addMenu( mirrorOtherMenu( act->menu() ) );
|
|
}
|
|
else
|
|
{
|
|
thisMenu->addAction( act );
|
|
}
|
|
}
|
|
}
|
|
|
|
QMenu *QgsComposer::mirrorOtherMenu( QMenu *otherMenu )
|
|
{
|
|
QMenu *newMenu = new QMenu( otherMenu->title(), this );
|
|
Q_FOREACH ( QAction *act, otherMenu->actions() )
|
|
{
|
|
if ( act->menu() )
|
|
{
|
|
newMenu->addMenu( mirrorOtherMenu( act->menu() ) );
|
|
}
|
|
else
|
|
{
|
|
newMenu->addAction( act );
|
|
}
|
|
}
|
|
return newMenu;
|
|
}
|
|
|
|
void QgsComposer::createComposerView()
|
|
{
|
|
if ( !mViewLayout )
|
|
{
|
|
return;
|
|
}
|
|
|
|
delete mView;
|
|
mView = new QgsComposerView();
|
|
mView->setMapCanvas( mQgis->mapCanvas() );
|
|
mView->setContentsMargins( 0, 0, 0, 0 );
|
|
mView->setHorizontalRuler( mHorizontalRuler );
|
|
mView->setVerticalRuler( mVerticalRuler );
|
|
mViewLayout->addWidget( mView, 1, 1 );
|
|
|
|
//view does not accept focus via tab
|
|
mView->setFocusPolicy( Qt::ClickFocus );
|
|
}
|
|
|
|
void QgsComposer::writeWorldFile( const QString &worldFileName, double a, double b, double c, double d, double e, double f ) const
|
|
{
|
|
QFile worldFile( worldFileName );
|
|
if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
|
|
{
|
|
return;
|
|
}
|
|
QTextStream fout( &worldFile );
|
|
|
|
// QString::number does not use locale settings (for the decimal point)
|
|
// which is what we want here
|
|
fout << QString::number( a, 'f', 12 ) << "\r\n";
|
|
fout << QString::number( d, 'f', 12 ) << "\r\n";
|
|
fout << QString::number( b, 'f', 12 ) << "\r\n";
|
|
fout << QString::number( e, 'f', 12 ) << "\r\n";
|
|
fout << QString::number( c, 'f', 12 ) << "\r\n";
|
|
fout << QString::number( f, 'f', 12 ) << "\r\n";
|
|
}
|
|
|
|
|
|
void QgsComposer::setAtlasFeature( QgsMapLayer *layer, const QgsFeature &feat )
|
|
{
|
|
//check if composition atlas settings match
|
|
QgsAtlasComposition &atlas = mComposition->atlasComposition();
|
|
if ( ! atlas.enabled() || atlas.coverageLayer() != layer )
|
|
{
|
|
//either atlas isn't enabled, or layer doesn't match
|
|
return;
|
|
}
|
|
|
|
if ( mComposition->atlasMode() != QgsComposition::PreviewAtlas )
|
|
{
|
|
mComposition->setAtlasMode( QgsComposition::PreviewAtlas );
|
|
//update gui controls
|
|
whileBlocking( mActionAtlasPreview )->setChecked( true );
|
|
mActionAtlasFirst->setEnabled( true );
|
|
mActionAtlasLast->setEnabled( true );
|
|
mActionAtlasNext->setEnabled( true );
|
|
mActionAtlasPrev->setEnabled( true );
|
|
mAtlasPageComboBox->setEnabled( true );
|
|
}
|
|
|
|
//bring composer window to foreground
|
|
activate();
|
|
|
|
mapCanvas()->stopRendering();
|
|
|
|
//set current preview feature id
|
|
atlas.prepareForFeature( &feat );
|
|
emit atlasPreviewFeatureChanged();
|
|
}
|
|
|
|
void QgsComposer::pageOrientationChanged( const QString & )
|
|
{
|
|
mSetPageOrientation = false;
|
|
}
|
|
|
|
void QgsComposer::setPrinterPageOrientation()
|
|
{
|
|
if ( !mSetPageOrientation )
|
|
{
|
|
double paperWidth = mComposition->paperWidth();
|
|
double paperHeight = mComposition->paperHeight();
|
|
|
|
//set printer page orientation
|
|
if ( paperWidth > paperHeight )
|
|
{
|
|
printer()->setOrientation( QPrinter::Landscape );
|
|
}
|
|
else
|
|
{
|
|
printer()->setOrientation( QPrinter::Portrait );
|
|
}
|
|
|
|
mSetPageOrientation = true;
|
|
}
|
|
}
|
|
|
|
void QgsComposer::loadAtlasPredefinedScalesFromProject()
|
|
{
|
|
if ( !mComposition )
|
|
{
|
|
return;
|
|
}
|
|
QgsAtlasComposition &atlasMap = mComposition->atlasComposition();
|
|
QVector<qreal> pScales;
|
|
// first look at project's scales
|
|
QStringList scales( QgsProject::instance()->readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) ) );
|
|
bool hasProjectScales( QgsProject::instance()->readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
|
|
if ( !hasProjectScales || scales.isEmpty() )
|
|
{
|
|
// default to global map tool scales
|
|
QgsSettings settings;
|
|
QString scalesStr( settings.value( QStringLiteral( "Map/scales" ), PROJECT_SCALES ).toString() );
|
|
scales = scalesStr.split( ',' );
|
|
}
|
|
|
|
for ( QStringList::const_iterator scaleIt = scales.constBegin(); scaleIt != scales.constEnd(); ++scaleIt )
|
|
{
|
|
QStringList parts( scaleIt->split( ':' ) );
|
|
if ( parts.size() == 2 )
|
|
{
|
|
pScales.push_back( parts[1].toDouble() );
|
|
}
|
|
}
|
|
atlasMap.setPredefinedScales( pScales );
|
|
}
|
|
|
|
QPrinter *QgsComposer::printer()
|
|
{
|
|
//only create the printer on demand - creating a printer object can be very slow
|
|
//due to QTBUG-3033
|
|
if ( !mPrinter )
|
|
mPrinter = new QPrinter();
|
|
|
|
return mPrinter;
|
|
}
|
|
|
|
|
|
//
|
|
// QgsAppComposerInterface
|
|
//
|
|
|
|
QgsAppComposerInterface::QgsAppComposerInterface( QgsComposer *composer )
|
|
: QgsComposerInterface( composer )
|
|
, mComposer( composer )
|
|
{}
|
|
|
|
QgsComposerView *QgsAppComposerInterface::view()
|
|
{
|
|
return mComposer->view();
|
|
}
|
|
|
|
QgsComposition *QgsAppComposerInterface::composition()
|
|
{
|
|
return mComposer->composition();
|
|
}
|
|
|
|
void QgsAppComposerInterface::close()
|
|
{
|
|
mComposer->close();
|
|
}
|