/* Copyright 2008-2009 by Frederik Gladhorn Copyright 2008-2009 by Sascha Peilicke 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "marble.h" #include #include #include #include #include #include #include #include #include #include #define DRAG_THRESHOLD 3 // Distance (pixel) before starting drag updates #define DEFAULT_UPDATE_INTERVAL 300000 // Wait time between redraws in msecs (5 min) #define MOVEMENT_KEY "movement" // Type of movement #define ROTATION_LON_KEY "rotateLongitude" // Amount of rotation (degrees/second) #define ROTATION_LAT_KEY "rotateLatitude" #define ROTATION_TIMEOUT_KEY "rotationTimeout" #define POSITION_LON_KEY "positionLongitude" // Last position #define POSITION_LAT_KEY "positionLatitude" #define ZOOM_KEY "zoom" // Zoom distance #define MAP_THEME_KEY "mapTheme" // Marble settings #define PROJECTION_KEY "projection" // Projection type #define QUALITY_KEY "quality" // Render quality #define SHOW_PLACEMARKS_KEY "showPlacemarks" namespace Marble { MarbleWallpaper::MarbleWallpaper(QObject * parent, const QVariantList & args) : Plasma::Wallpaper(parent, args), m_timer(0), m_map(0) { setPreviewDuringConfiguration(true); KGlobal::locale()->insertCatalog(QLatin1String( "marble" )); } MarbleWallpaper::~MarbleWallpaper() { delete m_map; delete m_timer; } void MarbleWallpaper::init(const KConfigGroup &config) { qreal home_lon = 0; qreal home_lat = 0; int home_zoom = 0; // Only on first start, otherwise opening the config dialog lets us // lose the current position if (!isInitialized()) { m_map = new MarbleMap(); // Get default position from marble to initialize on first startup (empty config) m_map->model()->home(home_lon, home_lat, home_zoom); m_map->model()->setClockDateTime( QDateTime::currentDateTime().toUTC() ); // These settings apply to Marble's "satellite" view mostly, e.g. make it beautiful m_map->setShowClouds(true); m_map->setShowCityLights(false); m_map->setShowSunShading(true); // Disable all render plugins (scale bar, compass, etc.) except the "stars" plugin foreach (RenderPlugin *item, m_map->renderPlugins()) { if (item->nameId() == QLatin1String( "stars" )) { item->setVisible(true); item->setEnabled(true); } else { item->setVisible(false); item->setEnabled(false); } } } // Read setting values or use defaults m_mapTheme = config.readEntry(MAP_THEME_KEY, QString::fromLatin1("earth/bluemarble/bluemarble.dgml")); m_movement = static_cast(config.readEntry(MOVEMENT_KEY, static_cast(Interactive))); m_positionLon = config.readEntry(POSITION_LON_KEY, home_lon); m_positionLat = config.readEntry(POSITION_LAT_KEY, home_lat); m_projection = static_cast(config.readEntry(PROJECTION_KEY, static_cast(Spherical))); m_quality = static_cast(config.readEntry(QUALITY_KEY, static_cast(NormalQuality))); m_rotationLat = config.readEntry(ROTATION_LAT_KEY, 0.0); m_rotationLon = config.readEntry(ROTATION_LON_KEY, 0.025); m_rotationTimeout = config.readEntry(ROTATION_TIMEOUT_KEY, 10000); m_showPlacemarks = config.readEntry(SHOW_PLACEMARKS_KEY, false); m_zoom = config.readEntry(ZOOM_KEY, home_zoom); m_map->setMapThemeId(m_mapTheme); m_map->setProjection(m_projection); m_map->setShowCities(m_showPlacemarks); m_map->setShowOtherPlaces(m_showPlacemarks); m_map->setShowPlaces(m_showPlacemarks); m_map->setShowTerrain(m_showPlacemarks); if (!isInitialized()) { qreal radius = pow(M_E, (m_zoom / 200.0)); m_map->setRadius(radius); m_map->centerOn(m_positionLon, m_positionLat); } updateGlobe(); } QWidget *MarbleWallpaper::createConfigurationInterface(QWidget *parent) { QWidget *configWidget = new QWidget(parent); m_ui.setupUi(configWidget); m_ui.movement->setCurrentIndex(static_cast(m_movement)); m_ui.projection->setCurrentIndex(static_cast(m_projection)); // The first MapQuality value is wireframe, which we don't show in the list m_ui.quality->setCurrentIndex(static_cast(m_quality) - 1); m_ui.rotationLon->setValue(m_rotationLon); m_ui.rotationLat->setValue(m_rotationLat); m_ui.timeout->setValue(m_rotationTimeout / 1000); m_ui.showPlacemarks->setChecked(m_showPlacemarks); MapThemeManager themeManager; // FIXME: Going manually through the model is ugly as hell, but plugging the // model into the view didn't work for me for (int i = 0; i < themeManager.mapThemeModel()->rowCount(); i++) { QModelIndex index = themeManager.mapThemeModel()->index(i, 0, QModelIndex()); QString theme = themeManager.mapThemeModel()->data(index, Qt::DisplayRole).toString(); QIcon icon = qvariant_cast(themeManager.mapThemeModel()->data(index, Qt::DecorationRole)); QModelIndex fileIndex = themeManager.mapThemeModel()->index(i, 0, QModelIndex()); QString themeFile = themeManager.mapThemeModel()->data(fileIndex, Qt::UserRole + 1).toString(); m_ui.themeList->addItem(icon, theme, themeFile); if (m_mapTheme == themeFile) { m_ui.themeList->setCurrentIndex(i); } } // Trigger initial visual movement setup updateConfigScreen(static_cast(m_movement)); connect(m_ui.movement, SIGNAL(currentIndexChanged(int)), SLOT(updateConfigScreen(int))); connect(m_ui.movement, SIGNAL(currentIndexChanged(int)), SLOT(updateSettings())); connect(m_ui.projection, SIGNAL(currentIndexChanged(int)), SLOT(updateSettings())); connect(m_ui.quality, SIGNAL(currentIndexChanged(int)), SLOT(updateSettings())); connect(m_ui.rotationLon, SIGNAL(valueChanged(double)), SLOT(updateSettings())); connect(m_ui.rotationLat, SIGNAL(valueChanged(double)), SLOT(updateSettings())); connect(m_ui.timeout, SIGNAL(valueChanged(double)), SLOT(updateSettings())); connect(m_ui.showPlacemarks, SIGNAL(stateChanged(int)), SLOT(updateSettings())); connect(m_ui.themeList, SIGNAL(currentIndexChanged(int)), SLOT(changeTheme(int))); // Notify the plasma background dialog of changes connect(this, SIGNAL(settingsChanged(bool)), parent, SLOT(settingsChanged(bool))); return configWidget; } void MarbleWallpaper::save(KConfigGroup &config) { if (m_map) { config.writeEntry(MAP_THEME_KEY, m_map->mapThemeId()); config.writeEntry(POSITION_LAT_KEY, m_map->centerLatitude()); config.writeEntry(POSITION_LON_KEY, m_map->centerLongitude()); } config.writeEntry(MOVEMENT_KEY, static_cast(m_movement)); config.writeEntry(ZOOM_KEY, m_zoom); config.writeEntry(PROJECTION_KEY, static_cast(m_projection)); config.writeEntry(QUALITY_KEY, static_cast(m_quality)); config.writeEntry(ROTATION_LAT_KEY, m_rotationLat); config.writeEntry(ROTATION_LON_KEY, m_rotationLon); config.writeEntry(ROTATION_TIMEOUT_KEY, m_rotationTimeout); config.writeEntry(SHOW_PLACEMARKS_KEY, m_showPlacemarks); } void MarbleWallpaper::paint(QPainter *painter, const QRectF &exposedRect) { // Update the pixmap if (m_pixmap.size() != boundingRect().size().toSize()) { m_pixmap = QPixmap(boundingRect().size().toSize()); } if (m_pixmap.size().isEmpty()) { return; } m_map->setSize(m_pixmap.size()); m_pixmap.fill(QColor(0x00, 0x00, 0x00, 0xFF)); GeoPainter gp(&m_pixmap, m_map->viewport(), m_quality); QRect mapRect(0, 0, m_map->width(), m_map->height()); m_map->paint(gp, mapRect); // Draw the requested part of the pixmap painter->drawPixmap(exposedRect, m_pixmap, exposedRect.translated(-boundingRect().topLeft())); } void MarbleWallpaper::wheelEvent(QGraphicsSceneWheelEvent *event) { if (m_movement == Interactive) { event->accept(); m_zoom = qMax(qreal(0), m_zoom + (event->delta() > 0 ? 40 : -40)); qreal radius = pow(M_E, (m_zoom / 200.0)); m_map->setRadius(radius); emit update(boundingRect()); } } void MarbleWallpaper::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (m_movement == Interactive && event->buttons() == Qt::LeftButton) { event->accept(); int polarity = m_map->viewport()->polarity(); qreal radius = (qreal)(m_map->radius()); int deltaX = event->screenPos().x() - m_dragStartPositionX; int deltaY = event->screenPos().y() - m_dragStartPositionY; // Only start dragging after a certain distance if (abs(deltaX) <= DRAG_THRESHOLD && abs(deltaY) <= DRAG_THRESHOLD) { return; } // Reverse spin direction if globe is turned upside down qreal const direction = polarity < 0 ? -1 : 1; m_positionLon = RAD2DEG * (qreal)(m_leftPressedTranslationX) - 90.0 * direction * deltaX / radius; m_positionLat = RAD2DEG * (qreal)(m_leftPressedTranslationY) + 90.0 * deltaY / radius; m_map->centerOn(m_positionLon, m_positionLat); emit update(boundingRect()); } } void MarbleWallpaper::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (m_movement == Interactive && event->buttons() == Qt::LeftButton) { event->accept(); // On the single event of a mouse button press these // values get stored, to enable us to e.g. calculate the // distance of a mouse drag while the mouse button is // still down. m_dragStartPositionX = event->screenPos().x(); m_dragStartPositionY = event->screenPos().y(); // Calculate translation of center point m_leftPressedTranslationX = m_map->centerLongitude() * DEG2RAD; m_leftPressedTranslationY = m_map->centerLatitude() * DEG2RAD; } } void MarbleWallpaper::updateGlobe() { if (!m_timer) { m_timer = new QTimer(this); connect(m_timer, SIGNAL(timeout()), SLOT(updateGlobe())); } else { m_timer->stop(); } if (m_movement == Rotate || m_movement == FollowSun) { m_timer->setInterval(m_rotationTimeout); } else { m_timer->setInterval(DEFAULT_UPDATE_INTERVAL); } m_timer->start(); if (m_movement == FollowSun) { m_map->model()->sunLocator()->update(); if (m_map->model()->sunLocator()->getLon() == m_map->centerLongitude()) { return; } m_positionLon = m_map->model()->sunLocator()->getLon(); m_positionLat = m_map->model()->sunLocator()->getLat(); m_map->centerOn(m_positionLon, m_positionLat); } else if (m_movement == Rotate) { m_map->rotateBy(m_rotationLon * m_rotationTimeout / 1000, m_rotationLat * m_rotationTimeout / 1000); m_positionLon = m_map->centerLongitude(); m_positionLat = m_map->centerLatitude(); } emit update(boundingRect()); } void MarbleWallpaper::updateSettings() { m_projection = static_cast(m_ui.projection->currentIndex()); m_rotationLon = m_ui.rotationLon->value(); m_rotationLat = m_ui.rotationLat->value(); m_rotationTimeout = m_ui.timeout->value() * 1000; // The first MapQuality value is wireframe, which we don't show in the list m_quality = static_cast(m_ui.quality->currentIndex() + 1); m_showPlacemarks = m_ui.showPlacemarks->isChecked(); emit settingsChanged(true); } void MarbleWallpaper::changeTheme(int index) { m_mapTheme = m_ui.themeList->itemData(index).toString(); m_map->setMapThemeId(m_mapTheme); emit update(boundingRect()); emit settingsChanged(true); } void MarbleWallpaper::updateConfigScreen(int index) { m_movement = static_cast(index); m_ui.mouseInstructions->setVisible(m_movement == Interactive); if (m_movement == Rotate) { m_ui.rotationLat->setVisible(true); m_ui.rotationLon->setVisible(true); m_ui.labelRotationLat->setVisible(true); m_ui.labelRotationLon->setVisible(true); } else { m_ui.rotationLat->setVisible(false); m_ui.rotationLon->setVisible(false); m_ui.labelRotationLat->setVisible(false); m_ui.labelRotationLon->setVisible(false); } if (m_movement == FollowSun || m_movement == Rotate) { m_ui.timeout->setVisible(true); m_ui.labelTimeout->setVisible(true); } else { m_ui.timeout->setVisible(false); m_ui.labelTimeout->setVisible(false); } emit settingsChanged(true); } } // Marble namespace #include "moc_marble.cpp" // vim: sw=4 sts=4 et tw=100