]> git.mxchange.org Git - flightgear.git/blob - src/GUI/AircraftItemDelegate.cxx
Code cleanups, code updates and fix at least on (possible) devide-by-zero
[flightgear.git] / src / GUI / AircraftItemDelegate.cxx
1 // AircraftItemDelegate.cxx - part of GUI launcher using Qt5
2 //
3 // Written by James Turner, started March 2015.
4 //
5 // Copyright (C) 2014 James Turner <zakalawe@mac.com>
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21
22 #include "AircraftItemDelegate.hxx"
23
24 #include <QDebug>
25 #include <QPainter>
26 #include <QLinearGradient>
27 #include <QListView>
28 #include <QMouseEvent>
29 #include <QFontMetrics>
30
31 #include "AircraftModel.hxx"
32
33 AircraftItemDelegate::AircraftItemDelegate(QListView* view) :
34     m_view(view)
35 {
36     view->viewport()->installEventFilter(this);
37     view->viewport()->setMouseTracking(true);
38
39     m_leftArrowIcon.load(":/left-arrow-icon");
40     m_rightArrowIcon.load(":/right-arrow-icon");
41 }
42
43 void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option,
44     const QModelIndex & index) const
45 {
46     QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
47
48     QVariant v = index.data(AircraftPackageStatusRole);
49     AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
50     if (status == NoOfficialCatalogMessage) {
51         painter->setPen(QColor(0x7f, 0x7f, 0x7f));
52         painter->setBrush(Qt::NoBrush);
53
54         // draw bottom dividing line
55         painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
56                           contentRect.right(), contentRect.bottom() + MARGIN);
57
58         return;
59     }
60
61     // selection feedback rendering
62     if (option.state & QStyle::State_Selected) {
63         QLinearGradient grad(option.rect.topLeft(), option.rect.bottomLeft());
64         grad.setColorAt(0.0, QColor(152, 163, 180));
65         grad.setColorAt(1.0, QColor(90, 107, 131));
66
67         QBrush backgroundBrush(grad);
68         painter->fillRect(option.rect, backgroundBrush);
69
70         painter->setPen(QColor(90, 107, 131));
71         painter->drawLine(option.rect.topLeft(), option.rect.topRight());
72     }
73
74
75     QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
76     quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2);
77     painter->drawPixmap(contentRect.left(), yPos, thumbnail);
78
79     // draw 1px frame
80     painter->setPen(QColor(0x7f, 0x7f, 0x7f));
81     painter->setBrush(Qt::NoBrush);
82     painter->drawRect(contentRect.left(), yPos, thumbnail.width(), thumbnail.height());
83
84     // draw bottom dividing line
85     painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
86                       contentRect.right(), contentRect.bottom() + MARGIN);
87
88     int variantCount = index.data(AircraftVariantCountRole).toInt();
89     int currentVariant = index.data(AircraftVariantRole).toInt();
90     QString description = index.data(Qt::DisplayRole).toString();
91     contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
92
93     painter->setPen(Qt::black);
94     QFont f;
95     f.setPointSize(18);
96     painter->setFont(f);
97
98     QRect descriptionRect = contentRect.adjusted(ARROW_SIZE, 0, -ARROW_SIZE, 0),
99         actualBounds;
100
101     if (variantCount > 0) {
102         bool canLeft = (currentVariant > 0);
103         bool canRight =  (currentVariant < variantCount );
104
105         QRect leftArrowRect = leftCycleArrowRect(option.rect, index);
106         if (canLeft) {
107             painter->drawPixmap(leftArrowRect.topLeft() + QPoint(2, 2), m_leftArrowIcon);
108         }
109
110         QRect rightArrowRect = rightCycleArrowRect(option.rect, index);
111         if (canRight) {
112             painter->drawPixmap(rightArrowRect.topLeft() + QPoint(2, 2), m_rightArrowIcon);
113         }
114     }
115
116     painter->drawText(descriptionRect, Qt::TextWordWrap, description, &actualBounds);
117     QString authors = index.data(AircraftAuthorsRole).toString();
118
119     f.setPointSize(12);
120     QFontMetrics smallMetrics(f);
121
122     painter->setFont(f);
123
124     if (!authors.isEmpty()) {
125         QRect authorsRect = descriptionRect;
126         authorsRect.moveTop(actualBounds.bottom() + MARGIN);
127         painter->drawText(authorsRect, Qt::TextWordWrap,
128                           QString("by: %1").arg(authors),
129                           &actualBounds);
130     }
131
132     QString longDescription = index.data(AircraftLongDescriptionRole).toString();
133     if (!longDescription.isEmpty()) {
134         QRect longDescriptionRect = descriptionRect;
135         longDescriptionRect.moveTop(actualBounds.bottom() + MARGIN);
136         painter->drawText(longDescriptionRect, Qt::TextWordWrap,
137                           longDescription, &actualBounds);
138     }
139
140     QRect r = contentRect;
141     r.setWidth(contentRect.width() / 3);
142     r.moveTop(actualBounds.bottom() + MARGIN);
143     r.moveLeft(r.right());
144     r.setHeight(qMax(24, smallMetrics.height() + MARGIN));
145
146     painter->setRenderHint(QPainter::Antialiasing, true);
147
148     if (index.data(AircraftHasRatingsRole).toBool()) {
149         drawRating(painter, "Flight model:", r, index.data(AircraftRatingRole).toInt());
150         r.moveTop(r.bottom());
151         drawRating(painter, "Systems:", r, index.data(AircraftRatingRole + 1).toInt());
152
153         r.moveTop(actualBounds.bottom() + MARGIN);
154         r.moveLeft(r.right());
155         drawRating(painter, "Cockpit:", r, index.data(AircraftRatingRole + 2).toInt());
156         r.moveTop(r.bottom());
157         drawRating(painter, "Exterior:", r, index.data(AircraftRatingRole + 3).toInt());
158     }
159
160     double downloadFraction = 0.0;
161     
162     if (status != PackageInstalled) {
163         QString buttonText, infoText;
164         QColor buttonColor(27, 122, 211);
165         
166         double sizeInMBytes = index.data(AircraftPackageSizeRole).toInt();
167         sizeInMBytes /= 0x100000;
168         
169         if (status == PackageDownloading) {
170             buttonText = tr("Cancel");
171             double downloadedMB = index.data(AircraftInstallDownloadedSizeRole).toInt();
172             downloadedMB /= 0x100000;
173             infoText = QStringLiteral("%1 MB of %2 MB").arg(downloadedMB, 0, 'f', 1).arg(sizeInMBytes, 0, 'f', 1);
174             buttonColor = QColor(0xcf, 0xcf, 0xcf);
175             downloadFraction = downloadedMB / sizeInMBytes;
176         } else if (status == PackageQueued) {
177             buttonText = tr("Cancel");
178             infoText = tr("Waiting to download %1 MB").arg(sizeInMBytes, 0, 'f', 1);
179             buttonColor = QColor(0xcf, 0xcf, 0xcf);
180         } else {
181             infoText = QStringLiteral("%1MB").arg(sizeInMBytes, 0, 'f', 1);
182             if (status == PackageNotInstalled) {
183                 buttonText = "Install";
184             } else if (status == PackageUpdateAvailable) {
185                 buttonText = "Update";
186             }
187         }
188         
189         painter->setBrush(Qt::NoBrush);
190         QRect buttonRect = packageButtonRect(option.rect, index);
191         painter->setPen(Qt::NoPen);
192         painter->setBrush(buttonColor);
193         painter->drawRoundedRect(buttonRect, 5, 5);
194         painter->setPen(Qt::white);
195         painter->drawText(buttonRect, Qt::AlignCenter, buttonText);
196         
197         QRect infoTextRect = buttonRect;
198         infoTextRect.setLeft(buttonRect.right() + MARGIN);
199         infoTextRect.setWidth(200);
200         
201         if (status == PackageDownloading) {
202             QRect progressRect = infoTextRect;
203             progressRect.setHeight(6);
204             painter->setPen(QPen(QColor(0xcf, 0xcf, 0xcf), 0));
205             painter->setBrush(Qt::NoBrush);
206             painter->drawRoundedRect(progressRect, 3, 3);
207             infoTextRect.setTop(progressRect.bottom() + 1);
208
209             QRect progressBarRect = progressRect.marginsRemoved(QMargins(2, 2, 2, 2));
210             
211             progressBarRect.setWidth(static_cast<int>(progressBarRect.width() * downloadFraction));
212             
213             painter->setBrush(QColor(27, 122, 211));
214             painter->setPen(Qt::NoPen);
215             painter->drawRoundedRect(progressBarRect, 2, 2);
216         }
217         
218         painter->setPen(Qt::black);
219         painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText);
220     } // of update / install / download status
221
222     painter->setRenderHint(QPainter::Antialiasing, false);
223
224 }
225
226 QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
227 {
228     QVariant v = index.data(AircraftPackageStatusRole);
229     AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
230
231     if (status == NoOfficialCatalogMessage) {
232         QSize r = option.rect.size();
233         r.setHeight(100);
234         return r;
235     }
236
237     QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
238
239     QSize thumbnailSize = index.data(AircraftThumbnailSizeRole).toSize();
240     contentRect.setLeft(contentRect.left() + MARGIN + thumbnailSize.width());
241     contentRect.setBottom(9999); // large value to avoid clipping
242     contentRect.adjust(ARROW_SIZE, 0, -ARROW_SIZE, 0);
243
244     QFont f;
245     f.setPointSize(18);
246     QFontMetrics metrics(f);
247
248     int textHeight = metrics.boundingRect(contentRect, Qt::TextWordWrap,
249                                           index.data().toString()).height();
250
251     f.setPointSize(12);
252     QFontMetrics smallMetrics(f);
253
254     QString authors = QString("by: %1").arg(index.data(AircraftAuthorsRole).toString());
255
256     if (!authors.isEmpty()) {
257         textHeight += MARGIN;
258         textHeight += smallMetrics.boundingRect(contentRect, Qt::TextWordWrap, authors).height();
259     }
260
261     QString desc = index.data(AircraftLongDescriptionRole).toString();
262     if (!desc.isEmpty()) {
263         textHeight += MARGIN;
264         textHeight += smallMetrics.boundingRect(contentRect, Qt::TextWordWrap, desc).height();
265     }
266
267     if (index.data(AircraftHasRatingsRole).toBool()) {
268         // ratings
269         int ratingHeight = qMax(24, smallMetrics.height() + MARGIN);
270         textHeight += ratingHeight * 2;
271     } else {
272         // just the button height
273         textHeight += BUTTON_HEIGHT;
274     }
275
276     textHeight = qMax(textHeight, thumbnailSize.height());
277     return QSize(option.rect.width(), textHeight + (MARGIN * 2));
278 }
279
280 bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
281 {
282     if ( event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease )
283     {
284         QMouseEvent* me = static_cast< QMouseEvent* >( event );
285         QModelIndex index = m_view->indexAt( me->pos() );
286         int variantCount = index.data(AircraftVariantCountRole).toInt();
287         int variantIndex = index.data(AircraftVariantRole).toInt();
288         QRect vr = m_view->visualRect(index);
289
290         if ( (event->type() == QEvent::MouseButtonRelease) && (variantCount > 0) )
291         {
292             QRect leftCycleRect = leftCycleArrowRect(vr, index),
293                 rightCycleRect = rightCycleArrowRect(vr, index);
294
295             if ((variantIndex > 0) && leftCycleRect.contains(me->pos())) {
296                 m_view->model()->setData(index, variantIndex - 1, AircraftVariantRole);
297                 emit variantChanged(index);
298                 return true;
299             } else if ((variantIndex < variantCount) && rightCycleRect.contains(me->pos())) {
300                 m_view->model()->setData(index, variantIndex + 1, AircraftVariantRole);
301                 emit variantChanged(index);
302                 return true;
303             }
304         }
305         
306         if ((event->type() == QEvent::MouseButtonRelease) &&
307             packageButtonRect(vr, index).contains(me->pos()))
308         {
309             QVariant v = index.data(AircraftPackageStatusRole);
310             AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
311             if (status == PackageNotInstalled) {
312                 emit requestInstall(index);
313             } else if ((status == PackageDownloading) || (status == PackageQueued)) {
314                 emit cancelDownload(index);
315             } else if (status == PackageUpdateAvailable) {
316                 emit requestInstall(index);
317             }
318             
319             return true;
320         }
321     } else if ( event->type() == QEvent::MouseMove ) {
322 #if 0
323         QMouseEvent* me = static_cast< QMouseEvent* >( event );
324         QModelIndex index = m_view->indexAt( me->pos() );
325         QRect vr = m_view->visualRect(index);
326
327         if (packageButtonRect(vr, index).contains(me->pos())) {
328             qDebug() << "mouse inside button";
329         }
330 #endif
331     }
332     
333     return false;
334 }
335
336
337 QRect AircraftItemDelegate::leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const
338 {
339     QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
340     QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
341     contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
342
343     QRect r = contentRect;
344     r.setRight(r.left() + ARROW_SIZE);
345     r.setBottom(r.top() + ARROW_SIZE);
346     return r;
347
348 }
349
350 QRect AircraftItemDelegate::rightCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const
351 {
352     QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
353     QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
354     contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
355
356     QRect r = contentRect;
357     r.setLeft(r.right() - ARROW_SIZE);
358     r.setBottom(r.top() + ARROW_SIZE);
359     return r;
360
361 }
362
363 QRect AircraftItemDelegate::packageButtonRect(const QRect& visualRect, const QModelIndex& index) const
364 {
365     QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
366     QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
367     contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
368
369     return QRect(contentRect.left() + ARROW_SIZE, contentRect.bottom() - 24,
370                  BUTTON_WIDTH, BUTTON_HEIGHT);
371 }
372
373 void AircraftItemDelegate::drawRating(QPainter* painter, QString label, const QRect& box, int value) const
374 {
375     const int DOT_SIZE = 10;
376     const int DOT_MARGIN = 2;
377
378     QRect dotBox = box;
379     dotBox.setLeft(box.right() - (DOT_MARGIN * 6 + DOT_SIZE * 5));
380
381     painter->setPen(Qt::black);
382     QRect textBox = box;
383     textBox.setRight(dotBox.left() - DOT_MARGIN);
384     painter->drawText(textBox, Qt::AlignVCenter | Qt::AlignRight, label);
385
386     painter->setPen(Qt::NoPen);
387     QRect dot(dotBox.left() + DOT_MARGIN,
388               dotBox.center().y() - (DOT_SIZE / 2),
389               DOT_SIZE,
390               DOT_SIZE);
391     for (int i=0; i<5; ++i) {
392         painter->setBrush((i < value) ? QColor(0x3f, 0x3f, 0x3f) : QColor(0xaf, 0xaf, 0xaf));
393         painter->drawEllipse(dot);
394         dot.moveLeft(dot.right() + DOT_MARGIN);
395     }
396 }