Marble

DownloadRegionDialog.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Jens-Michael Hoffmann <[email protected]>
4 //
5 
6 #include "DownloadRegionDialog.h"
7 
8 #include <cmath>
9 
10 #include <QDialogButtonBox>
11 #include <QGroupBox>
12 #include <QHBoxLayout>
13 #include <QHideEvent>
14 #include <QLabel>
15 #include <QPushButton>
16 #include <QRadioButton>
17 #include <QShowEvent>
18 #include <QVBoxLayout>
19 #include <QSpinBox>
20 #include <QScrollArea>
21 #include <QSet>
22 
23 #include "GeoDataLatLonAltBox.h"
24 #include "MarbleDebug.h"
25 #include "MarbleModel.h"
26 #include "MarbleWidget.h"
27 #include "LatLonBoxWidget.h"
28 #include "TileLayer.h"
29 #include "TextureLayer.h"
30 #include "VectorTileLayer.h"
31 #include "TileId.h"
32 #include "TileCoordsPyramid.h"
33 #include "TileLevelRangeWidget.h"
34 #include "TileLoaderHelper.h"
35 #include "routing/RoutingManager.h"
36 #include "routing/RoutingModel.h"
37 #include "GeoDataCoordinates.h"
38 #include "GeoDataLineString.h"
39 #include "DownloadRegion.h"
40 #include "GeoSceneDocument.h"
41 #include "GeoSceneMap.h"
42 #include "Route.h"
43 
44 namespace Marble
45 {
46 
47 int const maxTilesCount = 100000;
48 int const minimumRouteOffset = 0;
49 int const maximumRouteOffset = 10000;
50 int averageTileSize = 13; //The average size of a tile in kilobytes
51 
52 class Q_DECL_HIDDEN DownloadRegionDialog::Private
53 {
54 public:
55  Private( MarbleWidget *const widget, QDialog * const dialog );
56  QWidget * createSelectionMethodBox();
57  QLayout * createTilesCounter();
58  QWidget * createOkCancelButtonBox();
59 
60  bool hasRoute() const;
61  bool hasTextureLayer() const;
62  QDialog * m_dialog;
63  QRadioButton * m_visibleRegionMethodButton;
64  QRadioButton * m_specifiedRegionMethodButton;
65  LatLonBoxWidget * m_latLonBoxWidget;
66  TileLevelRangeWidget * m_tileLevelRangeWidget;
67  QRadioButton *m_routeDownloadMethodButton;
68  QLabel* m_routeOffsetLabel;
69  QDoubleSpinBox *m_routeOffsetSpinBox;
70  QLabel * m_tilesCountLabel;
71  QLabel * m_tileSizeInfo;
72  QPushButton * m_okButton;
73  QPushButton * m_applyButton;
74  TextureLayer const * m_textureLayer;
75  VectorTileLayer const * m_vectorTileLayer;
76  int m_visibleTileLevel;
77  MarbleModel const*const m_model;
78  MarbleWidget *const m_widget;
79  SelectionMethod m_selectionMethod;
80  GeoDataLatLonAltBox m_visibleRegion;
81  RoutingModel *m_routingModel;
82  DownloadRegion m_downloadRegion;
83 };
84 
85 DownloadRegionDialog::Private::Private( MarbleWidget * const widget,
86  QDialog * const dialog )
87  : m_dialog( dialog ),
88  m_visibleRegionMethodButton( nullptr ),
89  m_specifiedRegionMethodButton( nullptr ),
90  m_latLonBoxWidget( new LatLonBoxWidget ),
91  m_tileLevelRangeWidget( new TileLevelRangeWidget ),
92  m_routeDownloadMethodButton( nullptr ),
93  m_routeOffsetLabel( nullptr ),
94  m_routeOffsetSpinBox( nullptr ),
95  m_tilesCountLabel( nullptr ),
96  m_tileSizeInfo( nullptr ),
97  m_okButton( nullptr ),
98  m_applyButton( nullptr ),
99  m_textureLayer( widget->textureLayer() ),
100  m_vectorTileLayer( widget->vectorTileLayer() ),
101  m_visibleTileLevel( m_textureLayer->tileZoomLevel() ),
102  m_model( widget->model() ),
103  m_widget( widget ),
104  m_selectionMethod( VisibleRegionMethod ),
105  m_visibleRegion(),
106  m_routingModel( widget->model()->routingManager()->routingModel() )
107 {
108  m_latLonBoxWidget->setEnabled( false );
109  m_latLonBoxWidget->setLatLonBox( m_visibleRegion );
110  m_tileLevelRangeWidget->setDefaultLevel( m_visibleTileLevel );
111  m_downloadRegion.setMarbleModel( widget->model() );
112 }
113 
114 QWidget * DownloadRegionDialog::Private::createSelectionMethodBox()
115 {
116  m_visibleRegionMethodButton = new QRadioButton( tr( "Visible region" ) );
117  m_specifiedRegionMethodButton = new QRadioButton( tr( "Specify region" ) );
118 
119  m_routeDownloadMethodButton = new QRadioButton( tr( "Download Route" ) );
120  m_routeDownloadMethodButton->setToolTip( tr( "Enabled when a route exists" ) );
121  m_routeDownloadMethodButton->setEnabled( hasRoute() );
122  m_routeDownloadMethodButton->setChecked( hasRoute() );
123  m_routeOffsetSpinBox = new QDoubleSpinBox();
124  m_routeOffsetSpinBox->setEnabled( hasRoute() );
125  m_routeOffsetSpinBox->setRange( minimumRouteOffset, maximumRouteOffset );
126  int defaultOffset = 500;
127  m_routeOffsetSpinBox->setValue( defaultOffset );
128  m_routeOffsetSpinBox->setSingleStep( 100 );
129  m_routeOffsetSpinBox->setSuffix( " m" );
130  m_routeOffsetSpinBox->setDecimals( 0 );
131  m_routeOffsetSpinBox->setAlignment( Qt::AlignRight );
132 
133  m_routeOffsetLabel = new QLabel( tr( "Offset from route:" ) );
134  m_routeOffsetLabel->setAlignment( Qt::AlignHCenter );
135 
136  connect( m_visibleRegionMethodButton, SIGNAL(toggled(bool)),
137  m_dialog, SLOT(toggleSelectionMethod()) );
138  connect( m_specifiedRegionMethodButton, SIGNAL(toggled(bool)),
139  m_dialog, SLOT(toggleSelectionMethod()));
140  connect( m_routeDownloadMethodButton, SIGNAL(toggled(bool)),
141  m_dialog, SLOT(toggleSelectionMethod()) );
142  connect( m_routingModel, SIGNAL(modelReset()), m_dialog, SLOT(updateRouteDialog()) );
143  connect( m_routingModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
144  m_dialog, SLOT(updateRouteDialog()) );
145  connect( m_routingModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
146  m_dialog, SLOT(updateRouteDialog()) );
147 
148  QHBoxLayout *routeOffsetLayout = new QHBoxLayout;
149  routeOffsetLayout->addWidget( m_routeOffsetLabel );
150  routeOffsetLayout->insertSpacing( 0, 25 );
151  routeOffsetLayout->addWidget( m_routeOffsetSpinBox );
152 
153  QVBoxLayout * const routeLayout = new QVBoxLayout;
154  routeLayout->addWidget( m_routeDownloadMethodButton );
155  routeLayout->addLayout( routeOffsetLayout );
156 
157  QVBoxLayout * const layout = new QVBoxLayout;
158  layout->addWidget( m_visibleRegionMethodButton );
159  layout->addLayout( routeLayout );
160  layout->addWidget( m_specifiedRegionMethodButton );
161  layout->addWidget( m_latLonBoxWidget );
162 
163  bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
164  m_specifiedRegionMethodButton->setVisible( !smallScreen );
165  m_latLonBoxWidget->setVisible( !smallScreen );
166 
167  if ( smallScreen ) {
168  QWidget * const selectionMethodWidget = new QWidget;
169  selectionMethodWidget->setLayout( layout );
170  return selectionMethodWidget;
171  } else {
172  QGroupBox * const selectionMethodBox = new QGroupBox( tr( "Selection Method" ) );
173  selectionMethodBox->setLayout( layout );
174  return selectionMethodBox;
175  }
176 }
177 
178 QLayout * DownloadRegionDialog::Private::createTilesCounter()
179 {
180  QLabel * const description = new QLabel( tr( "Number of tiles to download:" ) );
181  m_tilesCountLabel = new QLabel;
182  m_tileSizeInfo = new QLabel;
183 
184  QHBoxLayout * const tilesCountLayout = new QHBoxLayout;
185  tilesCountLayout->addWidget( description );
186  tilesCountLayout->addWidget( m_tilesCountLabel );
187  //tilesCountLayout->insertSpacing( 0, 5 );
188  QVBoxLayout * const layout = new QVBoxLayout;
189  layout->addLayout( tilesCountLayout );
190  layout->addWidget( m_tileSizeInfo );
191  return layout;
192 }
193 
194 QWidget * DownloadRegionDialog::Private::createOkCancelButtonBox()
195 {
196  QDialogButtonBox * const buttonBox = new QDialogButtonBox;
197  m_okButton = buttonBox->addButton( QDialogButtonBox::Ok );
198  m_applyButton = buttonBox->addButton( QDialogButtonBox::Apply );
199  if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) {
200  buttonBox->removeButton( m_applyButton );
201  m_applyButton->setVisible( false );
202  }
203  buttonBox->addButton( QDialogButtonBox::Cancel );
204  connect( buttonBox, SIGNAL(accepted()), m_dialog, SLOT(accept()) );
205  connect( buttonBox, SIGNAL(rejected()), m_dialog, SLOT(reject()) );
206  connect( m_applyButton, SIGNAL(clicked()), m_dialog, SIGNAL(applied()) );
207  return buttonBox;
208 }
209 
210 bool DownloadRegionDialog::Private::hasRoute() const
211 {
212  return !m_routingModel->route().path().isEmpty();
213 }
214 
215 bool DownloadRegionDialog::Private::hasTextureLayer() const
216 {
217  return m_model->mapTheme()->map()->hasTextureLayers();
218 }
219 
220 DownloadRegionDialog::DownloadRegionDialog( MarbleWidget *const widget, QWidget * const parent,
221  Qt::WindowFlags const f )
222  : QDialog( parent, f ),
223  d( new Private( widget, this ))
224 {
225  setWindowTitle( tr( "Download Region" ));
226  QVBoxLayout * const layout = new QVBoxLayout;
227  layout->addWidget( d->createSelectionMethodBox() );
228  layout->addWidget( d->m_tileLevelRangeWidget );
229  layout->addLayout( d->createTilesCounter() );
230 
231  if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) {
232  QWidget* widget = new QWidget( this );
233  widget->setLayout( layout );
234  QScrollArea* scrollArea = new QScrollArea( this );
235  scrollArea->setFrameShape( QFrame::NoFrame );
236  scrollArea->setWidget( widget );
237  QVBoxLayout * const mainLayout = new QVBoxLayout;
238  mainLayout->addWidget( scrollArea );
239  mainLayout->addWidget( d->createOkCancelButtonBox() );
240  setLayout( mainLayout );
241  } else {
242  layout->addWidget( d->createOkCancelButtonBox() );
243  setLayout( layout );
244  }
245 
246  connect( d->m_latLonBoxWidget, SIGNAL(valueChanged()), SLOT(updateTilesCount()) );
247  connect( d->m_tileLevelRangeWidget, SIGNAL(topLevelChanged(int)),
248  SLOT(updateTilesCount()) );
249  connect( d->m_tileLevelRangeWidget, SIGNAL(bottomLevelChanged(int)),
250  SLOT(updateTilesCount()) );
251  connect( d->m_routeOffsetSpinBox, SIGNAL(valueChanged(double)), SLOT(updateTilesCount()) );
252  connect( d->m_routeOffsetSpinBox, SIGNAL(valueChanged(double)), SLOT(setOffsetUnit()) );
253  connect( d->m_model, SIGNAL(themeChanged(QString)), SLOT(updateTilesCount()) );
254 }
255 
256 DownloadRegionDialog::~DownloadRegionDialog()
257 {
258  delete d;
259 }
260 
261 void DownloadRegionDialog::setAllowedTileLevelRange( int const minimumTileLevel,
262  int const maximumTileLevel )
263 {
264  d->m_tileLevelRangeWidget->setAllowedLevelRange( minimumTileLevel, maximumTileLevel );
265 }
266 
267 void DownloadRegionDialog::setVisibleTileLevel( int const tileLevel )
268 {
269  d->m_visibleTileLevel = tileLevel;
270  d->m_tileLevelRangeWidget->setDefaultLevel( tileLevel );
271  d->m_downloadRegion.setVisibleTileLevel( tileLevel );
272 }
273 
274 void DownloadRegionDialog::setSelectionMethod( SelectionMethod const selectionMethod )
275 {
276  // block signals to prevent infinite recursion:
277  // radioButton->setChecked() -> toggleSelectionMethod() -> setSelectionMethod()
278  // -> radioButton->setChecked() -> ...
279  d->m_visibleRegionMethodButton->blockSignals( true );
280  d->m_specifiedRegionMethodButton->blockSignals( true );
281  d->m_routeDownloadMethodButton->blockSignals( true );
282 
283  d->m_selectionMethod = selectionMethod;
284  switch ( selectionMethod ) {
285  case VisibleRegionMethod:
286  d->m_visibleRegionMethodButton->setChecked( true );
287  d->m_routeOffsetLabel->setEnabled( false );
288  d->m_routeOffsetSpinBox->setEnabled( false );
289  d->m_latLonBoxWidget->setEnabled( false );
290  setSpecifiedLatLonAltBox( d->m_visibleRegion );
291  break;
292  case SpecifiedRegionMethod:
293  d->m_specifiedRegionMethodButton->setChecked( true );
294  d->m_routeOffsetLabel->setEnabled( false );
295  d->m_routeOffsetSpinBox->setEnabled( false );
296  d->m_latLonBoxWidget->setEnabled( true );
297  break;
298  case RouteDownloadMethod:
299  d->m_routeDownloadMethodButton->setChecked( true );
300  d->m_routeOffsetLabel->setEnabled( true );
301  d->m_routeOffsetSpinBox->setEnabled( true );
302  d->m_latLonBoxWidget->setEnabled( false );
303  }
304 
305  updateTilesCount();
306  d->m_visibleRegionMethodButton->blockSignals( false );
307  d->m_specifiedRegionMethodButton->blockSignals( false );
308  d->m_routeDownloadMethodButton->blockSignals( false );
309 }
310 
311 QVector<TileCoordsPyramid> DownloadRegionDialog::region() const
312 {
313  if ( !d->hasTextureLayer() ) {
315  }
316 
317  const TileLayer * tileLayer = d->m_textureLayer->textureLayerCount() > 0 ? dynamic_cast<const TileLayer *>(d->m_textureLayer) : dynamic_cast<const TileLayer *>(d->m_vectorTileLayer);
318 
319  // TODO: enhance for VectorLayer support
320  d->m_downloadRegion.setTileLevelRange( d->m_tileLevelRangeWidget->topLevel(),
321  d->m_tileLevelRangeWidget->bottomLevel() );
322  d->m_downloadRegion.setVisibleTileLevel( d->m_visibleTileLevel );
323  // check whether "visible region" or "lat/lon region" is selection method
324  GeoDataLatLonAltBox downloadRegion;
325  switch ( d->m_selectionMethod ) {
326  case VisibleRegionMethod:
327  downloadRegion = d->m_visibleRegion;
328  break;
329  case SpecifiedRegionMethod:
330  downloadRegion = GeoDataLatLonAltBox( d->m_latLonBoxWidget->latLonBox(), 0, 0 );
331  break;
332  case RouteDownloadMethod:
333  qreal offset = d->m_routeOffsetSpinBox->value();
334  if (d->m_routeOffsetSpinBox->suffix() == QLatin1String(" km")) {
335  offset *= KM2METER;
336  }
337  const GeoDataLineString waypoints = d->m_model->routingManager()->routingModel()->route().path();
338  return d->m_downloadRegion.fromPath( tileLayer, offset, waypoints );
339  }
340 
341  return d->m_downloadRegion.region( tileLayer, downloadRegion );
342 }
343 
344 void DownloadRegionDialog::setSpecifiedLatLonAltBox( GeoDataLatLonAltBox const & region )
345 {
346  d->m_latLonBoxWidget->setLatLonBox( region );
347 }
348 
349 void DownloadRegionDialog::setVisibleLatLonAltBox( GeoDataLatLonAltBox const & region )
350 {
351  d->m_visibleRegion = region;
352 
353  // update lat/lon widget only if not active to prevent that users unintentionally loose
354  // entered values
355  if ( d->m_selectionMethod == VisibleRegionMethod ) {
356  setSpecifiedLatLonAltBox( region );
357  }
358  updateTilesCount();
359 }
360 
361 void DownloadRegionDialog::updateTextureLayer()
362 {
363  mDebug() << "DownloadRegionDialog::updateTextureLayer";
364  updateTilesCount();
365 }
366 
367 void DownloadRegionDialog::hideEvent( QHideEvent * event )
368 {
369  disconnect( d->m_widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)),
370  this, SLOT(setVisibleLatLonAltBox(GeoDataLatLonAltBox)) );
371  disconnect( d->m_widget, SIGNAL(themeChanged(QString)),
372  this, SLOT(updateTextureLayer()) );
373 
374  emit hidden();
375  event->accept();
376 }
377 
378 void DownloadRegionDialog::showEvent( QShowEvent * event )
379 {
380  connect( d->m_widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)),
381  this, SLOT(setVisibleLatLonAltBox(GeoDataLatLonAltBox)) );
382  connect( d->m_widget, SIGNAL(themeChanged(QString)),
383  this, SLOT(updateTextureLayer()) );
384 
385  emit shown();
386  event->accept();
387 }
388 
389 void DownloadRegionDialog::toggleSelectionMethod()
390 {
391  // TODO:QButtonGroup would be easier to handle
392  switch ( d->m_selectionMethod ) {
393  case VisibleRegionMethod:
394  if( d->m_specifiedRegionMethodButton->isChecked() ) {
395  setSelectionMethod( SpecifiedRegionMethod );
396  }
397  else if( d->m_routeDownloadMethodButton->isChecked() ) {
398  setSelectionMethod( RouteDownloadMethod );
399  }
400 
401  break;
402  case SpecifiedRegionMethod:
403  if( d->m_visibleRegionMethodButton->isChecked() ) {
404  setSelectionMethod( VisibleRegionMethod );
405  }
406  else if ( d->m_routeDownloadMethodButton->isChecked() ) {
407  setSelectionMethod( RouteDownloadMethod );
408  }
409  break;
410  case RouteDownloadMethod:
411  if( d->m_specifiedRegionMethodButton->isChecked() ) {
412  setSelectionMethod( SpecifiedRegionMethod );
413  }
414  else if ( d->m_visibleRegionMethodButton->isChecked() ) {
415  setSelectionMethod( VisibleRegionMethod );
416  }
417  break;
418 
419  }
420 }
421 
422 void DownloadRegionDialog::updateTilesCount()
423 {
424  if ( !isVisible() || !d->hasTextureLayer() ) {
425  return;
426  }
427 
428  qint64 tilesCount = 0;
429  QString themeId( d->m_model->mapThemeId() );
430  QVector<TileCoordsPyramid> const pyramid = region();
431  Q_ASSERT( !pyramid.isEmpty() );
432  if( pyramid.size() == 1 ) {
433  tilesCount = pyramid[0].tilesCount();
434  }
435  else {
436  for( int level = pyramid[0].bottomLevel(); level>= pyramid[0].topLevel(); --level ) {
437  QSet<TileId> tileIdSet;
438  for( int i = 0; i < pyramid.size(); ++i ) {
439  QRect const coords = pyramid[i].coords( level );
440  int x1, y1, x2, y2;
441  coords.getCoords( &x1, &y1, &x2, &y2 );
442  for ( int x = x1; x <= x2; ++x ) {
443  for ( int y = y1; y <= y2; ++y ) {
444  TileId const tileId( 0, level, x, y );
445  tileIdSet.insert( tileId );
446  }
447  }
448  }
449  tilesCount += tileIdSet.count();
450  }
451  }
452 
453  if ( tilesCount > maxTilesCount ) {
454  d->m_tileSizeInfo->setToolTip( QString() );
455  //~ singular There is a limit of %n tile to download.
456  //~ plural There is a limit of %n tiles to download.
457  d->m_tileSizeInfo->setText( tr( "There is a limit of %n tile(s) to download.", "",
458  maxTilesCount ) );
459  } else if (themeId == QLatin1String("earth/openstreetmap/openstreetmap.dgml")) {
460  qreal tileDownloadSize = tilesCount * averageTileSize;
461 
462  d->m_tileSizeInfo->setToolTip( tr( "Approximate size of the tiles to be downloaded" ) );
463 
464  if( tileDownloadSize > 1024 ) {
465  tileDownloadSize = tileDownloadSize / 1024;
466  d->m_tileSizeInfo->setText( tr( "Estimated download size: %1 MB" ).arg( ceil( tileDownloadSize ) ) );
467  }
468  else {
469  d->m_tileSizeInfo->setText( tr( "Estimated download size: %1 kB" ).arg( tileDownloadSize ) );
470  }
471 
472  }
473  else {
474  d->m_tileSizeInfo->setToolTip( QString() );
475  d->m_tileSizeInfo->clear();
476  }
477 
478  d->m_tilesCountLabel->setText( QString::number( tilesCount ) );
479  bool const tilesCountWithinLimits = tilesCount > 0 && tilesCount <= maxTilesCount;
480  d->m_okButton->setEnabled( tilesCountWithinLimits );
481  d->m_applyButton->setEnabled( tilesCountWithinLimits );
482 }
483 
484 void DownloadRegionDialog::updateRouteDialog()
485 {
486  d->m_routeDownloadMethodButton->setEnabled( d->hasRoute() );
487  d->m_routeDownloadMethodButton->setChecked( d->hasRoute() );
488  if( !d->hasRoute() ) {
489  setSelectionMethod( VisibleRegionMethod );
490  }
491 }
492 
493 void DownloadRegionDialog::setOffsetUnit()
494 {
495  qreal offset = d->m_routeOffsetSpinBox->value();
496 
497  if( offset >= 1100 ) {
498  d->m_routeOffsetSpinBox->setSuffix( " km" );
499  d->m_routeOffsetSpinBox->setRange( minimumRouteOffset * METER2KM, maximumRouteOffset * METER2KM );
500  d->m_routeOffsetSpinBox->setDecimals( 1 );
501  d->m_routeOffsetSpinBox->setValue( offset * METER2KM );
502  d->m_routeOffsetSpinBox->setSingleStep( 0.1 );
503  }
504  else if (offset <= 1 && d->m_routeOffsetSpinBox->suffix() == QLatin1String(" km")) {
505  d->m_routeOffsetSpinBox->setSuffix( " m" );
506  d->m_routeOffsetSpinBox->setRange( minimumRouteOffset, maximumRouteOffset );
507  d->m_routeOffsetSpinBox->setDecimals( 0 );
508  d->m_routeOffsetSpinBox->setValue( offset * KM2METER );
509  d->m_routeOffsetSpinBox->setSingleStep( 100 );
510  }
511 }
512 
513 }
514 
515 #include "moc_DownloadRegionDialog.cpp"
AlignRight
bool isEmpty() const const
QString number(int n, int base)
int count() const const
QStringView level(QStringView ifopt)
void insertSpacing(int index, int size)
void setFrameShape(QFrame::Shape)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
typedef WindowFlags
void getCoords(int *x1, int *y1, int *x2, int *y2) const const
void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role)
Binds a QML item to a specific geodetic location in screen coordinates.
void removeButton(QAbstractButton *button)
QSet::iterator insert(const T &value)
int size() const const
void addLayout(QLayout *layout, int stretch)
void setLayout(QLayout *layout)
QString tr(const char *sourceText, const char *disambiguation, int n)
void setWidget(QWidget *widget)
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu May 26 2022 04:07:48 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.