QGIS/src/app/composer/qgscomposer.cpp
2015-09-25 18:55:35 +10:00

3983 lines
128 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 "qgsatlascompositionwidget.h"
#include "qgscomposerarrow.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 "qgscomposerattributetable.h"
#include "qgscomposerattributetablev2.h"
#include "qgscomposertablewidget.h"
#include "qgsexception.h"
#include "qgslogger.h"
#include "qgsproject.h"
#include "qgsmapcanvas.h"
#include "qgsmessageviewer.h"
#include "qgscontexthelp.h"
#include "qgscursors.h"
#include "qgsmaplayeractionregistry.h"
#include "qgsgeometry.h"
#include "qgspaperitem.h"
#include "qgsmaplayerregistry.h"
#include "qgsprevieweffect.h"
#include "ui_qgssvgexportoptions.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 <QSettings>
#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
// sort function for QList<QAction*>, e.g. menu listings
static bool cmpByText_( QAction* a, QAction* b )
{
return QString::localeAwareCompare( a->text(), b->text() ) < 0;
}
QgsComposer::QgsComposer( QgisApp *qgis, const QString& title )
: QMainWindow()
, mTitle( title )
, mQgis( qgis )
, mPrinter( 0 )
, mUndoView( 0 )
, mAtlasFeatureAction( 0 )
{
setupUi( this );
setWindowTitle( mTitle );
setupTheme();
QSettings settings;
setStyleSheet( mQgis->styleSheet() );
int size = settings.value( "/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( "/mActionAddBasicShape.png" ) );
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 );
QActionGroup* toggleActionGroup = new QActionGroup( this );
toggleActionGroup->addAction( mActionMoveItemContent );
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( 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 );
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( "mPrintComposersMenu" );
connect( mPrintComposersMenu, SIGNAL( aboutToShow() ), this, SLOT( 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, SIGNAL( triggered() ), this, SLOT( 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( "/mActionEditCut.png" ) );
connect( mActionCut, SIGNAL( triggered() ), this, SLOT( actionCutTriggered() ) );
mActionCopy = new QAction( tr( "&Copy" ), this );
mActionCopy->setShortcuts( QKeySequence::Copy );
mActionCopy->setStatusTip( tr( "Copy" ) );
mActionCopy->setIcon( QgsApplication::getThemeIcon( "/mActionEditCopy.png" ) );
connect( mActionCopy, SIGNAL( triggered() ), this, SLOT( actionCopyTriggered() ) );
mActionPaste = new QAction( tr( "&Paste" ), this );
mActionPaste->setShortcuts( QKeySequence::Paste );
mActionPaste->setStatusTip( tr( "Paste" ) );
mActionPaste->setIcon( QgsApplication::getThemeIcon( "/mActionEditPaste.png" ) );
connect( mActionPaste, SIGNAL( triggered() ), this, SLOT( 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( "Backspace" ), this );
connect( backSpace, SIGNAL( activated() ), mActionDeleteSelection, SLOT( 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, SIGNAL( triggered() ), this, SLOT( disablePreviewMode() ) );
mActionPreviewModeGrayscale = new QAction( tr( "Simulate Photocopy (&Grayscale)" ), this );
mActionPreviewModeGrayscale->setStatusTip( tr( "Simulate photocopy (grayscale)" ) );
mActionPreviewModeGrayscale->setCheckable( true );
connect( mActionPreviewModeGrayscale, SIGNAL( triggered() ), this, SLOT( activateGrayscalePreview() ) );
mActionPreviewModeMono = new QAction( tr( "Simulate Fax (&Mono)" ), this );
mActionPreviewModeMono->setStatusTip( tr( "Simulate fax (mono)" ) );
mActionPreviewModeMono->setCheckable( true );
connect( mActionPreviewModeMono, SIGNAL( triggered() ), this, SLOT( activateMonoPreview() ) );
mActionPreviewProtanope = new QAction( tr( "Simulate Color Blindness (&Protanope)" ), this );
mActionPreviewProtanope->setStatusTip( tr( "Simulate color blindness (Protanope)" ) );
mActionPreviewProtanope->setCheckable( true );
connect( mActionPreviewProtanope, SIGNAL( triggered() ), this, SLOT( activateProtanopePreview() ) );
mActionPreviewDeuteranope = new QAction( tr( "Simulate Color Blindness (&Deuteranope)" ), this );
mActionPreviewDeuteranope->setStatusTip( tr( "Simulate color blindness (Deuteranope)" ) );
mActionPreviewDeuteranope->setCheckable( true );
connect( mActionPreviewDeuteranope, SIGNAL( triggered() ), this, SLOT( 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( "Ctrl+=" ), this );
connect( ctrlEquals, SIGNAL( activated() ), mActionZoomIn, SLOT( trigger() ) );
#ifndef Q_OS_MAC
//disabled for OSX - see #10761
//also see http://qt-project.org/forums/viewthread/3630 QGraphicsEffects are not well supported on OSX
QMenu *previewMenu = viewMenu->addMenu( "&Preview" );
previewMenu->addAction( mActionPreviewModeOff );
previewMenu->addAction( mActionPreviewModeGrayscale );
previewMenu->addAction( mActionPreviewModeMono );
previewMenu->addAction( mActionPreviewProtanope );
previewMenu->addAction( mActionPreviewDeuteranope );
#endif
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( "mPanelMenu" );
mToolbarMenu = new QMenu( tr( "&Toolbars" ), this );
mToolbarMenu->setObjectName( "mToolbarMenu" );
viewMenu->addSeparator();
viewMenu->addAction( mActionToggleFullScreen );
viewMenu->addAction( mActionHidePanels );
viewMenu->addMenu( mPanelMenu );
viewMenu->addMenu( mToolbarMenu );
// 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( "Add Shape" );
shapeMenu->setIcon( QgsApplication::getThemeIcon( "/mActionAddBasicShape.png" ) );
shapeMenu->addAction( mActionAddRectangle );
shapeMenu->addAction( mActionAddTriangle );
shapeMenu->addAction( mActionAddEllipse );
layoutMenu->addAction( mActionAddArrow );
//layoutMenu->addAction( mActionAddTable );
layoutMenu->addAction( mActionAddAttributeTable );
layoutMenu->addAction( mActionAddHtml );
layoutMenu->addSeparator();
layoutMenu->addAction( mActionSelectMoveItem );
layoutMenu->addAction( mActionMoveItemContent );
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(), SIGNAL( editingFinished() ), this, SLOT( atlasPageComboEditingFinished() ) );
connect( mAtlasPageComboBox, SIGNAL( currentIndexChanged( QString ) ), this, SLOT( 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
mFirstTime = true;
// Create action to select this window
mWindowAction = new QAction( windowTitle(), this );
connect( mWindowAction, SIGNAL( triggered() ), this, SLOT( activate() ) );
QgsDebugMsg( "entered." );
setMouseTracking( true );
mViewFrame->setMouseTracking( true );
mStatusZoomCombo = new QComboBox( mStatusBar );
mStatusZoomCombo->setEditable( true );
mStatusZoomCombo->setInsertPolicy( QComboBox::NoInsert );
mStatusZoomCombo->setCompleter( 0 );
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, SIGNAL( currentIndexChanged( int ) ), this, SLOT( statusZoomCombo_currentIndexChanged( int ) ) );
connect( mStatusZoomCombo->lineEdit(), SIGNAL( returnPressed() ), this, SLOT( 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( "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 = 0;
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
QSettings myQSettings;
bool showRulers = myQSettings.value( "/Composer/showRulers", true ).toBool();
mActionShowRulers->blockSignals( true );
mActionShowRulers->setChecked( showRulers );
mHorizontalRuler->setVisible( showRulers );
mVerticalRuler->setVisible( showRulers );
mRulerLayoutFix->setVisible( showRulers );
mActionShowRulers->blockSignals( false );
connect( mActionShowRulers, SIGNAL( triggered( bool ) ), this, SLOT( toggleRulers( bool ) ) );
//init undo/redo buttons
mComposition = new QgsComposition( mQgis->mapCanvas()->mapSettings() );
mActionUndo->setEnabled( false );
mActionRedo->setEnabled( false );
if ( mComposition->undoStack() )
{
connect( mComposition->undoStack(), SIGNAL( canUndoChanged( bool ) ), mActionUndo, SLOT( setEnabled( bool ) ) );
connect( mComposition->undoStack(), SIGNAL( canRedoChanged( bool ) ), mActionRedo, SLOT( setEnabled( bool ) ) );
}
mActionShowPage->setChecked( mComposition->pagesVisible() );
restoreGridSettings();
connectViewSlots();
connectCompositionSlots();
connectOtherSlots();
mView->setComposition( mComposition );
//this connection is set up after setting the view's composition, as we don't want setComposition called
//for new composers
connect( mView, SIGNAL( compositionSet( QgsComposition* ) ), this, SLOT( setComposition( QgsComposition* ) ) );
int minDockWidth( 335 );
setTabPosition( Qt::AllDockWidgetAreas, QTabWidget::North );
mGeneralDock = new QDockWidget( tr( "Composition" ), this );
mGeneralDock->setObjectName( "CompositionDock" );
mGeneralDock->setMinimumWidth( minDockWidth );
mPanelMenu->addAction( mGeneralDock->toggleViewAction() );
mItemDock = new QDockWidget( tr( "Item properties" ), this );
mItemDock->setObjectName( "ItemDock" );
mItemDock->setMinimumWidth( minDockWidth );
mPanelMenu->addAction( mItemDock->toggleViewAction() );
mUndoDock = new QDockWidget( tr( "Command history" ), this );
mUndoDock->setObjectName( "CommandDock" );
mPanelMenu->addAction( mUndoDock->toggleViewAction() );
mAtlasDock = new QDockWidget( tr( "Atlas generation" ), this );
mAtlasDock->setObjectName( "AtlasDock" );
mPanelMenu->addAction( mAtlasDock->toggleViewAction() );
mItemsDock = new QDockWidget( tr( "Items" ), this );
mItemsDock->setObjectName( "ItemsDock" );
mPanelMenu->addAction( mItemsDock->toggleViewAction() );
QList<QDockWidget *> docks = findChildren<QDockWidget *>();
Q_FOREACH ( QDockWidget* dock, docks )
{
dock->setFeatures( QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
connect( dock, SIGNAL( visibilityChanged( bool ) ), this, SLOT( dockVisibilityChanged( bool ) ) );
}
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()->setResizeMode( 0, QHeaderView::Fixed );
mItemsTreeView->header()->setResizeMode( 1, QHeaderView::Fixed );
mItemsTreeView->header()->setMovable( false );
mItemsTreeView->setDragEnabled( true );
mItemsTreeView->setAcceptDrops( true );
mItemsTreeView->setDropIndicatorShown( true );
mItemsTreeView->setDragDropMode( QAbstractItemView::InternalMove );
mItemsTreeView->setIndentation( 0 );
mItemsDock->setWidget( mItemsTreeView );
connect( mItemsTreeView->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), mComposition->itemsModel(), SLOT( setSelected( QModelIndex ) ) );
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, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
connect( atlasMap, SIGNAL( numberFeaturesChanged( int ) ), this, SLOT( updateAtlasPageComboBox( int ) ) );
connect( atlasMap, SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( atlasFeatureChanged( QgsFeature* ) ) );
//default printer page setup
setPrinterPageDefaults();
// 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();
//connect with signals from QgsProject to write project files
if ( QgsProject::instance() )
{
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument& ) ), this, SLOT( writeXML( QDomDocument& ) ) );
}
#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()
{
deleteItemWidgets();
delete mPrinter;
}
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( "/mActionFileExit.png" ) );
mActionSaveProject->setIcon( QgsApplication::getThemeIcon( "/mActionFileSave.svg" ) );
mActionNewComposer->setIcon( QgsApplication::getThemeIcon( "/mActionNewComposer.svg" ) );
mActionDuplicateComposer->setIcon( QgsApplication::getThemeIcon( "/mActionDuplicateComposer.svg" ) );
mActionComposerManager->setIcon( QgsApplication::getThemeIcon( "/mActionComposerManager.svg" ) );
mActionLoadFromTemplate->setIcon( QgsApplication::getThemeIcon( "/mActionFileOpen.svg" ) );
mActionSaveAsTemplate->setIcon( QgsApplication::getThemeIcon( "/mActionFileSaveAs.svg" ) );
mActionExportAsImage->setIcon( QgsApplication::getThemeIcon( "/mActionSaveMapAsImage.png" ) );
mActionExportAsSVG->setIcon( QgsApplication::getThemeIcon( "/mActionSaveAsSVG.png" ) );
mActionExportAsPDF->setIcon( QgsApplication::getThemeIcon( "/mActionSaveAsPDF.png" ) );
mActionPrint->setIcon( QgsApplication::getThemeIcon( "/mActionFilePrint.png" ) );
mActionZoomAll->setIcon( QgsApplication::getThemeIcon( "/mActionZoomFullExtent.svg" ) );
mActionZoomIn->setIcon( QgsApplication::getThemeIcon( "/mActionZoomIn.svg" ) );
mActionZoomOut->setIcon( QgsApplication::getThemeIcon( "/mActionZoomOut.svg" ) );
mActionZoomActual->setIcon( QgsApplication::getThemeIcon( "/mActionZoomActual.svg" ) );
mActionMouseZoom->setIcon( QgsApplication::getThemeIcon( "/mActionZoomToArea.svg" ) );
mActionRefreshView->setIcon( QgsApplication::getThemeIcon( "/mActionDraw.svg" ) );
mActionUndo->setIcon( QgsApplication::getThemeIcon( "/mActionUndo.png" ) );
mActionRedo->setIcon( QgsApplication::getThemeIcon( "/mActionRedo.png" ) );
mActionAddImage->setIcon( QgsApplication::getThemeIcon( "/mActionAddImage.png" ) );
mActionAddNewMap->setIcon( QgsApplication::getThemeIcon( "/mActionAddMap.png" ) );
mActionAddNewLabel->setIcon( QgsApplication::getThemeIcon( "/mActionLabel.png" ) );
mActionAddNewLegend->setIcon( QgsApplication::getThemeIcon( "/mActionAddLegend.png" ) );
mActionAddNewScalebar->setIcon( QgsApplication::getThemeIcon( "/mActionScaleBar.png" ) );
mActionAddRectangle->setIcon( QgsApplication::getThemeIcon( "/mActionAddBasicShape.png" ) );
mActionAddTriangle->setIcon( QgsApplication::getThemeIcon( "/mActionAddBasicShape.png" ) );
mActionAddEllipse->setIcon( QgsApplication::getThemeIcon( "/mActionAddBasicShape.png" ) );
mActionAddArrow->setIcon( QgsApplication::getThemeIcon( "/mActionAddArrow.png" ) );
mActionAddTable->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) );
mActionAddAttributeTable->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) );
mActionAddHtml->setIcon( QgsApplication::getThemeIcon( "/mActionAddHtml.png" ) );
mActionSelectMoveItem->setIcon( QgsApplication::getThemeIcon( "/mActionSelect.svg" ) );
mActionMoveItemContent->setIcon( QgsApplication::getThemeIcon( "/mActionMoveItemContent.png" ) );
mActionGroupItems->setIcon( QgsApplication::getThemeIcon( "/mActionGroupItems.png" ) );
mActionUngroupItems->setIcon( QgsApplication::getThemeIcon( "/mActionUngroupItems.png" ) );
mActionRaiseItems->setIcon( QgsApplication::getThemeIcon( "/mActionRaiseItems.png" ) );
mActionLowerItems->setIcon( QgsApplication::getThemeIcon( "/mActionLowerItems.png" ) );
mActionMoveItemsToTop->setIcon( QgsApplication::getThemeIcon( "/mActionMoveItemsToTop.png" ) );
mActionMoveItemsToBottom->setIcon( QgsApplication::getThemeIcon( "/mActionMoveItemsToBottom.png" ) );
mActionAlignLeft->setIcon( QgsApplication::getThemeIcon( "/mActionAlignLeft.png" ) );
mActionAlignHCenter->setIcon( QgsApplication::getThemeIcon( "/mActionAlignHCenter.png" ) );
mActionAlignRight->setIcon( QgsApplication::getThemeIcon( "/mActionAlignRight.png" ) );
mActionAlignTop->setIcon( QgsApplication::getThemeIcon( "/mActionAlignTop.png" ) );
mActionAlignVCenter->setIcon( QgsApplication::getThemeIcon( "/mActionAlignVCenter.png" ) );
mActionAlignBottom->setIcon( QgsApplication::getThemeIcon( "/mActionAlignBottom.png" ) );
}
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, SIGNAL( selectedItemChanged( QgsComposerItem* ) ), this, SLOT( showItemOptions( QgsComposerItem* ) ) );
connect( mView, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( deleteItem( QgsComposerItem* ) ) );
connect( mView, SIGNAL( actionFinished() ), this, SLOT( setSelectionTool() ) );
//listen out for position updates from the QgsComposerView
connect( mView, SIGNAL( cursorPosChanged( QPointF ) ), this, SLOT( updateStatusCursorPos( QPointF ) ) );
connect( mView, SIGNAL( zoomLevelChanged() ), this, SLOT( updateStatusZoom() ) );
}
void QgsComposer::connectCompositionSlots()
{
if ( !mComposition )
{
return;
}
connect( mComposition, SIGNAL( selectedItemChanged( QgsComposerItem* ) ), this, SLOT( showItemOptions( QgsComposerItem* ) ) );
connect( mComposition, SIGNAL( composerArrowAdded( QgsComposerArrow* ) ), this, SLOT( addComposerArrow( QgsComposerArrow* ) ) );
connect( mComposition, SIGNAL( composerHtmlFrameAdded( QgsComposerHtml*, QgsComposerFrame* ) ), this, SLOT( addComposerHtmlFrame( QgsComposerHtml*, QgsComposerFrame* ) ) );
connect( mComposition, SIGNAL( composerLabelAdded( QgsComposerLabel* ) ), this, SLOT( addComposerLabel( QgsComposerLabel* ) ) );
connect( mComposition, SIGNAL( composerMapAdded( QgsComposerMap* ) ), this, SLOT( addComposerMap( QgsComposerMap* ) ) );
connect( mComposition, SIGNAL( composerScaleBarAdded( QgsComposerScaleBar* ) ), this, SLOT( addComposerScaleBar( QgsComposerScaleBar* ) ) );
connect( mComposition, SIGNAL( composerLegendAdded( QgsComposerLegend* ) ), this, SLOT( addComposerLegend( QgsComposerLegend* ) ) );
connect( mComposition, SIGNAL( composerPictureAdded( QgsComposerPicture* ) ), this, SLOT( addComposerPicture( QgsComposerPicture* ) ) );
connect( mComposition, SIGNAL( composerShapeAdded( QgsComposerShape* ) ), this, SLOT( addComposerShape( QgsComposerShape* ) ) );
connect( mComposition, SIGNAL( composerTableAdded( QgsComposerAttributeTable* ) ), this, SLOT( addComposerTable( QgsComposerAttributeTable* ) ) );
connect( mComposition, SIGNAL( composerTableFrameAdded( QgsComposerAttributeTableV2*, QgsComposerFrame* ) ), this, SLOT( addComposerTableV2( QgsComposerAttributeTableV2*, QgsComposerFrame* ) ) );
connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( deleteItem( QgsComposerItem* ) ) );
connect( mComposition, SIGNAL( paperSizeChanged() ), mHorizontalRuler, SLOT( update() ) );
connect( mComposition, SIGNAL( paperSizeChanged() ), mVerticalRuler, SLOT( update() ) );
connect( mComposition, SIGNAL( nPagesChanged() ), mHorizontalRuler, SLOT( update() ) );
connect( mComposition, SIGNAL( nPagesChanged() ), mVerticalRuler, SLOT( update() ) );
//listen out to status bar updates from the atlas
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
connect( atlasMap, SIGNAL( statusMsgChanged( QString ) ), this, SLOT( updateStatusAtlasMsg( QString ) ) );
//listen out to status bar updates from the composition
connect( mComposition, SIGNAL( statusMsgChanged( QString ) ), this, SLOT( updateStatusCompositionMsg( QString ) ) );
}
void QgsComposer::connectOtherSlots()
{
//also listen out for position updates from the horizontal/vertical rulers
connect( mHorizontalRuler, SIGNAL( cursorPosChanged( QPointF ) ), this, SLOT( updateStatusCursorPos( QPointF ) ) );
connect( mVerticalRuler, SIGNAL( cursorPosChanged( QPointF ) ), this, SLOT( updateStatusCursorPos( QPointF ) ) );
//listen out for zoom updates
connect( this, SIGNAL( zoomLevelChanged() ), this, SLOT( updateStatusZoom() ) );
}
void QgsComposer::open( void )
{
if ( mFirstTime )
{
//mComposition->createDefault();
mFirstTime = false;
show();
zoomFull(); // zoomFull() does not work properly until we have called show()
if ( mView )
{
mView->updateRulers();
}
}
else
{
show(); //make sure the window is displayed - with a saved project, it's possible to not have already called show()
//is that a bug?
activate(); //bring the composer window to the front
}
}
void QgsComposer::activate()
{
bool shown = isVisible();
show();
raise();
setWindowState( windowState() & ~Qt::WindowMinimized );
activateWindow();
if ( !shown )
{
on_mActionZoomAll_triggered();
}
}
void QgsComposer::changeEvent( QEvent* event )
{
QMainWindow::changeEvent( event );
switch ( event->type() )
{
#ifdef Q_OS_MAC
case QEvent::ActivationChange:
{
if ( QApplication::activeWindow() == this )
{
mWindowAction->setChecked( true );
}
break;
}
#endif
case QEvent::WindowStateChange:
{
/* Listen out for window un-minimisation and restore composer map states.
* We can't use showEvent to detect this due to QT Bug 36675 (see #6085).
*/
QWindowStateChangeEvent* changeEv = static_cast< QWindowStateChangeEvent* >( event );
if ( changeEv->oldState() & Qt::WindowMinimized )
{
// Window restored, restore composers
restoreComposerMapStates();
}
break;
}
default:
break;
}
}
void QgsComposer::setTitle( const QString& title )
{
mTitle = title;
setWindowTitle( mTitle );
if ( mWindowAction )
{
mWindowAction->setText( title );
}
//update atlas map layer action name if required
if ( mAtlasFeatureAction )
{
mAtlasFeatureAction->setText( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ) );
}
}
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;
mStatusZoomCombo->blockSignals( true );
mStatusZoomCombo->lineEdit()->setText( tr( "%1%" ).arg( zoomLevel, 0, 'f', 1 ) );
mStatusZoomCombo->blockSignals( false );
}
void QgsComposer::statusZoomCombo_currentIndexChanged( int index )
{
double selectedZoom = mStatusZoomLevelsList[ mStatusZoomLevelsList.count() - index - 1 ];
if ( mView )
{
mView->setZoomLevel( selectedZoom );
//update zoom combobox text for correct format (one decimal place, trailing % sign)
mStatusZoomCombo->blockSignals( true );
mStatusZoomCombo->lineEdit()->setText( tr( "%1%" ).arg( selectedZoom * 100.0, 0, 'f', 1 ) );
mStatusZoomCombo->blockSignals( false );
}
}
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( QString message )
{
mStatusCompositionLabel->setText( message );
}
void QgsComposer::updateStatusAtlasMsg( QString message )
{
mStatusAtlasLabel->setText( message );
}
void QgsComposer::showItemOptions( QgsComposerItem* item )
{
QWidget* currentWidget = mItemDock->widget();
if ( !item )
{
mItemDock->setWidget( 0 );
return;
}
QMap<QgsComposerItem*, QWidget*>::iterator it = mItemWidgetMap.find( item );
if ( it == mItemWidgetMap.constEnd() )
{
return;
}
QWidget* newWidget = it.value();
if ( !newWidget || newWidget == currentWidget ) //bail out if new widget does not exist or is already there
{
return;
}
mItemDock->setWidget( newWidget );
}
void QgsComposer::on_mActionOptions_triggered()
{
mQgis->showOptionsDialog( this, QString( "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 );
updateAtlasMapLayerAction( 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() ? QString( "%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 renderering
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( "atlas_featurenumber", mComposition->atlasComposition().currentFeatureNumber() + 1, true ) );
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( "atlas_pagename", mComposition->atlasComposition().currentPageName(), true ) );
QgsFeature atlasFeature = mComposition->atlasComposition().feature();
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( "atlas_feature", QVariant::fromValue( atlasFeature ), true ) );
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( "atlas_featureid", atlasFeature.id(), true ) );
mapCanvas()->expressionContextScope().addVariable( QgsExpressionContextScope::StaticVariable( "atlas_geometry", QVariant::fromValue( *atlasFeature.constGeometry() ), true ) );
}
void QgsComposer::on_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( 0, tr( "Enable atlas preview" ),
tr( "Atlas in not currently enabled for this composition!" ),
QMessageBox::Ok,
QMessageBox::Ok );
mActionAtlasPreview->blockSignals( true );
mActionAtlasPreview->setChecked( false );
mActionAtlasPreview->blockSignals( 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, eg, no matching features
QMessageBox::warning( 0, 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::on_mActionAtlasNext_triggered()
{
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
if ( !atlasMap->enabled() )
{
return;
}
mapCanvas()->stopRendering();
loadAtlasPredefinedScalesFromProject();
atlasMap->nextFeature();
emit atlasPreviewFeatureChanged();
}
void QgsComposer::on_mActionAtlasPrev_triggered()
{
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
if ( !atlasMap->enabled() )
{
return;
}
mapCanvas()->stopRendering();
loadAtlasPredefinedScalesFromProject();
atlasMap->prevFeature();
emit atlasPreviewFeatureChanged();
}
void QgsComposer::on_mActionAtlasFirst_triggered()
{
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
if ( !atlasMap->enabled() )
{
return;
}
mapCanvas()->stopRendering();
loadAtlasPredefinedScalesFromProject();
atlasMap->firstFeature();
emit atlasPreviewFeatureChanged();
}
void QgsComposer::on_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 )
{
mAtlasPageComboBox->blockSignals( true );
mAtlasPageComboBox->setCurrentIndex( mComposition->atlasComposition().currentFeatureNumber() );
mAtlasPageComboBox->blockSignals( false );
}
else if ( page != mComposition->atlasComposition().currentFeatureNumber() + 1 )
{
mComposition->atlasComposition().prepareForFeature( page - 1 );
}
}
QgsMapCanvas *QgsComposer::mapCanvas( void )
{
return mQgis->mapCanvas();
}
QgsComposerView *QgsComposer::view( void )
{
return mView;
}
void QgsComposer::zoomFull( void )
{
if ( mView )
{
mView->fitInView( mComposition->sceneRect(), Qt::KeepAspectRatio );
}
}
void QgsComposer::on_mActionZoomAll_triggered()
{
zoomFull();
mView->updateRulers();
mView->update();
emit zoomLevelChanged();
}
void QgsComposer::on_mActionZoomIn_triggered()
{
mView->scale( 2, 2 );
mView->updateRulers();
mView->update();
emit zoomLevelChanged();
}
void QgsComposer::on_mActionZoomOut_triggered()
{
mView->scale( .5, .5 );
mView->updateRulers();
mView->update();
emit zoomLevelChanged();
}
void QgsComposer::on_mActionZoomActual_triggered()
{
mView->setZoomLevel( 1.0 );
}
void QgsComposer::on_mActionMouseZoom_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::Zoom );
}
}
void QgsComposer::on_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::on_mActionShowGrid_triggered( bool checked )
{
//show or hide grid
if ( mComposition )
{
mComposition->setGridVisible( checked );
}
}
void QgsComposer::on_mActionSnapGrid_triggered( bool checked )
{
//enable or disable snap items to grid
if ( mComposition )
{
mComposition->setSnapToGridEnabled( checked );
}
}
void QgsComposer::on_mActionShowGuides_triggered( bool checked )
{
//show or hide guide lines
if ( mComposition )
{
mComposition->setSnapLinesVisible( checked );
}
}
void QgsComposer::on_mActionSnapGuides_triggered( bool checked )
{
//enable or disable snap items to guides
if ( mComposition )
{
mComposition->setAlignmentSnap( checked );
}
}
void QgsComposer::on_mActionSmartGuides_triggered( bool checked )
{
//enable or disable smart snapping guides
if ( mComposition )
{
mComposition->setSmartGuidesEnabled( checked );
}
}
void QgsComposer::on_mActionShowBoxes_triggered( bool checked )
{
//show or hide bounding boxes
if ( mComposition )
{
mComposition->setBoundingBoxesVisible( checked );
}
}
void QgsComposer::on_mActionShowPage_triggered( bool checked )
{
//toggle page display
if ( mComposition )
{
mComposition->setPagesVisible( checked );
}
}
void QgsComposer::on_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 );
QSettings myQSettings;
myQSettings.setValue( "/Composer/showRulers", checked );
}
void QgsComposer::on_mActionAtlasSettings_triggered()
{
if ( !mAtlasDock->isVisible() )
{
mAtlasDock->show();
}
mAtlasDock->raise();
}
void QgsComposer::on_mActionToggleFullScreen_triggered()
{
if ( mActionToggleFullScreen->isChecked() )
{
showFullScreen();
}
else
{
showNormal();
}
}
void QgsComposer::on_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::setComposition( QgsComposition* composition )
{
if ( !composition )
{
return;
}
//delete composition widget
QgsCompositionWidget* oldCompositionWidget = qobject_cast<QgsCompositionWidget *>( mGeneralDock->widget() );
delete oldCompositionWidget;
deleteItemWidgets();
delete mComposition;
mComposition = composition;
connectCompositionSlots();
createCompositionWidget();
restoreGridSettings();
setupUndoView();
mActionShowPage->setChecked( mComposition->pagesVisible() );
//setup atlas composition widget
QgsAtlasCompositionWidget* oldAtlasWidget = qobject_cast<QgsAtlasCompositionWidget *>( mAtlasDock->widget() );
delete oldAtlasWidget;
mAtlasDock->setWidget( new QgsAtlasCompositionWidget( mAtlasDock, mComposition ) );
//set state of atlas controls
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
toggleAtlasControls( atlasMap->enabled() );
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
connect( atlasMap, SIGNAL( numberFeaturesChanged( int ) ), this, SLOT( updateAtlasPageComboBox( int ) ) );
connect( atlasMap, SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( atlasFeatureChanged( QgsFeature* ) ) );
//default printer page setup
setPrinterPageDefaults();
}
void QgsComposer::dockVisibilityChanged( bool visible )
{
if ( visible )
{
mActionHidePanels->blockSignals( true );
mActionHidePanels->setChecked( false );
mActionHidePanels->blockSignals( false );
}
}
void QgsComposer::on_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::on_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 ) )
{
QSettings myQSettings; // where we keep last used filter in persistent state
QString lastUsedFile = myQSettings.value( "/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();
}
outputFileName = QFileDialog::getSaveFileName(
this,
tr( "Choose a file name to save the map as" ),
outputFileName,
tr( "PDF Format" ) + " (*.pdf *.PDF)" );
if ( outputFileName.isEmpty() )
{
return;
}
if ( !outputFileName.endsWith( ".pdf", Qt::CaseInsensitive ) )
{
outputFileName += ".pdf";
}
myQSettings.setValue( "/UI/lastSaveAsPdfFile", outputFileName );
}
// else, we need to choose a directory
else
{
if ( atlasMap->filenamePattern().size() == 0 )
{
int res = QMessageBox::warning( 0, 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( "'output_'||@atlas_featurenumber" );
}
QSettings myQSettings;
QString lastUsedDir = myQSettings.value( "/UI/lastSaveAtlasAsPdfDir", "." ).toString();
outputDir = QFileDialog::getExistingDirectory( this,
tr( "Directory where to save PDF files" ),
lastUsedDir,
QFileDialog::ShowDirsOnly );
if ( outputDir.isEmpty() )
{
return;
}
// test directory (if it exists and is writable)
if ( !QDir( outputDir ).exists() || !QFileInfo( outputDir ).isWritable() )
{
QMessageBox::warning( 0, tr( "Unable to write into the directory" ),
tr( "The given output directory is not writable. Cancelling." ),
QMessageBox::Ok,
QMessageBox::Ok );
return;
}
myQSettings.setValue( "/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( "Error creating %1." ) ).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( "Error creating %1." ) ).arg( outputFileName ),
QMessageBox::Ok,
QMessageBox::Ok );
mView->setPaintingEnabled( true );
QApplication::restoreOverrideCursor();
return;
}
mComposition->doPrint( multiFilePrinter, painter );
painter.end();
}
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 );
if ( !exportOk )
{
QMessageBox::warning( this, tr( "Atlas processing error" ),
QString( tr( "Error creating %1." ) ).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::on_mActionPrint_triggered()
{
//print only current feature
printComposition( QgsComposer::Single );
}
void QgsComposer::on_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 );
}
//orientation and page size are already set to QPrinter in the page setup dialog
QPrintDialog printDialog( printer(), 0 );
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 cancelling
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::on_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::on_mActionExportAsImage_triggered()
{
exportCompositionAsImage( QgsComposer::Single );
}
void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
{
if ( !mComposition || !mView )
{
return;
}
if ( containsWMSLayer() )
{
showWMSPrintingWarning();
}
QSettings settings;
// Image size
int width = ( int )( mComposition->printResolution() * mComposition->paperWidth() / 25.4 );
int height = ( int )( mComposition-> printResolution() * mComposition->paperHeight() / 25.4 );
int dpi = ( int )( 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( 0, 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;
}
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
if ( mode == QgsComposer::Single )
{
QString outputFileName = QString::null;
if ( atlasMap->enabled() && mComposition->atlasMode() == QgsComposition::PreviewAtlas )
{
QString lastUsedDir = settings.value( "/UI/lastSaveAsImageDir", "." ).toString();
outputFileName = QDir( lastUsedDir ).filePath( atlasMap->currentFilename() );
}
QPair<QString, QString> fileNExt = QgisGui::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ), outputFileName );
if ( fileNExt.first.isEmpty() )
{
return;
}
mView->setPaintingEnabled( false );
int worldFilePageNo = -1;
if ( mComposition->generateWorldFile() && mComposition->worldFileMap() )
{
worldFilePageNo = mComposition->worldFileMap()->page() - 1;
}
for ( int i = 0; i < mComposition->numPages(); ++i )
{
if ( !mComposition->shouldExportPage( i + 1 ) )
{
continue;
}
QImage image = mComposition->printPageAsRaster( i );
if ( image.isNull() )
{
QMessageBox::warning( 0, 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 = image.save( outputFilePath, fileNExt.second.toLocal8Bit().constData() );
if ( !saveOk )
{
QMessageBox::warning( this, tr( "Image export error" ),
QString( tr( "Error creating %1." ) ).arg( fileNExt.first ),
QMessageBox::Ok,
QMessageBox::Ok );
mView->setPaintingEnabled( true );
return;
}
if ( i == worldFilePageNo )
{
// should generate world file for this page
double a, b, c, d, e, f;
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
QFileInfo fi( outputFilePath );
// build the world file name
QString worldFileName = fi.absolutePath() + "/" + fi.baseName() + "."
+ fi.suffix()[0] + fi.suffix()[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().size() == 0 )
{
int res = QMessageBox::warning( 0, 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( "'output_'||@atlas_featurenumber" );
}
QSettings myQSettings;
QString lastUsedDir = myQSettings.value( "/UI/lastSaveAtlasAsImagesDir", "." ).toString();
QString lastUsedFormat = myQSettings.value( "/UI/lastSaveAtlasAsImagesFormat", "jpg" ).toString();
QFileDialog dlg( this, tr( "Directory where to save image files" ) );
dlg.setFileMode( QFileDialog::Directory );
dlg.setOption( QFileDialog::ShowDirsOnly, true );
dlg.setDirectory( lastUsedDir );
//
// Build an augmented FileDialog with a combo box to select the output format
QComboBox *box = new QComboBox();
QHBoxLayout* hlayout = new QHBoxLayout();
QWidget* widget = new QWidget();
QList<QByteArray> formats = QImageWriter::supportedImageFormats();
int selectedFormat = 0;
for ( int i = 0; i < formats.size(); ++i )
{
QString format = QString( formats.at( i ) );
if ( format == lastUsedFormat )
{
selectedFormat = i;
}
box->addItem( format );
}
box->setCurrentIndex( selectedFormat );
hlayout->setMargin( 0 );
hlayout->addWidget( new QLabel( tr( "Image format: " ) ) );
hlayout->addWidget( box );
widget->setLayout( hlayout );
dlg.layout()->addWidget( widget );
if ( !dlg.exec() )
{
return;
}
QStringList s = dlg.selectedFiles();
if ( s.size() < 1 || s.at( 0 ).isEmpty() )
{
return;
}
QString dir = s.at( 0 );
QString format = box->currentText();
QString fileExt = "." + format;
if ( dir.isEmpty() )
{
return;
}
// test directory (if it exists and is writable)
if ( !QDir( dir ).exists() || !QFileInfo( dir ).isWritable() )
{
QMessageBox::warning( 0, tr( "Unable to write into the directory" ),
tr( "The given output directory is not writable. Cancelling." ),
QMessageBox::Ok,
QMessageBox::Ok );
return;
}
myQSettings.setValue( "/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 cancelling
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->generateWorldFile() && mComposition->worldFileMap() )
{
worldFilePageNo = mComposition->worldFileMap()->page() - 1;
}
for ( int i = 0; i < mComposition->numPages(); ++i )
{
if ( !mComposition->shouldExportPage( i + 1 ) )
{
continue;
}
QImage image = mComposition->printPageAsRaster( i );
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 = image.save( imageFilename, format.toLocal8Bit().constData() );
if ( !saveOk )
{
QMessageBox::warning( this, tr( "Atlas processing error" ),
QString( tr( "Error creating %1." ) ).arg( imageFilename ),
QMessageBox::Ok,
QMessageBox::Ok );
mView->setPaintingEnabled( true );
QApplication::restoreOverrideCursor();
return;
}
if ( i == worldFilePageNo )
{
// should generate world file for this page
double a, b, c, d, e, f;
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
QFileInfo fi( imageFilename );
// build the world file name
QString worldFileName = fi.absolutePath() + "/" + fi.baseName() + "."
+ fi.suffix()[0] + fi.suffix()[fi.suffix().size()-1] + "w";
writeWorldFile( worldFileName, a, b, c, d, e, f );
}
}
}
atlasMap->endRender();
mView->setPaintingEnabled( true );
QApplication::restoreOverrideCursor();
}
}
void QgsComposer::on_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::on_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::iterator it = mItemVisibility.begin();
for ( ; it != mItemVisibility.end(); ++it ) it.key()->hide();
}
~QgsItemTempHider()
{
QgsItemVisibilityHash::iterator it = mItemVisibility.begin();
for ( ; it != mItemVisibility.end(); ++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 = "/UI/displaySVGWarning";
QSettings 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->setCheckBoxQSettingsLabel( 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( "PAL", "/DrawOutlineLabels", true );
if ( mode == QgsComposer::Single )
{
QString lastUsedFile = settings.value( "/UI/lastSaveAsSvgFile", "qgis.svg" ).toString();
QFileInfo file( lastUsedFile );
if ( atlasMap->enabled() )
{
outputFileName = QDir( file.path() ).filePath( atlasMap->currentFilename() ) + ".svg";
}
else
{
outputFileName = file.path();
}
// open file dialog
outputFileName = QFileDialog::getSaveFileName(
this,
tr( "Choose a file name to save the map as" ),
outputFileName,
tr( "SVG Format" ) + " (*.svg *.SVG)" );
if ( outputFileName.isEmpty() )
return;
// open otions dialog
{
QDialog dialog;
Ui::QgsSvgExportOptionsDialog options;
options.setupUi( &dialog );
options.chkTextAsOutline->setChecked( prevSettingLabelsAsOutlines );
dialog.exec();
groupLayers = options.chkMapLayersAsGroup->isChecked();
//temporarily override label draw outlines setting
QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", options.chkTextAsOutline->isChecked() );
}
if ( !outputFileName.endsWith( ".svg", Qt::CaseInsensitive ) )
{
outputFileName += ".svg";
}
settings.setValue( "/UI/lastSaveAsSvgFile", outputFileName );
}
else
{
// If we have an Atlas
if ( atlasMap->filenamePattern().size() == 0 )
{
int res = QMessageBox::warning( 0, 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( "'output_'||@atlas_featurenumber" );
}
QSettings myQSettings;
QString lastUsedDir = myQSettings.value( "/UI/lastSaveAtlasAsSvgDir", "." ).toString();
// open file dialog
outputDir = QFileDialog::getExistingDirectory( this,
tr( "Directory where to save SVG files" ),
lastUsedDir,
QFileDialog::ShowDirsOnly );
if ( outputDir.isEmpty() )
{
return;
}
// test directory (if it exists and is writable)
if ( !QDir( outputDir ).exists() || !QFileInfo( outputDir ).isWritable() )
{
QMessageBox::warning( 0, tr( "Unable to write into the directory" ),
tr( "The given output directory is not writable. Cancelling." ),
QMessageBox::Ok,
QMessageBox::Ok );
return;
}
// open otions dialog
{
QDialog dialog;
Ui::QgsSvgExportOptionsDialog options;
options.setupUi( &dialog );
options.chkTextAsOutline->setChecked( prevSettingLabelsAsOutlines );
dialog.exec();
groupLayers = options.chkMapLayersAsGroup->isChecked();
//temporarily override label draw outlines setting
QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", options.chkTextAsOutline->isChecked() );
}
myQSettings.setValue( "/UI/lastSaveAtlasAsSvgDir", outputDir );
}
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( "PAL", "/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( "PAL", "/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 );
}
//width in pixel
int width = ( int )( mComposition->paperWidth() * mComposition->printResolution() / 25.4 );
//height in pixel
int height = ( int )( mComposition->paperHeight() * mComposition->printResolution() / 25.4 );
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( "Error creating %1." ) ).arg( currentFileName ),
QMessageBox::Ok,
QMessageBox::Ok );
mView->setPaintingEnabled( true );
QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", prevSettingLabelsAsOutlines );
return;
}
mComposition->renderPage( &p, i );
p.end();
}
}
else
{
//width and height in pixel
const int width = ( int )( mComposition->paperWidth() * mComposition->printResolution() / 25.4 );
const int height = ( 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;
}
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.begin();
for ( unsigned svgLayerId = 1; it != items.end(); ++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.end(); ++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 );
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( 0, 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( "svg" ).at( 0 ), false );
svgDocRoot.toElement().setAttribute( "xmlns:inkscape", "http://www.inkscape.org/namespaces/inkscape" );
svg.appendChild( svgDocRoot );
}
QDomNode mainGroup = svg.importNode( doc.elementsByTagName( "g" ).at( 0 ), true );
mainGroup.toElement().setAttribute( "id", layerName );
mainGroup.toElement().setAttribute( "inkscape:label", layerName );
mainGroup.toElement().setAttribute( "inkscape:groupmode", "layer" );
QDomNode defs = svg.importNode( doc.elementsByTagName( "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 );
if ( !openOk )
{
QMessageBox::warning( this, tr( "SVG export error" ),
QString( tr( "Error creating %1." ) ).arg( currentFileName ),
QMessageBox::Ok,
QMessageBox::Ok );
mView->setPaintingEnabled( true );
QgsProject::instance()->writeEntry( "PAL", "/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( "PAL", "/DrawOutlineLabels", prevSettingLabelsAsOutlines );
}
void QgsComposer::on_mActionSelectMoveItem_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::Select );
}
}
void QgsComposer::on_mActionAddNewMap_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddMap );
}
}
void QgsComposer::on_mActionAddNewLegend_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddLegend );
}
}
void QgsComposer::on_mActionAddNewLabel_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddLabel );
}
}
void QgsComposer::on_mActionAddNewScalebar_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddScalebar );
}
}
void QgsComposer::on_mActionAddImage_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddPicture );
}
}
void QgsComposer::on_mActionAddRectangle_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddRectangle );
}
}
void QgsComposer::on_mActionAddTriangle_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddTriangle );
}
}
void QgsComposer::on_mActionAddEllipse_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddEllipse );
}
}
void QgsComposer::on_mActionAddTable_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddTable );
}
}
void QgsComposer::on_mActionAddAttributeTable_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddAttributeTable );
}
}
void QgsComposer::on_mActionAddHtml_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddHtml );
}
}
void QgsComposer::on_mActionAddArrow_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::AddArrow );
}
}
void QgsComposer::on_mActionSaveProject_triggered()
{
mQgis->actionSaveProject()->trigger();
}
void QgsComposer::on_mActionNewComposer_triggered()
{
QString title;
if ( !mQgis->uniqueComposerTitle( this, title, true ) )
{
return;
}
mQgis->createNewComposer( title );
}
void QgsComposer::on_mActionDuplicateComposer_triggered()
{
QString newTitle;
if ( !mQgis->uniqueComposerTitle( this, newTitle, false, title() + 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 = 0;
if ( !newComposer )
{
QMessageBox::warning( this, tr( "Duplicate Composer" ),
tr( "Composer duplication failed." ) );
}
}
void QgsComposer::on_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::on_mActionSaveAsTemplate_triggered()
{
//show file dialog
QSettings settings;
QString lastSaveDir = settings.value( "UI/lastComposerTemplateDir", "" ).toString();
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( "UI/lastComposerTemplateDir", saveFileInfo.absolutePath() );
QFile templateFile( saveFileName );
if ( !templateFile.open( QIODevice::WriteOnly ) )
{
return;
}
QDomDocument saveDocument;
templateXML( saveDocument );
if ( templateFile.write( saveDocument.toByteArray() ) == -1 )
{
QMessageBox::warning( 0, tr( "Save error" ), tr( "Error, could not save file" ) );
}
}
void QgsComposer::on_mActionLoadFromTemplate_triggered()
{
loadTemplate( false );
}
void QgsComposer::loadTemplate( const bool newComposer )
{
QSettings settings;
QString openFileDir = settings.value( "UI/lastComposerTemplateDir", "" ).toString();
QString openFileString = QFileDialog::getOpenFileName( 0, tr( "Load template" ), openFileDir, "*.qpt" );
if ( openFileString.isEmpty() )
{
return; //canceled by the user
}
QFileInfo openFileInfo( openFileString );
settings.setValue( "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;
}
QgsComposer* c = 0;
QgsComposition* comp = 0;
if ( newComposer )
{
QString newTitle;
if ( !mQgis->uniqueComposerTitle( this, newTitle, true ) )
{
return;
}
c = mQgis->createNewComposer( newTitle );
if ( !c )
{
QMessageBox::warning( this, tr( "Composer error" ), tr( "Error, could not create new composer" ) );
return;
}
comp = c->composition();
}
else
{
c = this;
comp = mComposition;
}
if ( comp )
{
QDomDocument templateDoc;
if ( templateDoc.setContent( &templateFile ) )
{
// provide feedback, since composer will be hidden when loading template (much faster)
QDialog* dlg = new QgsBusyIndicatorDialog( tr( "Loading template into composer..." ) );
dlg->setStyleSheet( mQgis->styleSheet() );
dlg->show();
c->setUpdatesEnabled( false );
comp->loadFromTemplate( templateDoc, 0, false, newComposer );
c->setUpdatesEnabled( true );
dlg->close();
delete dlg;
dlg = 0;
}
}
}
void QgsComposer::on_mActionMoveItemContent_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::MoveItemContent );
}
}
void QgsComposer::on_mActionPan_triggered()
{
if ( mView )
{
mView->setCurrentTool( QgsComposerView::Pan );
}
}
void QgsComposer::on_mActionGroupItems_triggered()
{
if ( mView )
{
mView->groupItems();
}
}
void QgsComposer::on_mActionUngroupItems_triggered()
{
if ( mView )
{
mView->ungroupItems();
}
}
void QgsComposer::on_mActionLockItems_triggered()
{
if ( mComposition )
{
mComposition->lockSelectedItems();
}
}
void QgsComposer::on_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::on_mActionPasteInPlace_triggered()
{
if ( mView )
{
mView->pasteItems( QgsComposerView::PasteModeInPlace );
}
}
void QgsComposer::on_mActionDeleteSelection_triggered()
{
if ( mView )
{
mView->deleteSelectedItems();
}
}
void QgsComposer::on_mActionSelectAll_triggered()
{
if ( mView )
{
mView->selectAll();
}
}
void QgsComposer::on_mActionDeselectAll_triggered()
{
if ( mView )
{
mView->selectNone();
}
}
void QgsComposer::on_mActionInvertSelection_triggered()
{
if ( mView )
{
mView->selectInvert();
}
}
void QgsComposer::on_mActionSelectNextAbove_triggered()
{
if ( mComposition )
{
mComposition->selectNextByZOrder( QgsComposition::ZValueAbove );
}
}
void QgsComposer::on_mActionSelectNextBelow_triggered()
{
if ( mComposition )
{
mComposition->selectNextByZOrder( QgsComposition::ZValueBelow );
}
}
void QgsComposer::on_mActionRaiseItems_triggered()
{
if ( mComposition )
{
mComposition->raiseSelectedItems();
}
}
void QgsComposer::on_mActionLowerItems_triggered()
{
if ( mComposition )
{
mComposition->lowerSelectedItems();
}
}
void QgsComposer::on_mActionMoveItemsToTop_triggered()
{
if ( mComposition )
{
mComposition->moveSelectedItemsToTop();
}
}
void QgsComposer::on_mActionMoveItemsToBottom_triggered()
{
if ( mComposition )
{
mComposition->moveSelectedItemsToBottom();
}
}
void QgsComposer::on_mActionAlignLeft_triggered()
{
if ( mComposition )
{
mComposition->alignSelectedItemsLeft();
}
}
void QgsComposer::on_mActionAlignHCenter_triggered()
{
if ( mComposition )
{
mComposition->alignSelectedItemsHCenter();
}
}
void QgsComposer::on_mActionAlignRight_triggered()
{
if ( mComposition )
{
mComposition->alignSelectedItemsRight();
}
}
void QgsComposer::on_mActionAlignTop_triggered()
{
if ( mComposition )
{
mComposition->alignSelectedItemsTop();
}
}
void QgsComposer::on_mActionAlignVCenter_triggered()
{
if ( mComposition )
{
mComposition->alignSelectedItemsVCenter();
}
}
void QgsComposer::on_mActionAlignBottom_triggered()
{
if ( mComposition )
{
mComposition->alignSelectedItemsBottom();
}
}
void QgsComposer::on_mActionUndo_triggered()
{
if ( mComposition && mComposition->undoStack() )
{
mComposition->undoStack()->undo();
}
}
void QgsComposer::on_mActionRedo_triggered()
{
if ( mComposition && mComposition->undoStack() )
{
mComposition->undoStack()->redo();
}
}
void QgsComposer::closeEvent( QCloseEvent *e )
{
Q_UNUSED( e );
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();
}
#ifdef Q_OS_MAC
void QgsComposer::showEvent( QShowEvent* event )
{
// add to menu if (re)opening window (event not due to unminimize)
if ( !event->spontaneous() )
{
mQgis->addWindow( mWindowAction );
}
}
#endif
void QgsComposer::saveWindowState()
{
QSettings settings;
settings.setValue( "/Composer/geometry", saveGeometry() );
// store the toolbar/dock widget settings using Qt4 settings API
settings.setValue( "/ComposerUI/state", saveState() );
}
#include "ui_defaults.h"
void QgsComposer::restoreWindowState()
{
// restore the toolbar and dock widgets postions using Qt4 settings API
QSettings settings;
if ( !restoreState( settings.value( "/ComposerUI/state", QByteArray::fromRawData(( char * )defaultComposerUIstate, sizeof defaultComposerUIstate ) ).toByteArray() ) )
{
QgsDebugMsg( "restore of composer UI state failed" );
}
// restore window geometry
if ( !restoreGeometry( settings.value( "/Composer/geometry", QByteArray::fromRawData(( char * )defaultComposerUIgeometry, sizeof defaultComposerUIgeometry ) ).toByteArray() ) )
{
QgsDebugMsg( "restore of composer UI geometry failed" );
}
}
void QgsComposer::writeXML( QDomDocument& doc )
{
QDomNodeList nl = doc.elementsByTagName( "qgis" );
if ( nl.count() < 1 )
{
return;
}
QDomElement qgisElem = nl.at( 0 ).toElement();
if ( qgisElem.isNull() )
{
return;
}
writeXML( qgisElem, doc );
}
void QgsComposer::writeXML( QDomNode& parentNode, QDomDocument& doc )
{
QDomElement composerElem = doc.createElement( "Composer" );
composerElem.setAttribute( "title", mTitle );
//change preview mode of minimised / hidden maps before saving XML (show contents only on demand)
QMap< QgsComposerMap*, int >::iterator mapIt = mMapsToRestore.begin();
for ( ; mapIt != mMapsToRestore.end(); ++mapIt )
{
mapIt.key()->setPreviewMode(( QgsComposerMap::PreviewMode )( mapIt.value() ) );
}
mMapsToRestore.clear();
//store if composer is open or closed
if ( isVisible() )
{
composerElem.setAttribute( "visible", 1 );
}
else
{
composerElem.setAttribute( "visible", 0 );
}
parentNode.appendChild( composerElem );
//store composition
if ( mComposition )
{
mComposition->writeXML( composerElem, doc );
}
// store atlas
mComposition->atlasComposition().writeXML( composerElem, doc );
}
void QgsComposer::templateXML( QDomDocument& doc )
{
writeXML( doc, doc );
}
void QgsComposer::readXML( const QDomDocument& doc )
{
QDomNodeList composerNodeList = doc.elementsByTagName( "Composer" );
if ( composerNodeList.size() < 1 )
{
return;
}
readXML( composerNodeList.at( 0 ).toElement(), doc, true );
cleanupAfterTemplateRead();
}
void QgsComposer::createCompositionWidget()
{
if ( !mComposition )
{
return;
}
QgsCompositionWidget* compositionWidget = new QgsCompositionWidget( mGeneralDock, mComposition );
connect( mComposition, SIGNAL( paperSizeChanged() ), compositionWidget, SLOT( displayCompositionWidthHeight() ) );
connect( this, SIGNAL( printAsRasterChanged( bool ) ), compositionWidget, SLOT( setPrintAsRasterCheckBox( bool ) ) );
connect( compositionWidget, SIGNAL( pageOrientationChanged( QString ) ), this, SLOT( setPrinterPageOrientation( QString ) ) );
mGeneralDock->setWidget( compositionWidget );
}
void QgsComposer::readXML( const QDomElement& composerElem, const QDomDocument& doc, bool fromTemplate )
{
// Set title only if reading from project file
if ( !fromTemplate )
{
if ( composerElem.hasAttribute( "title" ) )
{
setTitle( composerElem.attribute( "title", tr( "Composer" ) ) );
}
}
//delete composition widget
QgsCompositionWidget* oldCompositionWidget = qobject_cast<QgsCompositionWidget *>( mGeneralDock->widget() );
delete oldCompositionWidget;
deleteItemWidgets();
delete mComposition;
createComposerView();
//read composition settings
mComposition = new QgsComposition( mQgis->mapCanvas()->mapSettings() );
QDomNodeList compositionNodeList = composerElem.elementsByTagName( "Composition" );
if ( compositionNodeList.size() > 0 )
{
QDomElement compositionElem = compositionNodeList.at( 0 ).toElement();
mComposition->readXML( compositionElem, doc );
}
connectViewSlots();
connectCompositionSlots();
createCompositionWidget();
//read and restore all the items
QDomElement atlasElem;
if ( mComposition )
{
// read atlas parameters - must be done before adding items
atlasElem = composerElem.firstChildElement( "Atlas" );
mComposition->atlasComposition().readXML( atlasElem, doc );
mComposition->addItemsFromXML( composerElem, doc, &mMapsToRestore );
}
//restore grid settings
restoreGridSettings();
mActionShowPage->setChecked( mComposition->pagesVisible() );
// look for world file composer map, if needed
// Note: this must be done after maps have been added by addItemsFromXML
if ( mComposition->generateWorldFile() )
{
QDomElement compositionElem = compositionNodeList.at( 0 ).toElement();
QgsComposerMap* worldFileMap = 0;
QList<const QgsComposerMap*> maps = mComposition->composerMapItems();
for ( QList<const QgsComposerMap*>::const_iterator it = maps.begin(); it != maps.end(); ++it )
{
if (( *it )->id() == compositionElem.attribute( "worldFileMap" ).toInt() )
{
worldFileMap = const_cast<QgsComposerMap*>( *it );
break;
}
}
mComposition->setWorldFileMap( worldFileMap );
}
//make sure z values are consistent
mComposition->refreshZList();
//disconnect from view's compositionSet signal, since that will be emitted automatically
disconnect( mView, SIGNAL( compositionSet( QgsComposition* ) ), this, SLOT( setComposition( QgsComposition* ) ) );
mView->setComposition( mComposition );
connect( mView, SIGNAL( compositionSet( QgsComposition* ) ), this, SLOT( setComposition( QgsComposition* ) ) );
setupUndoView();
//delete old atlas composition widget
QgsAtlasCompositionWidget* oldAtlasWidget = qobject_cast<QgsAtlasCompositionWidget *>( mAtlasDock->widget() );
delete oldAtlasWidget;
mAtlasDock->setWidget( new QgsAtlasCompositionWidget( mAtlasDock, mComposition ) );
//read atlas map parameters (for pre 2.2 templates)
//this part must be done after adding items
mComposition->atlasComposition().readXMLMapSettings( atlasElem, doc );
//set state of atlas controls
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
toggleAtlasControls( atlasMap->enabled() );
connect( atlasMap, SIGNAL( toggled( bool ) ), this, SLOT( toggleAtlasControls( bool ) ) );
connect( atlasMap, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateAtlasMapLayerAction( QgsVectorLayer * ) ) );
connect( atlasMap, SIGNAL( numberFeaturesChanged( int ) ), this, SLOT( updateAtlasPageComboBox( int ) ) );
connect( atlasMap, SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( atlasFeatureChanged( QgsFeature* ) ) );
//default printer page setup
setPrinterPageDefaults();
//setup items tree view
mItemsTreeView->setModel( mComposition->itemsModel() );
mItemsTreeView->setColumnWidth( 0, 30 );
mItemsTreeView->setColumnWidth( 1, 30 );
connect( mItemsTreeView->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), mComposition->itemsModel(), SLOT( setSelected( QModelIndex ) ) );
setSelectionTool();
}
void QgsComposer::setupUndoView()
{
if ( !mUndoView || !mComposition )
{
return;
}
//init undo/redo buttons
mActionUndo->setEnabled( false );
mActionRedo->setEnabled( false );
if ( mComposition->undoStack() )
{
mUndoView->setStack( mComposition->undoStack() );
connect( mComposition->undoStack(), SIGNAL( canUndoChanged( bool ) ), mActionUndo, SLOT( setEnabled( bool ) ) );
connect( mComposition->undoStack(), SIGNAL( canRedoChanged( bool ) ), mActionRedo, SLOT( setEnabled( bool ) ) );
}
}
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::deleteItemWidgets()
{
//delete all the items
QMap<QgsComposerItem*, QWidget*>::iterator it = mItemWidgetMap.begin();
for ( ; it != mItemWidgetMap.end(); ++it )
{
delete it.value();
}
mItemWidgetMap.clear();
}
void QgsComposer::addComposerArrow( QgsComposerArrow* arrow )
{
if ( !arrow )
{
return;
}
QgsComposerArrowWidget* arrowWidget = new QgsComposerArrowWidget( arrow );
mItemWidgetMap.insert( arrow, arrowWidget );
}
void QgsComposer::addComposerMap( QgsComposerMap* map )
{
if ( !map )
{
return;
}
map->setMapCanvas( mapCanvas() ); //set canvas to composer map to have the possibility to draw canvas items
QgsComposerMapWidget* mapWidget = new QgsComposerMapWidget( map );
connect( this, SIGNAL( zoomLevelChanged() ), map, SLOT( renderModeUpdateCachedImage() ) );
mItemWidgetMap.insert( map, mapWidget );
}
void QgsComposer::addComposerLabel( QgsComposerLabel* label )
{
if ( !label )
{
return;
}
QgsComposerLabelWidget* labelWidget = new QgsComposerLabelWidget( label );
mItemWidgetMap.insert( label, labelWidget );
}
void QgsComposer::addComposerScaleBar( QgsComposerScaleBar* scalebar )
{
if ( !scalebar )
{
return;
}
QgsComposerScaleBarWidget* sbWidget = new QgsComposerScaleBarWidget( scalebar );
mItemWidgetMap.insert( scalebar, sbWidget );
}
void QgsComposer::addComposerLegend( QgsComposerLegend* legend )
{
if ( !legend )
{
return;
}
QgsComposerLegendWidget* lWidget = new QgsComposerLegendWidget( legend );
mItemWidgetMap.insert( legend, lWidget );
}
void QgsComposer::addComposerPicture( QgsComposerPicture* picture )
{
if ( !picture )
{
return;
}
QgsComposerPictureWidget* pWidget = new QgsComposerPictureWidget( picture );
mItemWidgetMap.insert( picture, pWidget );
}
void QgsComposer::addComposerShape( QgsComposerShape* shape )
{
if ( !shape )
{
return;
}
QgsComposerShapeWidget* sWidget = new QgsComposerShapeWidget( shape );
mItemWidgetMap.insert( shape, sWidget );
}
void QgsComposer::addComposerTable( QgsComposerAttributeTable* table )
{
if ( !table )
{
return;
}
QgsComposerTableWidget* tWidget = new QgsComposerTableWidget( table );
mItemWidgetMap.insert( table, tWidget );
}
void QgsComposer::addComposerTableV2( QgsComposerAttributeTableV2 *table, QgsComposerFrame* frame )
{
if ( !table )
{
return;
}
QgsComposerAttributeTableWidget* tWidget = new QgsComposerAttributeTableWidget( table, frame );
mItemWidgetMap.insert( frame, tWidget );
}
void QgsComposer::addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFrame* frame )
{
if ( !html )
{
return;
}
QgsComposerHtmlWidget* hWidget = new QgsComposerHtmlWidget( html, frame );
mItemWidgetMap.insert( frame, hWidget );
}
void QgsComposer::deleteItem( QgsComposerItem* item )
{
QMap<QgsComposerItem*, QWidget*>::iterator it = mItemWidgetMap.find( item );
if ( it == mItemWidgetMap.end() )
{
return;
}
//the item itself is not deleted here (usually, this is done in the destructor of QgsAddRemoveItemCommand)
it.value()->deleteLater();
mItemWidgetMap.remove( it.key() );
QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( item );
if ( map )
{
mMapsToRestore.remove( map );
}
}
void QgsComposer::setSelectionTool()
{
mActionSelectMoveItem->setChecked( true );
on_mActionSelectMoveItem_triggered();
}
bool QgsComposer::containsWMSLayer() const
{
QMap<QgsComposerItem*, QWidget*>::const_iterator item_it = mItemWidgetMap.constBegin();
QgsComposerItem* currentItem = 0;
QgsComposerMap* currentMap = 0;
for ( ; item_it != mItemWidgetMap.constEnd(); ++item_it )
{
currentItem = item_it.key();
currentMap = dynamic_cast<QgsComposerMap *>( currentItem );
if ( currentMap )
{
if ( currentMap->containsWMSLayer() )
{
return true;
}
}
}
return false;
}
bool QgsComposer::containsAdvancedEffects() const
{
// Check if composer contains any blend modes or flattened layers for transparency
QMap<QgsComposerItem*, QWidget*>::const_iterator item_it = mItemWidgetMap.constBegin();
QgsComposerItem* currentItem = 0;
QgsComposerMap* currentMap = 0;
for ( ; item_it != mItemWidgetMap.constEnd(); ++item_it )
{
currentItem = item_it.key();
// 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
currentMap = dynamic_cast<QgsComposerMap *>( currentItem );
if ( currentMap && currentMap->containsAdvancedEffects() )
{
return true;
}
}
return false;
}
void QgsComposer::showWMSPrintingWarning()
{
QString myQSettingsLabel = "/UI/displayComposerWMSWarning";
QSettings 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->setCheckBoxQSettingsLabel( myQSettingsLabel );
m->exec();
}
}
void QgsComposer::showAdvancedEffectsWarning()
{
if ( ! mComposition->printAsRaster() )
{
QgsMessageViewer* m = new QgsMessageViewer( this, QgisGui::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()
{
QMap<QgsComposerItem*, QWidget*>::const_iterator itemIt = mItemWidgetMap.constBegin();
for ( ; itemIt != mItemWidgetMap.constEnd(); ++itemIt )
{
//update all legends completely
QgsComposerLegend* legendItem = dynamic_cast<QgsComposerLegend *>( itemIt.key() );
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 *>( itemIt.key() );
if ( mapItem )
{
//test if composer map extent intersects extent of all layers
bool intersects = false;
QgsRectangle composerMapExtent = mapItem->extent();
if ( mQgis )
{
QgsMapCanvas* canvas = mQgis->mapCanvas();
if ( canvas )
{
QgsRectangle mapCanvasExtent = mQgis->mapCanvas()->fullExtent();
if ( composerMapExtent.intersects( mapCanvasExtent ) )
{
intersects = true;
}
}
}
//if not: apply current canvas extent
if ( !intersects )
{
double currentWidth = mapItem->rect().width();
double currentHeight = mapItem->rect().height();
if ( currentWidth - 0 > 0.0 ) //don't divide through zero
{
QgsRectangle canvasExtent = mComposition->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 );
}
}
}
}
restoreComposerMapStates();
}
void QgsComposer::on_mActionPageSetup_triggered()
{
if ( !mComposition )
{
return;
}
QPageSetupDialog pageSetupDialog( printer(), this );
pageSetupDialog.exec();
}
void QgsComposer::restoreComposerMapStates()
{
if ( !mMapsToRestore.isEmpty() )
{
//go through maps and restore original preview modes (show on demand after loading from project file)
QMap< QgsComposerMap*, int >::iterator mapIt = mMapsToRestore.begin();
for ( ; mapIt != mMapsToRestore.end(); ++mapIt )
{
mapIt.key()->setPreviewMode(( QgsComposerMap::PreviewMode )( mapIt.value() ) );
mapIt.key()->cache();
mapIt.key()->update();
}
mMapsToRestore.clear();
}
}
void QgsComposer::populatePrintComposersMenu()
{
mPrintComposersMenu->clear();
QList<QAction*> acts = mQgis->printComposersMenu()->actions();
if ( acts.size() > 1 )
{
// sort actions in case main app's aboutToShow slot has not yet
qSort( acts.begin(), acts.end(), cmpByText_ );
}
mPrintComposersMenu->addActions( acts );
}
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->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 );
//instead, if view is focused and tab is pressed than mActionHidePanels is triggered,
//to toggle display of panels
QShortcut* tab = new QShortcut( Qt::Key_Tab, mView );
tab->setContext( Qt::WidgetWithChildrenShortcut );
connect( tab, SIGNAL( activated() ), mActionHidePanels, SLOT( trigger() ) );
}
void QgsComposer::writeWorldFile( 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 ) )
{
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' ) << "\r\n";
fout << QString::number( d, 'f' ) << "\r\n";
fout << QString::number( b, 'f' ) << "\r\n";
fout << QString::number( e, 'f' ) << "\r\n";
fout << QString::number( c, 'f' ) << "\r\n";
fout << QString::number( f, 'f' ) << "\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
mActionAtlasPreview->blockSignals( true );
mActionAtlasPreview->setChecked( true );
mActionAtlasPreview->blockSignals( false );
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::updateAtlasMapLayerAction( QgsVectorLayer *coverageLayer )
{
if ( mAtlasFeatureAction )
{
delete mAtlasFeatureAction;
mAtlasFeatureAction = 0;
}
if ( coverageLayer )
{
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ),
this, coverageLayer, QgsMapLayerAction::SingleFeature ,
QgsApplication::getThemeIcon( "/mIconAtlas.svg" ) );
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, const QgsFeature& ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, const QgsFeature& ) ) );
}
}
void QgsComposer::setPrinterPageOrientation( QString orientation )
{
if ( orientation == tr( "Landscape" ) )
{
printer()->setOrientation( QPrinter::Landscape );
}
else
{
printer()->setOrientation( QPrinter::Portrait );
}
}
void QgsComposer::setPrinterPageDefaults()
{
double paperWidth = mComposition->paperWidth();
double paperHeight = mComposition->paperHeight();
//set printer page orientation
if ( paperWidth > paperHeight )
{
printer()->setOrientation( QPrinter::Landscape );
}
else
{
printer()->setOrientation( QPrinter::Portrait );
}
}
void QgsComposer::updateAtlasMapLayerAction( bool atlasEnabled )
{
if ( mAtlasFeatureAction )
{
delete mAtlasFeatureAction;
mAtlasFeatureAction = 0;
}
if ( atlasEnabled )
{
QgsAtlasComposition& atlas = mComposition->atlasComposition();
mAtlasFeatureAction = new QgsMapLayerAction( QString( tr( "Set as atlas feature for %1" ) ).arg( mTitle ),
this, atlas.coverageLayer(), QgsMapLayerAction::SingleFeature ,
QgsApplication::getThemeIcon( "/mIconAtlas.svg" ) );
QgsMapLayerActionRegistry::instance()->addMapLayerAction( mAtlasFeatureAction );
connect( mAtlasFeatureAction, SIGNAL( triggeredForFeature( QgsMapLayer*, const QgsFeature& ) ), this, SLOT( setAtlasFeature( QgsMapLayer*, const QgsFeature& ) ) );
}
}
void QgsComposer::loadAtlasPredefinedScalesFromProject()
{
if ( !mComposition )
{
return;
}
QgsAtlasComposition& atlasMap = mComposition->atlasComposition();
QVector<qreal> pScales;
// first look at project's scales
QStringList scales( QgsProject::instance()->readListEntry( "Scales", "/ScalesList" ) );
bool hasProjectScales( QgsProject::instance()->readBoolEntry( "Scales", "/useProjectScales" ) );
if ( !hasProjectScales || scales.isEmpty() )
{
// default to global map tool scales
QSettings settings;
QString scalesStr( settings.value( "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;
}