File indexing completed on 2025-01-05 03:51:59

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2003-11-03
0007  * Description : painter class to draw calendar.
0008  *
0009  * SPDX-FileCopyrightText: 2003-2005 by Renchi Raju <renchi dot raju at gmail dot com>
0010  * SPDX-FileCopyrightText: 2007-2008 by Orgad Shaneh <orgads at gmail dot com>
0011  * SPDX-FileCopyrightText: 2012      by Angelo Naselli <anaselli at linux dot it>
0012  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "calpainter.h"
0019 
0020 // Qt includes
0021 
0022 #include <QDate>
0023 #include <QFileInfo>
0024 #include <QPointer>
0025 #include <QPaintDevice>
0026 #include <QRect>
0027 #include <QString>
0028 #include <QUrl>
0029 #include <QImage>
0030 #include <QLocale>
0031 #include <QScopedPointer>
0032 
0033 // Local includes
0034 
0035 #include "calsettings.h"
0036 #include "calsystem.h"
0037 #include "digikam_debug.h"
0038 #include "metaengine.h"
0039 #include "dimg.h"
0040 #include "previewloadthread.h"
0041 
0042 using namespace Digikam;
0043 
0044 namespace DigikamGenericCalendarPlugin
0045 {
0046 
0047 class Q_DECL_HIDDEN CalPainter::Private
0048 {
0049 public:
0050 
0051     explicit Private()
0052       : cancelled  (false),
0053         orientation(MetaEngine::ORIENTATION_UNSPECIFIED)
0054     {
0055     }
0056 
0057     bool       cancelled;
0058 
0059     int        orientation;
0060 
0061     QImage     image;
0062     QUrl       imagePath;
0063 };
0064 
0065 CalPainter::CalPainter(QPaintDevice* const pDevice)
0066     : QPainter(pDevice),
0067       d       (new Private)
0068 {
0069 }
0070 
0071 CalPainter::~CalPainter()
0072 {
0073     delete d;
0074 }
0075 
0076 void CalPainter::cancel()
0077 {
0078     d->cancelled = true;
0079 }
0080 
0081 void CalPainter::setImage(const QUrl& imagePath)
0082 {
0083     d->imagePath   = imagePath;
0084     QScopedPointer<MetaEngine> meta(new MetaEngine(d->imagePath.toLocalFile()));
0085     d->orientation = (int)meta->getItemOrientation();
0086 }
0087 
0088 void CalPainter::paint(int month)
0089 {
0090     if (!device())
0091     {
0092         return;
0093     }
0094 
0095     int width                   = device()->width();
0096     int height                  = device()->height();
0097     CalSettings* const settings = CalSettings::instance();
0098     CalParams& params           = CalSettings::instance()->params;
0099 
0100     // --------------------------------------------------
0101 
0102     // FIXME: magic number 42
0103 
0104     int days[42];
0105     int startDayOffset = QLocale().weekdays().first();
0106 
0107     for (int i = 0 ; i < 42 ; ++i)
0108     {
0109         days[i] = -1;
0110     }
0111 
0112     QDate date = CalSystem().date(params.year, month, 1);
0113     int s      = date.dayOfWeek();
0114 
0115     if ((s + 7 - startDayOffset) >= 7)
0116     {
0117         s = s - 7;
0118     }
0119 
0120     for (int i = s ; i < (s + CalSystem().daysInMonth(date)) ; ++i)
0121     {
0122         days[i + (7 - startDayOffset)] = i - s + 1;
0123     }
0124 
0125     // -----------------------------------------------
0126 
0127     QRect rCal(0, 0, 0, 0);
0128     QRect rImage(0, 0, 0, 0);
0129     QRect rCalHeader(0, 0, 0, 0);
0130 
0131     int cellSizeX;
0132     int cellSizeY;
0133 
0134     switch (params.imgPos)
0135     {
0136         case (CalParams::Top):
0137         {
0138             rImage.setWidth(width);
0139             rImage.setHeight((int)(height * params.ratio / (params.ratio + 100)));
0140 
0141             int remainingHeight = height - rImage.height();
0142             cellSizeX           = (width - 20) / 7;
0143             cellSizeY           = remainingHeight / 8;
0144 
0145             rCal.setWidth(cellSizeX * 7);
0146             rCal.setHeight(cellSizeY * 7);
0147 
0148             rCalHeader.setWidth(rCal.width());
0149             rCalHeader.setHeight(cellSizeY);
0150             rCalHeader.moveTop(rImage.bottom());
0151             rCalHeader.moveLeft(width / 2 - rCalHeader.width() / 2);
0152 
0153             rCal.moveTopLeft(rCalHeader.bottomLeft());
0154 
0155             break;
0156         }
0157 
0158         case (CalParams::Left):
0159         {
0160             rImage.setHeight(height);
0161             rImage.setWidth((int)(width * params.ratio / (params.ratio + 100)));
0162 
0163             int remainingWidth  = width - rImage.width();
0164             cellSizeX           = (remainingWidth - 20) / 8;
0165             cellSizeY           = height / 8;
0166 
0167             rCal.setWidth(cellSizeX * 7);
0168             rCal.setHeight(cellSizeY * 7);
0169 
0170             rCalHeader.setWidth(rCal.width());
0171             rCalHeader.setHeight(cellSizeY);
0172             rCalHeader.moveLeft(rImage.right() + cellSizeX);
0173 
0174             rCal.moveTopLeft(rCalHeader.bottomLeft());
0175 
0176             break;
0177         }
0178 
0179         case (CalParams::Right):
0180         {
0181             rImage.setHeight(height);
0182             rImage.setWidth((int)(width * params.ratio / (params.ratio + 100)));
0183 
0184             int remainingWidth  = width - rImage.width();
0185             cellSizeX           = (remainingWidth - 20) / 8;
0186             cellSizeY           = height / 8;
0187 
0188             rCal.setWidth(cellSizeX * 7);
0189             rCal.setHeight(cellSizeY * 7);
0190 
0191             rCalHeader.setWidth(rCal.width());
0192             rCalHeader.setHeight(cellSizeY);
0193             rCal.moveTop(rCalHeader.bottom());
0194 
0195             rImage.moveLeft(width - rImage.width());
0196 
0197             break;
0198         }
0199 
0200         default:
0201         {
0202             return;
0203         }
0204     }
0205 
0206     int fontPixels = cellSizeX / 3;
0207     params.baseFont.setPixelSize(fontPixels);
0208 
0209     // ---------------------------------------------------------------
0210 
0211     fillRect(0, 0, width, height, Qt::white);
0212     setFont(params.baseFont);
0213 
0214     // ---------------------------------------------------------------
0215 
0216     save();
0217     QFont f(params.baseFont);
0218     f.setBold(true);
0219     f.setPixelSize(f.pixelSize() + 5);
0220     setFont(f);
0221     drawText(rCalHeader, Qt::AlignLeft  | Qt::AlignVCenter, QString::number(params.year));
0222     drawText(rCalHeader, Qt::AlignRight | Qt::AlignVCenter, QLocale().standaloneMonthName(month));
0223     restore();
0224 
0225     // ---------------------------------------------------------------
0226 
0227     int   sx, sy;
0228     QRect r, rsmall, rSpecial;
0229 
0230     r.setWidth(cellSizeX);
0231     r.setHeight(cellSizeY);
0232 
0233     int index = 0;
0234 
0235     save();
0236 
0237     setPen(Qt::red);
0238     sy = rCal.top();
0239 
0240     for (int i = 0 ; i < 7 ; ++i)
0241     {
0242         int dayname = i + startDayOffset;
0243 
0244         if (dayname > 7)
0245         {
0246             dayname = dayname - 7;
0247         }
0248 
0249         sx     = cellSizeX * i + rCal.left();
0250         r.moveTopLeft(QPoint(sx, sy));
0251         rsmall = r;
0252         rsmall.setWidth(r.width() - (r.width() / 10));
0253         rsmall.setHeight(r.height() - 2);
0254         drawText(rsmall, Qt::AlignRight | Qt::AlignBottom,
0255                  QLocale().standaloneDayName(dayname, QLocale::ShortFormat));
0256     }
0257 
0258     restore();
0259 
0260     for (int j = 0 ; j < 6 ; ++j)
0261     {
0262         sy = cellSizeY * (j + 1) + rCal.top();
0263 
0264         for (int i = 0 ; i < 7 ; ++i)
0265         {
0266             sx     = cellSizeX * i + rCal.left();
0267             r.moveTopLeft(QPoint(sx, sy));
0268             rsmall = r;
0269             rsmall.setWidth(r.width() - (r.width() / 10));
0270             rsmall.setHeight(r.height() - 2);
0271 
0272             if (days[index] != -1)
0273             {
0274                 if (settings->isSpecial(month, days[index]))
0275                 {
0276                     save();
0277                     setPen(settings->getDayColor(month, days[index]));
0278                     drawText(rsmall, Qt::AlignRight | Qt::AlignBottom,
0279                              QString::number(days[index]));
0280 
0281                     QString descr = settings->getDayDescr(month, days[index]);
0282 
0283                     qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Painting special info: '" << descr
0284                                                          << "' for date " << days[index] << "/"
0285                                                          << month;
0286 
0287                     rSpecial = rsmall;
0288                     rSpecial.translate(2, 0);
0289                     QFont fnt(params.baseFont);
0290                     fnt.setPixelSize(fnt.pixelSize() / 3);
0291                     setFont(fnt);
0292 
0293                     drawText(rSpecial, Qt::AlignLeft | Qt::AlignTop, descr);
0294 
0295                     restore();
0296                 }
0297                 else
0298                 {
0299                     drawText(rsmall, Qt::AlignRight | Qt::AlignBottom,
0300                              QString::number(days[index]));
0301                 }
0302             }
0303 
0304             index++;
0305         }
0306     }
0307 
0308     // ---------------------------------------------------------------
0309 
0310     if (params.drawLines)
0311     {
0312         sx = rCal.left();
0313 
0314         for (int j = 0 ; j < 8 ; ++j)
0315         {
0316             sy = cellSizeY * j + rCal.top();
0317             drawLine(sx, sy, rCal.right(), sy);
0318         }
0319 
0320         sy = rCal.top();
0321 
0322         for (int i = 0 ; i < 8 ; ++i)
0323         {
0324             sx = cellSizeX * i + rCal.left();
0325             drawLine(sx, sy, sx, rCal.bottom());
0326         }
0327     }
0328 
0329     d->image = PreviewLoadThread::loadHighQualitySynchronously(d->imagePath.toLocalFile()).copyQImage();
0330 
0331     if (d->image.isNull())
0332     {
0333         fillRect(rImage, Qt::blue);
0334     }
0335     else
0336     {
0337         if ( d->orientation != MetaEngine::ORIENTATION_UNSPECIFIED )
0338         {
0339             QScopedPointer<MetaEngine> meta(new MetaEngine);
0340             meta->rotateExifQImage(d->image, (MetaEngine::ImageOrientation)d->orientation);
0341         }
0342 
0343         Q_EMIT signalProgress(0);
0344 
0345         d->image = d->image.scaled(rImage.width(),
0346                                    rImage.height(),
0347                                    Qt::KeepAspectRatio,
0348                                    Qt::SmoothTransformation);
0349 
0350         Q_EMIT signalTotal(d->image.height());
0351 
0352         int h         = d->image.height();
0353         int x         = rImage.bottomLeft().x() + (rImage.width() - d->image.width()) / 2;
0354         int y         = (rImage.height() - h) / 2;
0355         int blockSize = 10;
0356         int block     = 0;
0357 
0358         while ((block < h) && !d->cancelled)
0359         {
0360             if (block + blockSize > h)
0361             {
0362                 blockSize = h - block;
0363             }
0364 
0365             drawImage(x, y + block, d->image, 0, block, d->image.width(), blockSize);
0366             block += blockSize;
0367             Q_EMIT signalProgress(block);
0368         }
0369 
0370         Q_EMIT signalFinished();
0371     }
0372 }
0373 
0374 } // namespace Digikam
0375 
0376 #include "moc_calpainter.cpp"