1
/***************************************************************************
2
qgscomposer.cpp - description
5
copyright : (C) 2005 by Radim Blazek
7
***************************************************************************/
9
/***************************************************************************
11
* This program is free software; you can redistribute it and/or modify *
12
* it under the terms of the GNU General Public License as published by *
13
* the Free Software Foundation; either version 2 of the License, or *
14
* (at your option) any later version. *
16
***************************************************************************/
17
#include "qgscomposer.h"
20
#include "qgsapplication.h"
21
#include "qgscomposerview.h"
22
#include "qgscomposition.h"
23
#include "qgscompositionwidget.h"
24
#include "qgscomposerarrow.h"
25
#include "qgscomposerarrowwidget.h"
26
#include "qgscomposerlabel.h"
27
#include "qgscomposerlabelwidget.h"
28
#include "qgscomposerlegend.h"
29
#include "qgscomposerlegendwidget.h"
30
#include "qgscomposermap.h"
31
#include "qgscomposermapwidget.h"
32
#include "qgscomposerpicture.h"
33
#include "qgscomposerpicturewidget.h"
34
#include "qgscomposerscalebar.h"
35
#include "qgscomposerscalebarwidget.h"
36
#include "qgscomposershape.h"
37
#include "qgscomposershapewidget.h"
38
#include "qgsexception.h"
39
#include "qgsproject.h"
40
#include "qgsmapcanvas.h"
41
#include "qgsmaprenderer.h"
42
#include "qgsmessageviewer.h"
43
#include "qgscontexthelp.h"
44
#include "qgscursors.h"
46
#include <QCloseEvent>
47
#include <QDesktopWidget>
48
#include <QFileDialog>
52
#include <QMessageBox>
56
#include <QPrintDialog>
60
#if QT_VERSION < 0x040300
63
#include <QSvgGenerator>
66
#include <QToolButton>
67
#include <QImageWriter>
70
#include "qgslogger.h"
72
QgsComposer::QgsComposer( QgisApp *qgis, const QString& title ): QMainWindow(), mTitle( title )
75
setWindowTitle( mTitle );
77
QObject::connect( mButtonBox, SIGNAL( rejected() ), this, SLOT( close() ) );
79
QToolButton* orderingToolButton = new QToolButton( this );
80
orderingToolButton->setPopupMode( QToolButton::InstantPopup );
81
orderingToolButton->setAutoRaise( true );
82
orderingToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
84
orderingToolButton->addAction( mActionRaiseItems );
85
orderingToolButton->addAction( mActionLowerItems );
86
orderingToolButton->addAction( mActionMoveItemsToTop );
87
orderingToolButton->addAction( mActionMoveItemsToBottom );
88
orderingToolButton->setDefaultAction( mActionRaiseItems );
89
toolBar->addWidget( orderingToolButton );
91
QToolButton* alignToolButton = new QToolButton( this );
92
alignToolButton->setPopupMode( QToolButton::InstantPopup );
93
alignToolButton->setAutoRaise( true );
94
alignToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
96
alignToolButton->addAction( mActionAlignLeft );
97
alignToolButton->addAction( mActionAlignHCenter );
98
alignToolButton->addAction( mActionAlignRight );
99
alignToolButton->addAction( mActionAlignTop );
100
alignToolButton->addAction( mActionAlignVCenter );
101
alignToolButton->addAction( mActionAlignBottom );
102
alignToolButton->setDefaultAction( mActionAlignLeft );
103
toolBar->addWidget( alignToolButton );
105
QActionGroup* toggleActionGroup = new QActionGroup( this );
106
toggleActionGroup->addAction( mActionMoveItemContent );
107
toggleActionGroup->addAction( mActionAddNewMap );
108
toggleActionGroup->addAction( mActionAddNewLabel );
109
toggleActionGroup->addAction( mActionAddNewLegend );
110
toggleActionGroup->addAction( mActionAddNewScalebar );
111
toggleActionGroup->addAction( mActionAddImage );
112
toggleActionGroup->addAction( mActionSelectMoveItem );
113
toggleActionGroup->addAction( mActionAddBasicShape );
114
toggleActionGroup->addAction( mActionAddArrow );
115
toggleActionGroup->setExclusive( true );
117
mActionAddNewMap->setCheckable( true );
118
mActionAddNewLabel->setCheckable( true );
119
mActionAddNewLegend->setCheckable( true );
120
mActionSelectMoveItem->setCheckable( true );
121
mActionAddNewScalebar->setCheckable( true );
122
mActionAddImage->setCheckable( true );
123
mActionMoveItemContent->setCheckable( true );
124
mActionAddBasicShape->setCheckable( true );
125
mActionAddArrow->setCheckable( true );
128
QMenu *appMenu = menuBar()->addMenu( tr( "QGIS" ) );
129
appMenu->addAction( QgisApp::instance()->actionAbout() );
130
appMenu->addAction( QgisApp::instance()->actionOptions() );
133
QMenu *fileMenu = menuBar()->addMenu( tr( "File" ) );
134
fileMenu->addAction( mActionLoadFromTemplate );
135
fileMenu->addAction( mActionSaveAsTemplate );
136
fileMenu->addSeparator();
137
fileMenu->addAction( mActionExportAsImage );
138
fileMenu->addAction( mActionExportAsPDF );
139
fileMenu->addAction( mActionExportAsSVG );
140
fileMenu->addSeparator();
141
fileMenu->addAction( mActionPrint );
142
fileMenu->addSeparator();
143
fileMenu->addAction( mActionQuit );
144
QObject::connect( mActionQuit, SIGNAL( triggered() ), this, SLOT( close() ) );
146
QMenu *viewMenu = menuBar()->addMenu( tr( "View" ) );
147
viewMenu->addAction( mActionZoomIn );
148
viewMenu->addAction( mActionZoomOut );
149
viewMenu->addAction( mActionZoomAll );
150
viewMenu->addSeparator();
151
viewMenu->addAction( mActionRefreshView );
153
QMenu *layoutMenu = menuBar()->addMenu( tr( "Layout" ) );
154
layoutMenu->addAction( mActionAddNewMap );
155
layoutMenu->addAction( mActionAddNewLabel );
156
layoutMenu->addAction( mActionAddNewScalebar );
157
layoutMenu->addAction( mActionAddNewLegend );
158
layoutMenu->addAction( mActionAddImage );
159
layoutMenu->addAction( mActionSelectMoveItem );
160
layoutMenu->addAction( mActionMoveItemContent );
161
layoutMenu->addAction( mActionAddBasicShape );
162
layoutMenu->addAction( mActionAddArrow );
163
layoutMenu->addSeparator();
164
layoutMenu->addAction( mActionGroupItems );
165
layoutMenu->addAction( mActionUngroupItems );
166
layoutMenu->addAction( mActionRaiseItems );
167
layoutMenu->addAction( mActionLowerItems );
168
layoutMenu->addAction( mActionMoveItemsToTop );
169
layoutMenu->addAction( mActionMoveItemsToBottom );
172
#ifndef Q_WS_MAC64 /* assertion failure in NSMenuItem setSubmenu (Qt 4.5.0-snapshot-20080830) */
173
menuBar()->addMenu( QgisApp::instance()->windowMenu() );
175
menuBar()->addMenu( QgisApp::instance()->helpMenu() );
182
// Create action to select this window
183
mWindowAction = new QAction( windowTitle(), this );
184
connect( mWindowAction, SIGNAL( triggered() ), this, SLOT( activate() ) );
186
QgsDebugMsg( "entered." );
188
setMouseTracking( true );
189
//mSplitter->setMouseTracking(true);
190
mViewFrame->setMouseTracking( true );
192
//create composer view
193
mView = new QgsComposerView( mViewFrame );
196
mComposition = new QgsComposition( mQgis->mapCanvas()->mapRenderer() );
197
mComposition->setParent( mView );
198
mView->setComposition( mComposition );
200
QgsCompositionWidget* compositionWidget = new QgsCompositionWidget( mCompositionOptionsFrame, mComposition );
201
QObject::connect( mComposition, SIGNAL( paperSizeChanged() ), compositionWidget, SLOT( displayCompositionWidthHeight() ) );
202
compositionWidget->show();
204
mCompositionOptionsLayout = new QGridLayout( mCompositionOptionsFrame );
205
mCompositionOptionsLayout->setMargin( 0 );
206
mCompositionOptionsLayout->addWidget( compositionWidget );
208
QGridLayout *l = new QGridLayout( mViewFrame );
210
l->addWidget( mView, 0, 0 );
212
mCompositionNameComboBox->insertItem( 0, tr( "Map 1" ) );
214
// Create size grip (needed by Mac OS X for QMainWindow if QStatusBar is not visible)
215
mSizeGrip = new QSizeGrip( this );
216
mSizeGrip->resize( mSizeGrip->sizeHint() );
217
mSizeGrip->move( rect().bottomRight() - mSizeGrip->rect().bottomRight() );
219
restoreWindowState();
224
//connect with signals from QgsProject to write project files
225
if ( QgsProject::instance() )
227
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument& ) ), this, SLOT( writeXML( QDomDocument& ) ) );
231
QgsComposer::~QgsComposer()
236
void QgsComposer::setupTheme()
238
//now set all the icons - getThemeIcon will fall back to default theme if its
239
//missing from active theme
240
mActionQuit->setIcon( QgisApp::getThemeIcon( "/mActionFileExit.png" ) );
241
mActionLoadFromTemplate->setIcon( QgisApp::getThemeIcon( "/mActionFileOpen.png" ) );
242
mActionSaveAsTemplate->setIcon( QgisApp::getThemeIcon( "/mActionFileSaveAs.png" ) );
243
mActionExportAsImage->setIcon( QgisApp::getThemeIcon( "/mActionSaveMapAsImage.png" ) );
244
mActionExportAsSVG->setIcon( QgisApp::getThemeIcon( "/mActionSaveAsSVG.png" ) );
245
mActionExportAsPDF->setIcon( QgisApp::getThemeIcon( "/mActionSaveAsPDF.png" ) );
246
mActionPrint->setIcon( QgisApp::getThemeIcon( "/mActionFilePrint.png" ) );
247
mActionZoomAll->setIcon( QgisApp::getThemeIcon( "/mActionZoomFullExtent.png" ) );
248
mActionZoomIn->setIcon( QgisApp::getThemeIcon( "/mActionZoomIn.png" ) );
249
mActionZoomOut->setIcon( QgisApp::getThemeIcon( "/mActionZoomOut.png" ) );
250
mActionRefreshView->setIcon( QgisApp::getThemeIcon( "/mActionDraw.png" ) );
251
mActionAddImage->setIcon( QgisApp::getThemeIcon( "/mActionAddImage.png" ) );
252
mActionAddNewMap->setIcon( QgisApp::getThemeIcon( "/mActionAddMap.png" ) );
253
mActionAddNewLabel->setIcon( QgisApp::getThemeIcon( "/mActionLabel.png" ) );
254
mActionAddNewLegend->setIcon( QgisApp::getThemeIcon( "/mActionAddLegend.png" ) );
255
mActionAddNewScalebar->setIcon( QgisApp::getThemeIcon( "/mActionScaleBar.png" ) );
256
mActionAddBasicShape->setIcon( QgisApp::getThemeIcon( "/mActionAddBasicShape.png" ) );
257
mActionAddArrow->setIcon( QgisApp::getThemeIcon( "/mActionAddArrow.png" ) );
258
mActionSelectMoveItem->setIcon( QgisApp::getThemeIcon( "/mActionSelectPan.png" ) );
259
mActionMoveItemContent->setIcon( QgisApp::getThemeIcon( "/mActionMoveItemContent.png" ) );
260
mActionGroupItems->setIcon( QgisApp::getThemeIcon( "/mActionGroupItems.png" ) );
261
mActionUngroupItems->setIcon( QgisApp::getThemeIcon( "/mActionUngroupItems.png" ) );
262
mActionRaiseItems->setIcon( QgisApp::getThemeIcon( "/mActionRaiseItems.png" ) );
263
mActionLowerItems->setIcon( QgisApp::getThemeIcon( "/mActionLowerItems.png" ) );
264
mActionMoveItemsToTop->setIcon( QgisApp::getThemeIcon( "/mActionMoveItemsToTop.png" ) );
265
mActionMoveItemsToBottom->setIcon( QgisApp::getThemeIcon( "/mActionMoveItemsToBottom.png" ) );
266
mActionAlignLeft->setIcon( QgisApp::getThemeIcon( "/mActionAlignLeft.png" ) );
267
mActionAlignHCenter->setIcon( QgisApp::getThemeIcon( "/mActionAlignHCenter.png" ) );
268
mActionAlignRight->setIcon( QgisApp::getThemeIcon( "/mActionAlignRight.png" ) );
269
mActionAlignTop->setIcon( QgisApp::getThemeIcon( "/mActionAlignTop.png" ) );
270
mActionAlignVCenter->setIcon( QgisApp::getThemeIcon( "/mActionAlignVCenter.png" ) );
271
mActionAlignBottom->setIcon( QgisApp::getThemeIcon( "/mActionAlignBottom.png" ) );
274
void QgsComposer::connectSlots()
276
connect( mView, SIGNAL( selectedItemChanged( const QgsComposerItem* ) ), this, SLOT( showItemOptions( const QgsComposerItem* ) ) );
277
connect( mView, SIGNAL( composerLabelAdded( QgsComposerLabel* ) ), this, SLOT( addComposerLabel( QgsComposerLabel* ) ) );
278
connect( mView, SIGNAL( composerMapAdded( QgsComposerMap* ) ), this, SLOT( addComposerMap( QgsComposerMap* ) ) );
279
connect( mView, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( deleteItem( QgsComposerItem* ) ) );
280
connect( mView, SIGNAL( composerScaleBarAdded( QgsComposerScaleBar* ) ), this, SLOT( addComposerScaleBar( QgsComposerScaleBar* ) ) );
281
connect( mView, SIGNAL( composerLegendAdded( QgsComposerLegend* ) ), this, SLOT( addComposerLegend( QgsComposerLegend* ) ) );
282
connect( mView, SIGNAL( composerPictureAdded( QgsComposerPicture* ) ), this, SLOT( addComposerPicture( QgsComposerPicture* ) ) );
283
connect( mView, SIGNAL( composerShapeAdded( QgsComposerShape* ) ), this, SLOT( addComposerShape( QgsComposerShape* ) ) );
284
connect( mView, SIGNAL( composerArrowAdded( QgsComposerArrow* ) ), this, SLOT( addComposerArrow( QgsComposerArrow* ) ) );
285
connect( mView, SIGNAL( actionFinished() ), this, SLOT( setSelectionTool() ) );
288
void QgsComposer::open( void )
292
//mComposition->createDefault();
295
zoomFull(); // zoomFull() does not work properly until we have called show()
300
show(); //make sure the window is displayed - with a saved project, it's possible to not have already called show()
302
activate(); //bring the composer window to the front
306
void QgsComposer::activate()
310
setWindowState( windowState() & ~Qt::WindowMinimized );
315
void QgsComposer::changeEvent( QEvent* event )
317
QMainWindow::changeEvent( event );
318
switch ( event->type() )
320
case QEvent::ActivationChange:
321
if ( QApplication::activeWindow() == this )
323
mWindowAction->setChecked( true );
332
void QgsComposer::showEvent( QShowEvent *event )
334
QMainWindow::showEvent( event );
335
// add to menu if (re)opening window (event not due to unminimize)
336
if ( !event->spontaneous() )
338
QgisApp::instance()->addWindow( mWindowAction );
343
void QgsComposer::setTitle( const QString& title )
346
setWindowTitle( mTitle );
349
mWindowAction->setText( title );
353
void QgsComposer::showCompositionOptions( QWidget *w )
355
QWidget* currentWidget = mItemStackedWidget->currentWidget();
356
mItemStackedWidget->removeWidget( currentWidget );
357
mItemStackedWidget->addWidget( w );
360
void QgsComposer::showItemOptions( const QgsComposerItem* item )
362
QWidget* currentWidget = mItemStackedWidget->currentWidget();
366
mItemStackedWidget->removeWidget( currentWidget );
367
mItemStackedWidget->setCurrentWidget( 0 );
371
QMap<QgsComposerItem*, QWidget*>::iterator it = mItemWidgetMap.find( const_cast<QgsComposerItem*>( item ) );
372
if ( it == mItemWidgetMap.constEnd() )
377
QWidget* newWidget = it.value();
379
if ( !newWidget || newWidget == currentWidget ) //bail out if new widget does not exist or is already there
384
mItemStackedWidget->removeWidget( currentWidget );
385
mItemStackedWidget->addWidget( newWidget );
386
mItemStackedWidget->setCurrentWidget( newWidget );
390
QgsMapCanvas *QgsComposer::mapCanvas( void )
392
return mQgis->mapCanvas();
395
QgsComposerView *QgsComposer::view( void )
400
/*QgsComposition *QgsComposer::composition(void)
405
void QgsComposer::zoomFull( void )
409
mView->fitInView( 0, 0, mComposition->paperWidth() + 1, mComposition->paperHeight() + 1, Qt::KeepAspectRatio );
413
void QgsComposer::on_mActionZoomAll_triggered()
417
emit zoomLevelChanged();
420
void QgsComposer::on_mActionZoomIn_triggered()
422
mView->scale( 2, 2 );
424
emit zoomLevelChanged();
427
void QgsComposer::on_mActionZoomOut_triggered()
429
mView->scale( .5, .5 );
431
emit zoomLevelChanged();
434
void QgsComposer::on_mActionRefreshView_triggered()
441
//refresh preview of all composer maps
442
QMap<QgsComposerItem*, QWidget*>::iterator it = mItemWidgetMap.begin();
443
for ( ; it != mItemWidgetMap.end(); ++it )
445
QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( it.key() );
446
if ( map && !map->isDrawing() )
453
mComposition->update();
456
void QgsComposer::on_mActionExportAsPDF_triggered()
458
QSettings myQSettings; // where we keep last used filter in persistent state
459
QString myLastUsedFile = myQSettings.value( "/UI/lastSaveAsPdfFile", "qgis.pdf" ).toString();
460
QFileInfo file( myLastUsedFile );
461
QFileDialog *myQFileDialog = new QFileDialog( this, tr( "Choose a file name to save the map as" ),
462
file.path(), tr( "PDF Format" ) + " (*.pdf *PDF)" );
463
myQFileDialog->selectFile( file.fileName() );
464
myQFileDialog->setFileMode( QFileDialog::AnyFile );
465
myQFileDialog->setAcceptMode( QFileDialog::AcceptSave );
467
int result = myQFileDialog->exec();
469
if ( result != QDialog::Accepted ) return;
471
QString myOutputFileNameQString = myQFileDialog->selectedFiles().first();
472
if ( myOutputFileNameQString == "" ) return;
474
myQSettings.setValue( "/UI/lastSaveAsPdfFile", myOutputFileNameQString );
478
printer.setOutputFormat( QPrinter::PdfFormat );
479
printer.setOutputFileName( myOutputFileNameQString );
480
printer.setPaperSize( QSizeF( mComposition->paperWidth(), mComposition->paperHeight() ), QPrinter::Millimeter );
485
void QgsComposer::on_mActionPrint_triggered()
490
if ( mComposition->paperWidth() >= mComposition->paperHeight() )
492
printer.setOrientation( QPrinter::Landscape );
496
printer.setOrientation( QPrinter::Portrait );
499
printer.setPaperSize( QSizeF( mComposition->paperWidth(), mComposition->paperHeight() ), QPrinter::Millimeter );
500
QPrintDialog printDialog( &printer, 0 );
501
if ( printDialog.exec() != QDialog::Accepted )
509
void QgsComposer::print( QPrinter &printer )
514
if ( containsWMSLayer() )
516
showWMSPrintingWarning();
519
//set resolution based on composer setting
520
printer.setFullPage( true );
521
printer.setColorMode( QPrinter::Color );
523
//set user-defined resolution
524
printer.setResolution( mComposition->printResolution() );
526
QPainter p( &printer );
528
QgsComposition::PlotStyle savedPlotStyle = mComposition->plotStyle();
529
mComposition->setPlotStyle( QgsComposition::Print );
531
QApplication::setOverrideCursor( Qt::BusyCursor );
533
if ( mComposition->printAsRaster() )
535
//print out via QImage, code copied from on_mActionExportAsImage_activated
536
int width = ( int )( mComposition->printResolution() * mComposition->paperWidth() / 25.4 );
537
int height = ( int )( mComposition-> printResolution() * mComposition->paperHeight() / 25.4 );
538
QImage image( QSize( width, height ), QImage::Format_ARGB32 );
539
image.setDotsPerMeterX( mComposition->printResolution() / 25.4 * 1000 );
540
image.setDotsPerMeterY( mComposition->printResolution() / 25.4 * 1000 );
542
QPainter imagePainter( &image );
543
QRectF sourceArea( 0, 0, mComposition->paperWidth(), mComposition->paperHeight() );
544
QRectF targetArea( 0, 0, width, height );
545
mComposition->render( &imagePainter, targetArea, sourceArea );
547
p.drawImage( targetArea, image, targetArea );
551
#if QT_VERSION < 0x040400
552
QRectF paperRect( 0, 0, mComposition->paperWidth(), mComposition->paperHeight() );
553
QRect pageRect = printer.pageRect();
554
mComposition->render( &p, pageRect, paperRect );
556
//better in case of custom page size, but only possible with Qt>=4.4.0
557
QRectF paperRectMM = printer.pageRect( QPrinter::Millimeter );
558
QRectF paperRectPixel = printer.pageRect( QPrinter::DevicePixel );
559
mComposition->render( &p, paperRectPixel, paperRectMM );
562
mComposition->setPlotStyle( savedPlotStyle );
563
QApplication::restoreOverrideCursor();
566
void QgsComposer::on_mActionExportAsImage_triggered()
568
if ( containsWMSLayer() )
570
showWMSPrintingWarning();
574
int width = ( int )( mComposition->printResolution() * mComposition->paperWidth() / 25.4 );
575
int height = ( int )( mComposition-> printResolution() * mComposition->paperHeight() / 25.4 );
577
int memuse = width * height * 3 / 1000000; // pixmap + image
578
QgsDebugMsg( QString( "Image %1 x %2" ).arg( width ).arg( height ) );
579
QgsDebugMsg( QString( "memuse = %1" ).arg( memuse ) );
581
if ( memuse > 200 ) // cca 4500 x 4500
583
int answer = QMessageBox::warning( 0, tr( "Big image" ),
584
tr( "To create image %1 x %2 requires circa %3 MB of memory" )
585
.arg( width ).arg( height ).arg( memuse ),
586
QMessageBox::Ok, QMessageBox::Abort );
589
if ( answer == QMessageBox::Abort ) return;
592
// Get file and format (stolen from qgisapp.cpp but modified significantely)
594
//create a map to hold the QImageIO names and the filter names
595
//the QImageIO name must be passed to the mapcanvas saveas image function
596
typedef QMap<QString, QString> FilterMap;
597
FilterMap myFilterMap;
599
//find out the last used filter
600
QSettings myQSettings; // where we keep last used filter in persistent state
601
QString myLastUsedFormat = myQSettings.value( "/UI/lastSaveAsImageFormat", "png" ).toString();
602
QString myLastUsedFile = myQSettings.value( "/UI/lastSaveAsImageFile", "qgis.png" ).toString();
603
QFileInfo file( myLastUsedFile );
605
// get a list of supported output image types
606
int myCounterInt = 0;
608
QString myLastUsedFilter;
609
for ( ; myCounterInt < QImageWriter::supportedImageFormats().count(); myCounterInt++ )
611
QString myFormat = QString( QImageWriter::supportedImageFormats().at( myCounterInt ) );
612
QString myFilter = tr( "%1 format (*.%2 *.%3)" )
613
.arg( myFormat ).arg( myFormat.toLower() ).arg( myFormat.toUpper() );
615
if ( myCounterInt > 0 ) myFilters += ";;";
616
myFilters += myFilter;
617
myFilterMap[myFilter] = myFormat;
618
if ( myFormat == myLastUsedFormat )
620
myLastUsedFilter = myFilter;
624
QgsDebugMsg( "Available Filters Map: " );
625
FilterMap::Iterator myIterator;
626
for ( myIterator = myFilterMap.begin(); myIterator != myFilterMap.end(); ++myIterator )
628
QgsDebugMsg( QString( "%1 : %2" ).arg( myIterator.key() ).arg( myIterator.value() ) );
632
//create a file dialog using the the filter list generated above
633
std::auto_ptr < QFileDialog > myQFileDialog(
636
tr( "Choose a file name to save the map image as" ),
642
myQFileDialog->setFileMode( QFileDialog::AnyFile );
644
// set the filter to the last one used
645
myQFileDialog->selectFilter( myLastUsedFilter );
647
// set the 'Open' button to something that makes more sense
648
myQFileDialog->setAcceptMode( QFileDialog::AcceptSave );
650
//prompt the user for a file name
651
QString myOutputFileNameQString;
653
int result = myQFileDialog->exec();
656
if ( result != QDialog::Accepted )
661
myOutputFileNameQString = myQFileDialog->selectedFiles().last();
662
QgsDebugMsg( myOutputFileNameQString );
663
QString myFilterString = myQFileDialog->selectedFilter();
664
QgsDebugMsg( QString( "Selected filter: %1" ).arg( myFilterString ) );
665
QgsDebugMsg( QString( "Image type: %1" ).arg( myFilterMap[myFilterString] ) );
667
// Add the file type suffix to the fileName if required
668
if ( !myOutputFileNameQString.endsWith( myFilterMap[myFilterString] ) )
670
myOutputFileNameQString += "." + myFilterMap[myFilterString];
673
myQSettings.setValue( "/UI/lastSaveAsImageFormat", myFilterMap[myFilterString] );
674
myQSettings.setValue( "/UI/lastSaveAsImageFile", myOutputFileNameQString );
676
if ( myOutputFileNameQString == "" ) return;
678
mComposition->setPlotStyle( QgsComposition::Print );
679
mView->setScene( 0 );
681
QImage image( QSize( width, height ), QImage::Format_ARGB32 );
682
image.setDotsPerMeterX( mComposition->printResolution() / 25.4 * 1000 );
683
image.setDotsPerMeterY( mComposition->printResolution() / 25.4 * 1000 );
685
QPainter p( &image );
686
QRectF sourceArea( 0, 0, mComposition->paperWidth(), mComposition->paperHeight() );
687
QRectF targetArea( 0, 0, width, height );
688
mComposition->render( &p, targetArea, sourceArea );
691
mComposition->setPlotStyle( QgsComposition::Preview );
692
image.save( myOutputFileNameQString, myFilterMap[myFilterString].toLocal8Bit().data() );
693
mView->setScene( mComposition );
697
void QgsComposer::on_mActionExportAsSVG_triggered()
699
if ( containsWMSLayer() )
701
showWMSPrintingWarning();
704
QString myQSettingsLabel = "/UI/displaySVGWarning";
705
QSettings myQSettings;
707
bool displaySVGWarning = myQSettings.value( myQSettingsLabel, true ).toBool();
709
if ( displaySVGWarning )
711
QgsMessageViewer* m = new QgsMessageViewer( this );
712
m->setWindowTitle( tr( "SVG warning" ) );
713
m->setCheckBoxText( tr( "Don't show this message again" ) );
714
m->setCheckBoxState( Qt::Unchecked );
715
m->setCheckBoxVisible( true );
716
m->setCheckBoxQSettingsLabel( myQSettingsLabel );
717
m->setMessageAsHtml( tr( "<p>The SVG export function in Qgis has several "
718
"problems due to bugs and deficiencies in the " )
719
+ tr( "Qt4 svg code. In particular, there are problems "
720
"with layers not being clipped to the map "
721
"bounding box.</p>" )
722
+ tr( "If you require a vector-based output file from "
723
"Qgis it is suggested that you try printing "
724
"to PostScript if the SVG output is not "
729
QString myLastUsedFile = myQSettings.value( "/UI/lastSaveAsSvgFile", "qgis.svg" ).toString();
730
QFileInfo file( myLastUsedFile );
731
QFileDialog *myQFileDialog = new QFileDialog( this, tr( "Choose a file name to save the map as" ),
732
file.path(), tr( "SVG Format" ) + " (*.svg *SVG)" );
733
myQFileDialog->selectFile( file.fileName() );
734
myQFileDialog->setFileMode( QFileDialog::AnyFile );
735
myQFileDialog->setAcceptMode( QFileDialog::AcceptSave );
737
int result = myQFileDialog->exec();
739
if ( result != QDialog::Accepted ) return;
741
QString myOutputFileNameQString = myQFileDialog->selectedFiles().first();
742
if ( myOutputFileNameQString == "" ) return;
744
myQSettings.setValue( "/UI/lastSaveAsSvgFile", myOutputFileNameQString );
746
//mView->setScene(0);//don't redraw the scene on the display while we render
747
mComposition->setPlotStyle( QgsComposition::Print );
749
QSvgGenerator generator;
750
#if QT_VERSION >= 0x040500
751
generator.setTitle( QgsProject::instance()->title() );
753
generator.setFileName( myOutputFileNameQString );
755
int width = ( int )( mComposition->paperWidth() * mComposition->printResolution() / 25.4 );
757
int height = ( int )( mComposition->paperHeight() * mComposition->printResolution() / 25.4 );
758
generator.setSize( QSize( width, height ) );
759
generator.setResolution( mComposition->printResolution() ); //because the rendering is done in mm, convert the dpi
761
QPainter p( &generator );
763
QRectF sourceArea( 0, 0, mComposition->paperWidth(), mComposition->paperHeight() );
764
QRectF targetArea( 0, 0, width, height );
765
mComposition->render( &p, targetArea, sourceArea );
769
mComposition->setPlotStyle( QgsComposition::Preview );
770
//mView->setScene(mComposition->canvas()); //now that we're done, set the view to show the scene again
773
void QgsComposer::on_mActionSelectMoveItem_triggered()
777
mView->setCurrentTool( QgsComposerView::Select );
781
void QgsComposer::on_mActionAddNewMap_triggered()
785
mView->setCurrentTool( QgsComposerView::AddMap );
789
void QgsComposer::on_mActionAddNewLegend_triggered()
793
mView->setCurrentTool( QgsComposerView::AddLegend );
797
void QgsComposer::on_mActionAddNewLabel_triggered()
801
mView->setCurrentTool( QgsComposerView::AddLabel );
805
void QgsComposer::on_mActionAddNewScalebar_triggered()
809
mView->setCurrentTool( QgsComposerView::AddScalebar );
813
void QgsComposer::on_mActionAddImage_triggered()
817
mView->setCurrentTool( QgsComposerView::AddPicture );
821
void QgsComposer::on_mActionAddBasicShape_triggered()
825
mView->setCurrentTool( QgsComposerView::AddShape );
829
void QgsComposer::on_mActionAddArrow_triggered()
833
mView->setCurrentTool( QgsComposerView::AddArrow );
837
void QgsComposer::on_mActionSaveAsTemplate_triggered()
841
QString lastSaveDir = settings.value( "UI/lastComposerTemplateDir", "" ).toString();
842
QString saveFileName = QFileDialog::getSaveFileName( 0, tr( "save template" ), lastSaveDir, "*.qpt" );
843
if ( saveFileName.isEmpty() )
848
QFileInfo saveFileInfo( saveFileName );
849
//check if suffix has been added
850
if ( saveFileInfo.suffix().isEmpty() )
852
QString saveFileNameWithSuffix = saveFileName.append( ".qpt" );
853
saveFileInfo = QFileInfo( saveFileNameWithSuffix );
855
settings.setValue( "UI/LastComposerTemplateDir", saveFileInfo.absolutePath() );
857
QFile templateFile( saveFileName );
858
if ( !templateFile.open( QIODevice::WriteOnly ) )
863
QDomDocument saveDocument;
864
writeXML( saveDocument, saveDocument );
866
if ( templateFile.write( saveDocument.toByteArray() ) == -1 )
868
QMessageBox::warning( 0, tr( "Save error" ), tr( "Error, could not save file" ) );
872
void QgsComposer::on_mActionLoadFromTemplate_triggered()
875
QString openFileDir = settings.value( "UI/lastComposerTemplateDir", "" ).toString();
876
QString openFileString = QFileDialog::getOpenFileName( 0, tr( "Load template" ), openFileDir, "*.qpt" );
878
if ( openFileString.isEmpty() )
880
return; //canceled by the user
883
QFileInfo openFileInfo( openFileString );
884
settings.setValue( "UI/LastComposerTemplateDir", openFileInfo.absolutePath() );
886
QFile templateFile( openFileString );
887
if ( !templateFile.open( QIODevice::ReadOnly ) )
889
QMessageBox::warning( 0, tr( "Read error" ), tr( "Error, could not read file" ) );
893
QDomDocument templateDocument;
894
if ( !templateDocument.setContent( &templateFile, false ) )
896
QMessageBox::warning( 0, tr( "Read error" ), tr( "Content of template file is not valid" ) );
901
readXML( templateDocument );
903
//clean up after template read (e.g. legend and map extent)
904
cleanupAfterTemplateRead();
907
void QgsComposer::on_mActionMoveItemContent_triggered()
911
mView->setCurrentTool( QgsComposerView::MoveItemContent );
915
void QgsComposer::on_mActionGroupItems_triggered()
923
void QgsComposer::on_mActionUngroupItems_triggered()
927
mView->ungroupItems();
931
void QgsComposer::on_mActionRaiseItems_triggered()
935
mComposition->raiseSelectedItems();
939
void QgsComposer::on_mActionLowerItems_triggered()
943
mComposition->lowerSelectedItems();
947
void QgsComposer::on_mActionMoveItemsToTop_triggered()
951
mComposition->moveSelectedItemsToTop();
955
void QgsComposer::on_mActionMoveItemsToBottom_triggered()
959
mComposition->moveSelectedItemsToBottom();
963
void QgsComposer::on_mActionAlignLeft_triggered()
967
mComposition->alignSelectedItemsLeft();
971
void QgsComposer::on_mActionAlignHCenter_triggered()
975
mComposition->alignSelectedItemsHCenter();
979
void QgsComposer::on_mActionAlignRight_triggered()
983
mComposition->alignSelectedItemsRight();
987
void QgsComposer::on_mActionAlignTop_triggered()
991
mComposition->alignSelectedItemsTop();
995
void QgsComposer::on_mActionAlignVCenter_triggered()
999
mComposition->alignSelectedItemsVCenter();
1003
void QgsComposer::on_mActionAlignBottom_triggered()
1007
mComposition->alignSelectedItemsBottom();
1011
void QgsComposer::moveEvent( QMoveEvent *e ) { saveWindowState(); }
1013
void QgsComposer::resizeEvent( QResizeEvent *e )
1015
// Move size grip when window is resized
1016
mSizeGrip->move( rect().bottomRight() - mSizeGrip->rect().bottomRight() );
1021
void QgsComposer::saveWindowState()
1024
settings.setValue( "/Composer/geometry", saveGeometry() );
1025
//settings.setValue("/Composer/splitterState", mSplitter->saveState());
1028
void QgsComposer::restoreWindowState()
1031
restoreGeometry( settings.value( "/Composer/geometry" ).toByteArray() );
1032
QVariant splitterState = settings.value( "/Composer/splitterState" );
1033
if ( splitterState != QVariant::QVariant() )
1035
//mSplitter->restoreState(settings.value("/Composer/splitterState").toByteArray());
1039
QList<int> defaultSize;
1040
defaultSize << 300 << 100; // page display 300 pixels, details pane 100 pixels
1041
//mSplitter->setSizes(defaultSize);
1045
void QgsComposer::writeXML( QDomDocument& doc )
1048
QDomNodeList nl = doc.elementsByTagName( "qgis" );
1049
if ( nl.count() < 1 )
1053
QDomElement qgisElem = nl.at( 0 ).toElement();
1054
if ( qgisElem.isNull() )
1059
writeXML( qgisElem, doc );
1062
void QgsComposer::writeXML( QDomNode& parentNode, QDomDocument& doc )
1064
QDomElement composerElem = doc.createElement( "Composer" );
1065
composerElem.setAttribute( "title", mTitle );
1067
//store if composer is open or closed
1070
composerElem.setAttribute( "visible", 1 );
1074
composerElem.setAttribute( "visible", 0 );
1076
parentNode.appendChild( composerElem );
1078
//store composer items:
1079
QMap<QgsComposerItem*, QWidget*>::const_iterator itemIt = mItemWidgetMap.constBegin();
1080
for ( ; itemIt != mItemWidgetMap.constEnd(); ++itemIt )
1082
itemIt.key()->writeXML( composerElem, doc );
1085
//store composer view
1090
mComposition->writeXML( composerElem, doc );
1094
void QgsComposer::readXML( const QDomDocument& doc )
1096
QDomNodeList composerNodeList = doc.elementsByTagName( "Composer" );
1097
if ( composerNodeList.size() < 1 )
1101
readXML( composerNodeList.at( 0 ).toElement(), doc, true );
1104
void QgsComposer::readXML( const QDomElement& composerElem, const QDomDocument& doc, bool fromTemplate )
1106
// Set title only if reading from project file
1107
if ( !fromTemplate )
1109
if ( composerElem.hasAttribute( "title" ) )
1111
setTitle( composerElem.attribute( "title", tr( "Composer" ) ) );
1115
//delete composer view and composition
1118
//delete every child of mViewFrame
1119
QObjectList viewFrameChildren = mViewFrame->children();
1120
QObjectList::iterator it = viewFrameChildren.begin();
1121
for ( ; it != viewFrameChildren.end(); ++it )
1125
//delete composition widget
1126
QgsCompositionWidget* oldCompositionWidget = qobject_cast<QgsCompositionWidget *>( mCompositionOptionsFrame->children().at( 0 ) );
1127
delete oldCompositionWidget;
1128
delete mCompositionOptionsLayout;
1129
mCompositionOptionsLayout = 0;
1131
mView = new QgsComposerView( mViewFrame );
1134
//read composition settings
1135
mComposition = new QgsComposition( mQgis->mapCanvas()->mapRenderer() );
1136
QDomNodeList compositionNodeList = composerElem.elementsByTagName( "Composition" );
1137
if ( compositionNodeList.size() > 0 )
1139
QDomElement compositionElem = compositionNodeList.at( 0 ).toElement();
1140
mComposition->readXML( compositionElem, doc );
1143
QGridLayout *l = new QGridLayout( mViewFrame );
1145
l->addWidget( mView, 0, 0 );
1147
//create compositionwidget
1148
QgsCompositionWidget* compositionWidget = new QgsCompositionWidget( mCompositionOptionsFrame, mComposition );
1149
QObject::connect( mComposition, SIGNAL( paperSizeChanged() ), compositionWidget, SLOT( displayCompositionWidthHeight() ) );
1150
compositionWidget->show();
1152
mCompositionOptionsLayout = new QGridLayout( mCompositionOptionsFrame );
1153
mCompositionOptionsLayout->setMargin( 0 );
1154
mCompositionOptionsLayout->addWidget( compositionWidget );
1156
//read and restore all the items
1159
QDomNodeList composerLabelList = composerElem.elementsByTagName( "ComposerLabel" );
1160
for ( int i = 0; i < composerLabelList.size(); ++i )
1162
QDomElement currentComposerLabelElem = composerLabelList.at( i ).toElement();
1163
QgsComposerLabel* newLabel = new QgsComposerLabel( mComposition );
1164
newLabel->readXML( currentComposerLabelElem, doc );
1165
addComposerLabel( newLabel );
1166
mComposition->addItem( newLabel );
1167
mComposition->update();
1168
mComposition->clearSelection();
1169
newLabel->setSelected( true );
1170
showItemOptions( newLabel );
1174
QDomNodeList composerMapList = composerElem.elementsByTagName( "ComposerMap" );
1175
for ( int i = 0; i < composerMapList.size(); ++i )
1177
QDomElement currentComposerMapElem = composerMapList.at( i ).toElement();
1178
QgsComposerMap* newMap = new QgsComposerMap( mComposition );
1179
newMap->readXML( currentComposerMapElem, doc );
1180
addComposerMap( newMap );
1181
mComposition->addItem( newMap );
1182
mComposition->update();
1183
mComposition->clearSelection();
1184
newMap->setSelected( true );
1185
showItemOptions( newMap );
1188
//composer scalebars
1189
QDomNodeList composerScaleBarList = composerElem.elementsByTagName( "ComposerScaleBar" );
1190
for ( int i = 0; i < composerScaleBarList.size(); ++i )
1192
QDomElement currentScaleBarElem = composerScaleBarList.at( i ).toElement();
1193
QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( mComposition );
1194
newScaleBar->readXML( currentScaleBarElem, doc );
1195
addComposerScaleBar( newScaleBar );
1196
mComposition->addItem( newScaleBar );
1197
mComposition->update();
1198
mComposition->clearSelection();
1199
newScaleBar->setSelected( true );
1200
showItemOptions( newScaleBar );
1204
QDomNodeList composerLegendList = composerElem.elementsByTagName( "ComposerLegend" );
1205
for ( int i = 0; i < composerLegendList.size(); ++i )
1207
QDomElement currentLegendElem = composerLegendList.at( i ).toElement();
1208
QgsComposerLegend* newLegend = new QgsComposerLegend( mComposition );
1209
newLegend->readXML( currentLegendElem, doc );
1210
addComposerLegend( newLegend );
1211
mComposition->addItem( newLegend );
1212
mComposition->update();
1213
mComposition->clearSelection();
1214
newLegend->setSelected( true );
1215
showItemOptions( newLegend );
1219
QDomNodeList composerPictureList = composerElem.elementsByTagName( "ComposerPicture" );
1220
for ( int i = 0; i < composerPictureList.size(); ++i )
1222
QDomElement currentPictureElem = composerPictureList.at( i ).toElement();
1223
QgsComposerPicture* newPicture = new QgsComposerPicture( mComposition );
1224
newPicture->readXML( currentPictureElem, doc );
1225
addComposerPicture( newPicture );
1226
mComposition->addItem( newPicture );
1227
mComposition->update();
1228
mComposition->clearSelection();
1229
newPicture->setSelected( true );
1230
showItemOptions( newPicture );
1234
QDomNodeList composerShapeList = composerElem.elementsByTagName( "ComposerShape" );
1235
for ( int i = 0; i < composerShapeList.size(); ++i )
1237
QDomElement currentShapeElem = composerShapeList.at( i ).toElement();
1238
QgsComposerShape* newShape = new QgsComposerShape( mComposition );
1239
newShape->readXML( currentShapeElem, doc );
1240
addComposerShape( newShape );
1241
mComposition->addItem( newShape );
1242
mComposition->update();
1243
mComposition->clearSelection();
1244
newShape->setSelected( true );
1245
showItemOptions( newShape );
1249
QDomNodeList composerArrowList = composerElem.elementsByTagName( "ComposerArrow" );
1250
for ( int i = 0; i < composerArrowList.size(); ++i )
1252
QDomElement currentArrowElem = composerArrowList.at( i ).toElement();
1253
QgsComposerArrow* newArrow = new QgsComposerArrow( mComposition );
1254
newArrow->readXML( currentArrowElem, doc );
1255
addComposerArrow( newArrow );
1256
mComposition->addItem( newArrow );
1257
mComposition->update();
1258
mComposition->clearSelection();
1259
newArrow->setSelected( true );
1260
showItemOptions( newArrow );
1263
mComposition->sortZList();
1264
mView->setComposition( mComposition );
1269
void QgsComposer::deleteItems()
1271
//delete all the items
1272
QMap<QgsComposerItem*, QWidget*>::iterator it = mItemWidgetMap.begin();
1273
for ( ; it != mItemWidgetMap.end(); ++it )
1278
mItemWidgetMap.clear();
1281
void QgsComposer::addComposerArrow( QgsComposerArrow* arrow )
1288
QgsComposerArrowWidget* arrowWidget = new QgsComposerArrowWidget( arrow );
1289
mItemWidgetMap.insert( arrow, arrowWidget );
1292
void QgsComposer::addComposerMap( QgsComposerMap* map )
1299
QgsComposerMapWidget* mapWidget = new QgsComposerMapWidget( map );
1300
connect( this, SIGNAL( zoomLevelChanged() ), map, SLOT( renderModeUpdateCachedImage() ) );
1301
mItemWidgetMap.insert( map, mapWidget );
1304
void QgsComposer::addComposerLabel( QgsComposerLabel* label )
1311
QgsComposerLabelWidget* labelWidget = new QgsComposerLabelWidget( label );
1312
mItemWidgetMap.insert( label, labelWidget );
1315
void QgsComposer::addComposerScaleBar( QgsComposerScaleBar* scalebar )
1322
QgsComposerScaleBarWidget* sbWidget = new QgsComposerScaleBarWidget( scalebar );
1323
mItemWidgetMap.insert( scalebar, sbWidget );
1326
void QgsComposer::addComposerLegend( QgsComposerLegend* legend )
1333
QgsComposerLegendWidget* lWidget = new QgsComposerLegendWidget( legend );
1334
mItemWidgetMap.insert( legend, lWidget );
1337
void QgsComposer::addComposerPicture( QgsComposerPicture* picture )
1344
QgsComposerPictureWidget* pWidget = new QgsComposerPictureWidget( picture );
1345
mItemWidgetMap.insert( picture, pWidget );
1348
void QgsComposer::addComposerShape( QgsComposerShape* shape )
1354
QgsComposerShapeWidget* sWidget = new QgsComposerShapeWidget( shape );
1355
mItemWidgetMap.insert( shape, sWidget );
1358
void QgsComposer::deleteItem( QgsComposerItem* item )
1360
QMap<QgsComposerItem*, QWidget*>::iterator it = mItemWidgetMap.find( item );
1362
if ( it == mItemWidgetMap.end() )
1368
delete( it.value() );
1369
mItemWidgetMap.remove( it.key() );
1372
void QgsComposer::setSelectionTool()
1374
mActionSelectMoveItem->setChecked( true );
1375
on_mActionSelectMoveItem_triggered();
1378
bool QgsComposer::containsWMSLayer() const
1380
QMap<QgsComposerItem*, QWidget*>::const_iterator item_it = mItemWidgetMap.constBegin();
1381
QgsComposerItem* currentItem = 0;
1382
QgsComposerMap* currentMap = 0;
1384
for ( ; item_it != mItemWidgetMap.constEnd(); ++item_it )
1386
currentItem = item_it.key();
1387
currentMap = dynamic_cast<QgsComposerMap *>( currentItem );
1390
if ( currentMap->containsWMSLayer() )
1399
void QgsComposer::showWMSPrintingWarning()
1401
QString myQSettingsLabel = "/UI/displayComposerWMSWarning";
1402
QSettings myQSettings;
1404
bool displayWMSWarning = myQSettings.value( myQSettingsLabel, true ).toBool();
1405
if ( displayWMSWarning )
1407
QgsMessageViewer* m = new QgsMessageViewer( this );
1408
m->setWindowTitle( tr( "Project contains WMS layers" ) );
1409
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 );
1410
m->setCheckBoxText( tr( "Don't show this message again" ) );
1411
m->setCheckBoxState( Qt::Unchecked );
1412
m->setCheckBoxVisible( true );
1413
m->setCheckBoxQSettingsLabel( myQSettingsLabel );
1418
void QgsComposer::cleanupAfterTemplateRead()
1420
QMap<QgsComposerItem*, QWidget*>::const_iterator itemIt = mItemWidgetMap.constBegin();
1421
for ( ; itemIt != mItemWidgetMap.constEnd(); ++itemIt )
1423
//update all legends completely
1424
QgsComposerLegend* legendItem = dynamic_cast<QgsComposerLegend *>( itemIt.key() );
1427
legendItem->updateLegend();
1431
//update composer map extent if it does not intersect the full extent of all layers
1432
QgsComposerMap* mapItem = dynamic_cast<QgsComposerMap *>( itemIt.key() );
1435
//test if composer map extent intersects extent of all layers
1436
bool intersects = false;
1437
QgsRectangle composerMapExtent = mapItem->extent();
1440
QgsMapCanvas* canvas = mQgis->mapCanvas();
1443
QgsRectangle mapCanvasExtent = mQgis->mapCanvas()->fullExtent();
1444
if ( composerMapExtent.intersects( mapCanvasExtent ) )
1451
//if not: apply current canvas extent
1454
double currentWidth = mapItem->rect().width();
1455
double currentHeight = mapItem->rect().height();
1456
if ( currentWidth - 0 > 0.0 ) //don't divide through zero
1458
QgsRectangle canvasExtent = mapItem->mapRenderer()->extent();
1459
//adapt min y of extent such that the size of the map item stays the same
1460
double newCanvasExtentHeight = currentHeight / currentWidth * canvasExtent.width();
1461
canvasExtent.setYMinimum( canvasExtent.yMaximum() - newCanvasExtentHeight );
1462
mapItem->setNewExtent( canvasExtent );