Marble

MapThemeDownloadDialog.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
4// SPDX-FileCopyrightText: 2013 Dennis Nienhüser <nienhueser@kde.org>
5//
6
7#include "MapThemeDownloadDialog.h"
8#include "ui_MapThemeDownloadDialog.h"
9
10#include "MarbleDirs.h"
11#include "NewstuffModel.h"
12#include "MarbleWidget.h"
13
14#include <QPainter>
15#include <QTextDocument>
16#include <QAbstractTextDocumentLayout>
17#include <QStyledItemDelegate>
18
19namespace Marble
20{
21
22class MapItemDelegate : public QStyledItemDelegate
23{
25
26public:
27 MapItemDelegate( QListView* view, NewstuffModel* newstuffModel, MarbleWidget* marbleWidget );
28 void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
29 QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
30
31protected:
32 bool editorEvent( QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index ) override;
33
34private:
35 enum Element {
36 Icon,
37 Text,
38 InstallButton,
39 UpgradeButton,
40 OpenButton,
41 CancelButton,
42 RemoveButton,
43 ProgressReport
44 };
45
46 int buttonWidth( const QStyleOptionViewItem &option ) const;
47 QStyleOptionButton button( Element element, const QStyleOptionViewItem &option ) const;
48 QRect position( Element element, const QStyleOptionViewItem &option ) const;
49 static QString text( const QModelIndex &index );
50 QListView* m_view;
51 NewstuffModel* m_newstuffModel;
52 mutable int m_buttonWidth;
53 int const m_margin;
54 int const m_iconSize;
55 MarbleWidget* m_marbleWidget;
56};
57
58class Q_DECL_HIDDEN MapThemeDownloadDialog::Private : public Ui::MapThemeDownloadDialog
59{
60public:
61 Private() :
62 m_model()
63 {}
64
65 NewstuffModel m_model;
66};
67
68MapThemeDownloadDialog::MapThemeDownloadDialog( MarbleWidget* marbleWidget ) :
69 QDialog( marbleWidget ),
70 d( new Private )
71{
72 d->setupUi( this );
73
74 d->m_model.setTargetDirectory(MarbleDirs::localPath() + QLatin1String("/maps"));
75 d->m_model.setProvider( "https://marble.kde.org/maps-v3.xml" );
76 d->m_model.setRegistryFile(MarbleDirs::localPath() + QLatin1String("/newstuff/marble-map-themes.knsregistry"), Marble::NewstuffModel::NameTag);
77
78 d->listView->setIconSize( QSize( 130, 130 ) );
79 d->listView->setAlternatingRowColors( true );
80 d->listView->setUniformItemSizes( false );
81 d->listView->setResizeMode( QListView::Adjust );
82 d->listView->setItemDelegate( new MapItemDelegate( d->listView, &d->m_model, marbleWidget ) );
83 d->listView->setModel( &d->m_model );
84}
85
86MapThemeDownloadDialog::~MapThemeDownloadDialog()
87{
88 delete d;
89}
90
91MapItemDelegate::MapItemDelegate( QListView *view , NewstuffModel *newstuffModel, MarbleWidget* marbleWidget ) :
92 m_view( view ),
93 m_newstuffModel( newstuffModel ),
94 m_buttonWidth( 0 ),
95 m_margin( 5 ),
96 m_iconSize( 16 ),
97 m_marbleWidget( marbleWidget )
98{
99 // nothing to do
100}
101
102void MapItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
103{
104 QStyleOptionViewItem styleOption = option;
105 styleOption.text = QString();
107
109 if (styleOption.state & QStyle::State_Selected) {
110 paintContext.palette.setColor(QPalette::Text,
111 styleOption.palette.color(QPalette::Active, QPalette::HighlightedText));
112 }
113
114 // Draw the map preview icon
115 QRect const iconRect = position( Icon, option );
116 QIcon const icon = index.data( Qt::DecorationRole ).value<QIcon>();
117 painter->drawPixmap( iconRect, icon.pixmap( iconRect.size() ) );
118
119 // Draw summary, author, and similar information
120 QTextDocument document;
121 QRect const textRect = position( Text, option );
122 document.setTextWidth( textRect.width() );
123 document.setDefaultFont( option.font );
124 document.setHtml( text( index ) );
125
126 painter->save();
127 painter->translate( textRect.topLeft() );
128 painter->setClipRect( 0, 0, textRect.width(), textRect.height() );
129 document.documentLayout()->draw( painter, paintContext );
130 painter->restore();
131
132 // Draw buttons and installation progress
133 if ( index.data( NewstuffModel::IsTransitioning ).toBool() ) {
134 qint64 total = qMax( qint64( 1 ), index.data( NewstuffModel::PayloadSize ).value<qint64>() );
135 qint64 progress = index.data( NewstuffModel::DownloadedSize ).value<qint64>();
136
137 QStyleOptionProgressBar progressBarOption;
138 progressBarOption.rect = position( ProgressReport, option );
139 progressBarOption.minimum = 0;
140 progressBarOption.maximum = 100;
141 progressBarOption.progress = ( 100.0 * progress / total );
142 progressBarOption.text = QString::number(progressBarOption.progress) + QLatin1Char('%');
143 progressBarOption.textVisible = true;
144 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
145
146 QStyleOptionButton cancelButton = button( CancelButton, option );
147 QRect installRect = position( CancelButton, option );
148 cancelButton.rect = installRect;
149 QApplication::style()->drawControl( QStyle::CE_PushButton, &cancelButton, painter );
150 QRect buttonTextRect(installRect);
151 buttonTextRect.adjust(cancelButton.iconSize.width() + 4, 0, 0, 0);
152 painter->drawText(buttonTextRect, Qt::AlignCenter, cancelButton.text);
153 } else {
154 bool const installed = index.data( NewstuffModel::IsInstalled ).toBool();
155 bool const upgradable = index.data( NewstuffModel::IsUpgradable ).toBool();
156 Element element = InstallButton;
157 if ( installed ) {
158 element = upgradable ? UpgradeButton : OpenButton;
159 }
160 QStyleOptionButton actionButton = button( element, option );
161 QRect installRect = position( element, option );
162 actionButton.rect = installRect;
163 QApplication::style()->drawControl( QStyle::CE_PushButton, &actionButton, painter );
164 QRect buttonTextRect(installRect);
165 buttonTextRect.adjust(actionButton.iconSize.width() + 4, 0, 0, 0);
166 painter->drawText(buttonTextRect, Qt::AlignCenter, actionButton.text);
167
168 if ( installed ) {
169 QStyleOptionButton removeButton = button( RemoveButton, option );
170 QRect removeRect = position( RemoveButton, option );
171 removeButton.rect = removeRect;
172 QApplication::style()->drawControl( QStyle::CE_PushButton, &removeButton, painter );
173 buttonTextRect = removeRect;
174 buttonTextRect.adjust(removeButton.iconSize.width() + 4, 0, 0 ,0);
175 painter->drawText(buttonTextRect, Qt::AlignCenter, removeButton.text);
176 }
177 }
178}
179
180QSize MapItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
181{
182 if ( index.column() == 0 ) {
183 QSize const iconSize = option.decorationSize;
184 QTextDocument doc;
185 doc.setDefaultFont( option.font );
186 doc.setTextWidth( qMax( 200, m_view->contentsRect().width() - iconSize.width() - buttonWidth( option ) - 3 * m_margin ) );
187 doc.setHtml( text( index ) );
188 return QSize( iconSize.width() + doc.size().width() + buttonWidth( option ) + 3 * m_margin,
189 2 + qMax( iconSize.height(), qRound( doc.size().height() ) ) );
190 }
191
192 return QSize();
193}
194
195bool MapItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *, const QStyleOptionViewItem &option, const QModelIndex &index)
196{
197 if ( ( event->type() == QEvent::MouseButtonRelease ) ) {
198 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
199 if ( index.data( NewstuffModel::IsTransitioning ).toBool() ) {
200 QRect cancelRect = position( CancelButton, option );
201 if ( cancelRect.contains( mouseEvent->pos() ) ) {
202 m_newstuffModel->cancel( index.row() );
203 return true;
204 }
205 } else {
206 bool const installed = index.data( NewstuffModel::IsInstalled ).toBool();
207 bool const upgradable = index.data( NewstuffModel::IsUpgradable ).toBool();
208
209 if ( !installed || upgradable ) {
210 QRect installRect = position( InstallButton, option );
211 if ( installRect.contains( mouseEvent->pos() ) ) {
212 m_newstuffModel->install( index.row() );
213 return true;
214 }
215 }
216
217 if ( installed && !upgradable && m_marbleWidget ) {
218 QRect openRect = position( OpenButton, option );
219 if ( openRect.contains( mouseEvent->pos() ) ) {
220 QStringList const files = index.data( NewstuffModel::InstalledFiles ).toStringList();
221 for( const QString &file: files ) {
222 if ( file.endsWith( QLatin1String( ".dgml" ) ) ) {
223 QFileInfo dgmlFile( file );
224 QDir baseDir = dgmlFile.dir();
225 baseDir.cdUp();
226 baseDir.cdUp();
227 int const index = baseDir.absolutePath().size();
228 QString const mapTheme = dgmlFile.absoluteFilePath().mid( index+1 );
229 m_marbleWidget->setMapThemeId( mapTheme );
230 return true;
231 }
232 }
233 }
234 }
235
236 if ( installed ) {
237 QRect removeRect = position( RemoveButton, option );
238 if ( removeRect.contains( mouseEvent->pos() ) ) {
239 m_newstuffModel->uninstall( index.row() );
240 return true;
241 }
242 }
243 }
244 }
245
246 return false;
247}
248
249int MapItemDelegate::buttonWidth(const QStyleOptionViewItem &option) const
250{
251 if ( m_buttonWidth <= 0 ) {
252 int const installWidth = option.fontMetrics.size( 0, tr( "Install" ) ).width();
253 int const removeWidth = option.fontMetrics.size( 0, tr( "Remove" ) ).width();
254 int const cancelWidth = option.fontMetrics.size( 0, tr( "Cancel" ) ).width();
255 int const upgradeWidth = option.fontMetrics.size( 0, tr( "Upgrade" ) ).width();
256 m_buttonWidth = 2 * m_iconSize + qMax( qMax( installWidth, removeWidth ),
257 qMax( cancelWidth, upgradeWidth ) );
258 }
259
260 return m_buttonWidth;
261}
262
263QStyleOptionButton MapItemDelegate::button( Element element, const QStyleOptionViewItem &option ) const
264{
265 QStyleOptionButton result;
266 result.state = option.state;
267 result.state &= ~QStyle::State_HasFocus;
268
269 result.palette = option.palette;
270 result.features = QStyleOptionButton::None;
271
272 switch (element) {
273 case InstallButton:
274 result.text = tr( "Install" );
275 result.icon = QIcon(QStringLiteral(":/marble/dialog-ok.png"));
276 result.iconSize = QSize( m_iconSize, m_iconSize );
277 break;
278 case UpgradeButton:
279 result.text = tr( "Update" );
280 result.icon = QIcon(QStringLiteral(":/marble/system-software-update.png"));
281 result.iconSize = QSize( m_iconSize, m_iconSize );
282 break;
283 case OpenButton:
284 result.text = tr( "Open" );
285 result.icon = QIcon(QStringLiteral(":/marble/document-open.png"));
286 result.iconSize = QSize( m_iconSize, m_iconSize );
287 break;
288 case CancelButton:
289 result.text = tr( "Cancel" );
290 break;
291 case RemoveButton:
292 result.text = tr( "Remove" );
293 result.icon = QIcon(QStringLiteral(":/marble/edit-delete.png"));
294 result.iconSize = QSize( m_iconSize, m_iconSize );
295 break;
296 default:
297 // ignored
298 break;
299 }
300
301 return result;
302}
303
304QRect MapItemDelegate::position(Element element, const QStyleOptionViewItem &option ) const
305{
306 int const width = buttonWidth( option );
307 QPoint const topLeftCol1 = option.rect.topLeft() + QPoint( 0, 2 );
308 QPoint const topLeftCol2 = topLeftCol1 + QPoint( option.decorationSize.width(), 0 );
309 QPoint const topLeftCol3 = topLeftCol2 + QPoint( option.rect.width() - 3 * m_margin - width - option.decorationSize.width(), 0 );
310 switch (element) {
311 case Icon:
312 return QRect( topLeftCol1, option.decorationSize );
313 case Text:
314 return QRect( topLeftCol2, QSize( topLeftCol3.x()-topLeftCol2.x(), option.rect.height() ) );
315 case InstallButton:
316 case UpgradeButton:
317 case OpenButton:
318 {
319 QStyleOptionButton optionButton = button( element, option );
320 QSize size = option.fontMetrics.size( 0, optionButton.text ) + QSize( 4, 4 );
321 QSize buttonSize = QApplication::style()->sizeFromContents( QStyle::CT_PushButton, &optionButton, size );
322 buttonSize.setWidth( width );
323 return QRect( topLeftCol3, buttonSize );
324 }
325 case RemoveButton:
326 case CancelButton:
327 {
328 QStyleOptionButton optionButton = button( element, option );
329 QSize size = option.fontMetrics.size( 0, optionButton.text ) + QSize( 4, 4 );
330 QSize buttonSize = QApplication::style()->sizeFromContents( QStyle::CT_PushButton, &optionButton, size );
331 buttonSize.setWidth( width );
332 return QRect( topLeftCol3 + QPoint( 0, option.fontMetrics.height() + 8 + m_margin ), buttonSize );
333 }
334 case ProgressReport:
335 {
336 QSize const progressSize = QSize( width, option.fontMetrics.height() + 4 );
337 return QRect( topLeftCol3 + QPoint( 0, m_margin ), progressSize );
338 }
339 }
340
341 Q_ASSERT(false);
342 return QRect();
343}
344
345QString MapItemDelegate::text( const QModelIndex &index )
346{
347 qreal const size = index.data( NewstuffModel::PayloadSize ).toLongLong() / 1024.0 / 1024.0;
348 // Fields are typically not longer than 200 characters. Prevent excessive long text here anyway
349 // due to bug 319542
350 int const maxEntrySize = 4096;
351 return QString("<p><b>%1</b><br />%2</p><p>Author: %3<br />License: %4<br />Version %5 (%6) %7</p>")
352 .arg( index.data().toString() )
353 .arg( index.data( NewstuffModel::Summary ).toString().left( maxEntrySize ) )
354 .arg( index.data( NewstuffModel::Author ).toString().left( maxEntrySize ) )
355 .arg( index.data( NewstuffModel::License ).toString().left( maxEntrySize ) )
356 .arg( index.data( NewstuffModel::Version ).toString().left( maxEntrySize ) )
357 .arg( index.data( NewstuffModel::ReleaseDate ).toString().left( maxEntrySize ) )
358 .arg( size > 0 ? QString( "%1 MB" ).arg( size, 0, 'f', 1 ) : QString() );
359}
360
361}
362
363#include "MapThemeDownloadDialog.moc" // needed for Q_OBJECT here in source
364
365#include "moc_MapThemeDownloadDialog.cpp"
This file contains the headers for MarbleWidget.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
Binds a QML item to a specific geodetic location in screen coordinates.
virtual void draw(QPainter *painter, const PaintContext &context)=0
QStyle * style()
QString absolutePath() const const
bool cdUp()
MouseButtonRelease
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
int column() const const
QVariant data(int role) const const
int row() const const
QPoint pos() const const
Q_OBJECTQ_OBJECT
virtual bool event(QEvent *e)
T qobject_cast(QObject *object)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void drawText(const QPoint &position, const QString &text)
void restore()
void save()
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void translate(const QPoint &offset)
int x() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
bool contains(const QPoint &point, bool proper) const const
int height() const const
QSize size() const const
QPoint topLeft() const const
int width() const const
int height() const const
void setWidth(int width)
int width() const const
QString arg(Args &&... args) const const
QString left(qsizetype n) const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
qsizetype size() const const
virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
virtual QSize sizeFromContents(ContentsType type, const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const const=0
AlignCenter
DecorationRole
QAbstractTextDocumentLayout * documentLayout() const const
void setDefaultFont(const QFont &font)
void setHtml(const QString &html)
void setTextWidth(qreal width)
bool toBool() const const
qlonglong toLongLong(bool *ok) const const
QString toString() const const
QStringList toStringList() const const
T value() const const
void setupUi(QWidget *widget)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.