1 #include "RouteItemDelegate.h"
2 
3 #include "CloudRouteModel.h"
4 
5 #ifdef MARBLE_NO_WEBKITWIDGETS
6 #include <QWidget>
7 #else
8 #include <QWebEngineView>
9 #endif
10 
11 #include <QApplication>
12 #include <QAbstractTextDocumentLayout>
13 #include <QPainter>
14 #include <QListView>
15 
16 namespace Marble {
17 
RouteItemDelegate(QListView * view,CloudRouteModel * model)18 RouteItemDelegate::RouteItemDelegate( QListView *view, CloudRouteModel *model ) :
19     m_view( view ),
20     m_model( model ),
21     m_buttonWidth( 0 ),
22     m_iconSize( 16 ),
23     m_previewSize( 128 ),
24     m_margin( 8 )
25 {
26 }
27 
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const28 void RouteItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
29 {
30     QStyleOptionViewItem styleOption = option;
31     styleOption.text = QString();
32     QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &styleOption, painter );
33 
34     QAbstractTextDocumentLayout::PaintContext paintContext;
35     if ( styleOption.state & QStyle::State_Selected)  {
36         paintContext.palette.setColor( QPalette::Text, styleOption.palette.color( QPalette::Active, QPalette::HighlightedText ) );
37     }
38 
39     QRect const iconRect = position( Preview, option );
40     QIcon const icon = index.data( Qt::DecorationRole ).value<QIcon>();
41     painter->drawPixmap( iconRect, icon.pixmap( iconRect.size() ) );
42 
43     QTextDocument document;
44     QRect const textRect = position( Text, option );
45     document.setTextWidth( textRect.width() );
46     document.setDefaultFont( option.font );
47     document.setHtml( text( index ) );
48 
49     painter->save();
50     painter->translate( textRect.topLeft() );
51     painter->setClipRect( 0, 0, textRect.width(), textRect.height() );
52     document.documentLayout()->draw( painter, paintContext );
53     painter->restore();
54 
55     bool cached = index.data( CloudRouteModel::IsCached ).toBool();
56     bool downloading = index.data( CloudRouteModel::IsDownloading ).toBool();
57     bool onCloud = index.data( CloudRouteModel::IsOnCloud ).toBool();
58 
59     if ( downloading ) {
60         qint64 total = m_model->totalSize();
61         qint64 progress = m_model->downloadedSize();
62 
63         QStyleOptionProgressBar progressBarOption;
64         progressBarOption.rect = position( Progressbar, option );
65         progressBarOption.minimum = 0;
66         progressBarOption.maximum = 100;
67         progressBarOption.progress = ( 100.0 * progress / total );
68         progressBarOption.text = QString::number(progressBarOption.progress) + QLatin1Char('%');
69         progressBarOption.textVisible = true;
70         QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
71     } else if ( !cached && onCloud ) {
72         QStyleOptionButton downloadButton = button( DownloadButton, option );
73         QRect downloadRect = position( DownloadButton, option );
74         downloadButton.rect = downloadRect;
75         QApplication::style()->drawControl( QStyle::CE_PushButton, &downloadButton, painter );
76 
77         QStyleOptionButton cloudRemoveButton = button( RemoveFromCloudButton, option );
78         QRect cloudRemoveRect = position( RemoveFromCloudButton, option );
79         cloudRemoveButton.rect = cloudRemoveRect;
80         QApplication::style()->drawControl( QStyle::CE_PushButton, &cloudRemoveButton, painter );
81     } else if ( cached && !onCloud ) {
82         QStyleOptionButton openButton = button( OpenButton, option );
83         QRect openRect = position( OpenButton, option );
84         openButton.rect = openRect;
85         QApplication::style()->drawControl( QStyle::CE_PushButton, &openButton, painter );
86 
87         QStyleOptionButton cacheRemoveButton = button( RemoveFromCacheButton, option );
88         QRect cacheRemoveRect = position( RemoveFromCacheButton, option );
89         cacheRemoveButton.rect = cacheRemoveRect;
90         QApplication::style()->drawControl( QStyle::CE_PushButton, &cacheRemoveButton, painter );
91 
92         QStyleOptionButton uploadButton = button( UploadToCloudButton, option );
93         QRect uploadRect = position( UploadToCloudButton, option );
94         uploadButton.rect = uploadRect;
95         QApplication::style()->drawControl( QStyle::CE_PushButton, &uploadButton, painter );
96     } else if ( cached && onCloud ) {
97         QStyleOptionButton openButton = button( OpenButton, option );
98         QRect openRect = position( OpenButton, option );
99         openButton.rect = openRect;
100         QApplication::style()->drawControl( QStyle::CE_PushButton, &openButton, painter );
101 
102         QStyleOptionButton cacheRemoveButton = button( RemoveFromCacheButton, option );
103         QRect cacheRemoveRect = position( RemoveFromCacheButton, option );
104         cacheRemoveButton.rect = cacheRemoveRect;
105         QApplication::style()->drawControl( QStyle::CE_PushButton, &cacheRemoveButton, painter );
106     }
107 }
108 
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const109 QSize RouteItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
110 {
111     if ( index.column() == 0 ) {
112         QTextDocument doc;
113         doc.setDefaultFont( option.font );
114         doc.setTextWidth( qMax( 128, m_view->contentsRect().width() - m_previewSize - buttonWidth( option ) ) );
115         doc.setHtml( text( index ) );
116         return QSize( qMax( 256, qRound( m_previewSize + buttonWidth( option ) + doc.size().width() + m_margin * 2 ) ),
117                       qMax( m_previewSize + m_margin * 2, qRound( doc.size().height() ) ) );
118     }
119 
120     return QSize();
121 }
122 
editorEvent(QEvent * event,QAbstractItemModel * model,const QStyleOptionViewItem & option,const QModelIndex & index)123 bool RouteItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
124 {
125     Q_UNUSED( model );
126 
127     if ( ( event->type() == QEvent::MouseButtonRelease ) ) {
128         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
129         QPoint pos = mouseEvent->pos();
130 
131         bool cached = index.data( CloudRouteModel::IsCached ).toBool();
132         bool onCloud = index.data( CloudRouteModel::IsOnCloud ).toBool();
133 
134         if( cached && !onCloud ) {
135             QRect uploadRect = position( UploadToCloudButton, option );
136 
137             if ( uploadRect.contains( pos ) ) {
138                 QString timestamp = index.data( CloudRouteModel::Timestamp ).toString();
139                 emit uploadToCloudButtonClicked( timestamp );
140                 return true;
141             }
142         }
143 
144         if ( cached ) {
145             QRect openRect = position( OpenButton, option );
146             QRect cacheRemoveRect = position( RemoveFromCacheButton, option );
147 
148             if ( openRect.contains( pos ) ) {
149                 QString timestamp = index.data( CloudRouteModel::Timestamp ).toString();
150                 emit openButtonClicked( timestamp );
151                 return true;
152             } else if ( cacheRemoveRect.contains( pos ) ) {
153                 QString timestamp = index.data( CloudRouteModel::Timestamp ).toString();
154                 emit removeFromCacheButtonClicked( timestamp );
155                 return true;
156             }
157         } else {
158             QRect downloadRect = position( DownloadButton, option );
159             QRect cloudRemoveRect = position( RemoveFromCloudButton, option );
160 
161             if ( downloadRect.contains( pos ) ) {
162                 QString timestamp = index.data( CloudRouteModel::Timestamp ).toString();
163                 m_model->setDownloadingItem( index );
164                 emit downloadButtonClicked( timestamp );
165                 return true;
166             }
167 
168             if ( cloudRemoveRect.contains( pos ) ) {
169                 QString timestamp = index.data( CloudRouteModel::Timestamp ).toString();
170                 emit deleteButtonClicked( timestamp );
171                 return true;
172             }
173         }
174     }
175 
176     return false;
177 }
178 
buttonWidth(const QStyleOptionViewItem & option) const179 int RouteItemDelegate::buttonWidth( const QStyleOptionViewItem &option ) const
180 {
181     if ( m_buttonWidth <= 0 ) {
182         int const openWidth = option.fontMetrics.size( 0, tr( "Open" ) ).width();
183         int const downloadWidth = option.fontMetrics.size( 0, tr( "Load" ) ).width();
184         int const cacheWidth = option.fontMetrics.size( 0, tr( "Remove from device" ) ).width();
185         int const cloudWidth = option.fontMetrics.size( 0, tr( "Delete from cloud" ) ).width();
186         m_buttonWidth = 2 * m_iconSize + qMax( qMax( openWidth, downloadWidth ), qMax( cacheWidth, cloudWidth ) );
187     }
188 
189     return m_buttonWidth;
190 }
191 
button(Element element,const QStyleOptionViewItem & option) const192 QStyleOptionButton RouteItemDelegate::button( Element element, const QStyleOptionViewItem &option ) const
193 {
194     QStyleOptionButton result;
195     result.state = option.state;
196     result.state &= ~QStyle::State_HasFocus;
197 
198     result.palette = option.palette;
199     result.features = QStyleOptionButton::None;
200 
201     switch ( element ) {
202     case OpenButton:
203         result.text = tr( "Open" );
204         result.icon = QIcon(QStringLiteral(":/marble/document-open.png"));
205         result.iconSize = QSize( m_iconSize, m_iconSize );
206         break;
207     case DownloadButton:
208         result.text = tr( "Load" );
209         result.icon = QIcon(QStringLiteral(":/marble/dialog-ok.png"));
210         result.iconSize = QSize( m_iconSize, m_iconSize );
211         break;
212     case RemoveFromCacheButton:
213         result.text = tr( "Remove from device" );
214         result.icon = QIcon(QStringLiteral(":/marble/edit-clear.png"));
215         result.iconSize = QSize( m_iconSize, m_iconSize );
216         break;
217     case RemoveFromCloudButton:
218         result.text = tr( "Delete from cloud" );
219         result.icon = QIcon(QStringLiteral(":/marble/edit-delete.png"));
220         result.iconSize = QSize( m_iconSize, m_iconSize );
221         break;
222     case UploadToCloudButton:
223         result.text = tr( "Upload to cloud" );
224         result.icon = QIcon(QStringLiteral(":/icons/cloud-upload.png"));
225         result.iconSize = QSize( m_iconSize, m_iconSize );
226         break;
227     default:
228         // Ignored.
229         break;
230     }
231 
232     return result;
233 }
234 
text(const QModelIndex & index)235 QString RouteItemDelegate::text( const QModelIndex& index )
236 {
237     return QString( "%0" ).arg( index.data( CloudRouteModel::Name ).toString() );
238     // TODO: Show distance and duration
239     //return QString( "%0<br /><b>Duration:</b> %1<br/><b>Distance:</b> %2" )
240             //.arg( index.data( CloudRouteModel::Name ).toString() )
241             //.arg( index.data( CloudRouteModel::Duration ).toString() )
242             //.arg( index.data( CloudRouteModel::Distance ).toString() );
243 }
244 
position(Element element,const QStyleOptionViewItem & option) const245 QRect RouteItemDelegate::position( Element element, const QStyleOptionViewItem& option ) const
246 {
247     int const width = buttonWidth( option );
248     QPoint const firstColumn = option.rect.topLeft() + QPoint( m_margin, m_margin );
249     QPoint const secondColumn = firstColumn + QPoint( m_previewSize + m_margin, 0 );
250     QPoint const thirdColumn = QPoint( option.rect.width() - width - option.decorationSize.width(), firstColumn.y() );
251 
252     switch (element) {
253     case Text:
254         return QRect( secondColumn, QSize( thirdColumn.x() - secondColumn.x(), option.rect.height() ) );
255     case OpenButton:
256     case DownloadButton:
257     {
258         QStyleOptionButton optionButton = button( element, option );
259         QSize size = option.fontMetrics.size( 0, optionButton.text ) + QSize( 4, 4 );
260         QSize buttonSize = QApplication::style()->sizeFromContents( QStyle::CT_PushButton, &optionButton, size );
261         buttonSize.setWidth( width );
262         return QRect( thirdColumn, buttonSize );
263     }
264     case RemoveFromCacheButton:
265     case RemoveFromCloudButton:
266     {
267         QStyleOptionButton optionButton = button( element, option );
268         QSize size = option.fontMetrics.size( 0, optionButton.text ) + QSize( 4, 4 );
269         QSize buttonSize = QApplication::style()->sizeFromContents( QStyle::CT_PushButton, &optionButton, size );
270         buttonSize.setWidth( width );
271         return QRect( thirdColumn + QPoint( 0, buttonSize.height() ), buttonSize );
272     }
273     case Progressbar:
274     {
275         QSize const progressSize = QSize( width, option.fontMetrics.height() + 4 );
276         return QRect( thirdColumn + QPoint( 0, 10 ), progressSize );
277     }
278     case Preview:
279     {
280         return QRect( firstColumn, QSize( m_previewSize, m_previewSize) );
281     }
282     case UploadToCloudButton:
283     {
284         QStyleOptionButton optionButton = button( element, option );
285         QSize size = option.fontMetrics.size( 0, optionButton.text ) + QSize( 4, 4 );
286         QSize buttonSize = QApplication::style()->sizeFromContents( QStyle::CT_PushButton, &optionButton, size );
287         buttonSize.setWidth( width );
288         return QRect( thirdColumn + QPoint( 0, buttonSize.height() * 2 ), buttonSize );
289     }
290     }
291 
292     return QRect();
293 }
294 
295 }
296 
297 #include "moc_RouteItemDelegate.cpp"
298