File indexing completed on 2025-01-19 03:50:33
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2005-05-02 0007 * Description : a widget to perform month selection. 0008 * 0009 * SPDX-FileCopyrightText: 2005 by Renchi Raju <renchi dot raju at gmail dot com> 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2011 by Andi Clemens <andi dot clemens at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "monthwidget.h" 0018 0019 // Qt includes 0020 0021 #include <QList> 0022 #include <QResizeEvent> 0023 #include <QMouseEvent> 0024 #include <QDateTime> 0025 #include <QFontMetrics> 0026 #include <QPainter> 0027 #include <QPixmap> 0028 #include <QPalette> 0029 #include <QTimer> 0030 #include <QLocale> 0031 #include <QDate> 0032 0033 // Local includes 0034 0035 #include "itemfiltermodel.h" 0036 #include "itemmodel.h" 0037 0038 namespace Digikam 0039 { 0040 0041 class Q_DECL_HIDDEN MonthWidget::Private 0042 { 0043 public: 0044 0045 class Q_DECL_HIDDEN Month 0046 { 0047 public: 0048 0049 Month() 0050 : active (false), 0051 selected (false), 0052 day (0), 0053 numImages(0) 0054 { 0055 } 0056 0057 bool active; 0058 bool selected; 0059 0060 int day; 0061 int numImages; 0062 }; 0063 0064 public: 0065 0066 explicit Private() 0067 : active(true), 0068 model (nullptr), 0069 timer (nullptr), 0070 year (0), 0071 month (0), 0072 width (0), 0073 height(0), 0074 currw (0), 0075 currh (0) 0076 { 0077 } 0078 0079 bool active; 0080 0081 ItemFilterModel* model; 0082 QTimer* timer; 0083 0084 int year; 0085 int month; 0086 int width; 0087 int height; 0088 int currw; 0089 int currh; 0090 0091 Month days[42]; 0092 }; 0093 0094 MonthWidget::MonthWidget(QWidget* const parent) 0095 : QWidget(parent), 0096 d (new Private) 0097 { 0098 init(); 0099 0100 QDate date = QDate::currentDate(); 0101 setYearMonth(date.year(), date.month()); 0102 0103 setActive(false); 0104 0105 d->timer = new QTimer(this); 0106 d->timer->setSingleShot(true); 0107 d->timer->setInterval(150); 0108 0109 connect(d->timer, &QTimer::timeout, 0110 this, &MonthWidget::updateDays); 0111 } 0112 0113 MonthWidget::~MonthWidget() 0114 { 0115 delete d; 0116 } 0117 0118 void MonthWidget::init() 0119 { 0120 QFont fn(font()); 0121 fn.setBold(true); 0122 fn.setPointSize(fn.pointSize()+1); 0123 QFontMetrics fm(fn); 0124 QRect r(fm.boundingRect(QLatin1String("XX"))); 0125 r.setWidth(r.width() + 2); 0126 r.setHeight(r.height() + 4); 0127 d->width = r.width(); 0128 d->height = r.height(); 0129 0130 setMinimumWidth(d->width * 8); 0131 setMinimumHeight(d->height * 9); 0132 } 0133 0134 void MonthWidget::setYearMonth(int year, int month) 0135 { 0136 d->year = year; 0137 d->month = month; 0138 0139 for (int i = 0 ; i < 42 ; ++i) 0140 { 0141 d->days[i].active = false; 0142 d->days[i].selected = false; 0143 d->days[i].day = -1; 0144 d->days[i].numImages = 0; 0145 } 0146 0147 QDate date(year, month, 1); 0148 int s = date.dayOfWeek(); 0149 0150 for (int i = s ; i < (s+date.daysInMonth()) ; ++i) 0151 { 0152 d->days[i-1].day = i-s+1; 0153 } 0154 0155 update(); 0156 } 0157 0158 QSize MonthWidget::sizeHint() const 0159 { 0160 return QSize(d->width * 8, d->height * 9); 0161 } 0162 0163 void MonthWidget::resizeEvent(QResizeEvent* e) 0164 { 0165 QWidget::resizeEvent(e); 0166 0167 d->currw = contentsRect().width()/8; 0168 d->currh = contentsRect().height()/9; 0169 } 0170 0171 void MonthWidget::paintEvent(QPaintEvent*) 0172 { 0173 QRect cr(contentsRect()); 0174 0175 qreal dpr = devicePixelRatio(); 0176 0177 QPixmap pix(cr.width() * dpr, cr.height() * dpr); 0178 pix.setDevicePixelRatio(dpr); 0179 0180 QFont fnBold(font()); 0181 QFont fnOrig(font()); 0182 fnBold.setBold(true); 0183 fnOrig.setBold(false); 0184 0185 QPainter p(&pix); 0186 p.fillRect(0, 0, cr.width(), cr.height(), palette().color(QPalette::Window)); 0187 0188 QRect r(0, 0, d->currw, d->currh); 0189 QRect rsmall; 0190 0191 int sx, sy; 0192 int index = 0; 0193 bool weekvisible; 0194 0195 for (int j = 3 ; j < 9 ; ++j) 0196 { 0197 sy = d->currh * j; 0198 weekvisible = false; 0199 0200 for (int i = 1 ; i < 8 ; ++i) 0201 { 0202 sx = d->currw * i; 0203 r.moveTopLeft(QPoint(sx,sy)); 0204 rsmall = QRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); 0205 0206 if (d->days[index].day != -1) 0207 { 0208 if (d->days[index].selected) 0209 { 0210 p.fillRect(r, palette().color(QPalette::Highlight)); 0211 p.setPen(palette().color(QPalette::HighlightedText)); 0212 0213 if (d->days[index].active) 0214 { 0215 p.setFont(fnBold); 0216 } 0217 else 0218 { 0219 p.setFont(fnOrig); 0220 } 0221 } 0222 else 0223 { 0224 if (d->days[index].active) 0225 { 0226 p.setPen(palette().color(QPalette::Text)); 0227 p.setFont(fnBold); 0228 } 0229 else 0230 { 0231 p.setPen(palette().color(QPalette::Mid)); 0232 p.setFont(fnOrig); 0233 } 0234 } 0235 0236 p.drawText(rsmall, Qt::AlignVCenter|Qt::AlignHCenter, 0237 QString::number(d->days[index].day)); 0238 0239 if (!weekvisible) 0240 { 0241 int weeknr = QDate(d->year, d->month, d->days[index].day).weekNumber(); 0242 p.setPen(d->active ? Qt::black : Qt::gray); 0243 p.setFont(fnBold); 0244 p.fillRect(1, sy, d->currw-1, d->currh-1, QColor(210, 210, 210)); 0245 p.drawText(1, sy, d->currw-1, d->currh-1, Qt::AlignVCenter|Qt::AlignHCenter, 0246 QString::number(weeknr)); 0247 weekvisible = true; 0248 } 0249 0250 } 0251 0252 ++index; 0253 } 0254 } 0255 0256 p.setPen(d->active ? Qt::black : Qt::gray); 0257 p.setFont(fnBold); 0258 0259 sy = 2 * d->currh; 0260 0261 for (int i = 1 ; i < 8 ; ++i) 0262 { 0263 sx = d->currw * i; 0264 r.moveTopLeft(QPoint(sx+1,sy+1)); 0265 rsmall = r; 0266 rsmall.setWidth(r.width() - 2); 0267 rsmall.setHeight(r.height() - 2); 0268 p.drawText(rsmall, Qt::AlignVCenter|Qt::AlignHCenter, 0269 QLocale().standaloneDayName(i, QLocale::ShortFormat).remove(2, 1)); 0270 ++index; 0271 } 0272 0273 r = QRect(0, 0, cr.width(), 2*d->currh); 0274 0275 fnBold.setPointSize(fnBold.pointSize()+2); 0276 p.setFont(fnBold); 0277 0278 p.drawText(r, Qt::AlignCenter, QString::fromUtf8("%1 %2") 0279 .arg(QLocale().standaloneMonthName(d->month, QLocale::LongFormat)) 0280 .arg(QDate(d->year, d->month, 1).year())); 0281 0282 p.end(); 0283 0284 QPainter p2(this); 0285 p2.drawPixmap(cr.x(), cr.y(), pix); 0286 p2.end(); 0287 } 0288 0289 void MonthWidget::mousePressEvent(QMouseEvent* e) 0290 { 0291 int firstSelected = 0; 0292 int lastSelected = 0; 0293 0294 if (e->modifiers() != Qt::ControlModifier) 0295 { 0296 for (int i = 0 ; i < 42 ; ++i) 0297 { 0298 if (d->days[i].selected) 0299 { 0300 if (firstSelected == 0) 0301 { 0302 firstSelected = i; 0303 } 0304 0305 lastSelected =i; 0306 } 0307 0308 d->days[i].selected = false; 0309 } 0310 } 0311 0312 QRect r1(0, d->currh*3, d->currw, d->currh*6); 0313 QRect r2(d->currw, d->currh*3, d->currw*7, d->currh*6); 0314 QRect r3(d->currw, d->currh*2, d->currw*7, d->currh); 0315 0316 // Click on a weekday 0317 0318 if (r3.contains(e->pos())) 0319 { 0320 int j = (e->pos().x() - d->currw)/d->currw; 0321 0322 for (int i = 0 ; i < 6 ; ++i) 0323 { 0324 d->days[i*7+j].selected = !d->days[i*7+j].selected; 0325 } 0326 } 0327 0328 // Click on a week 0329 0330 else if (r1.contains(e->pos())) 0331 { 0332 int j = (e->pos().y() - 3*d->currh)/d->currh; 0333 0334 for (int i = 0 ; i < 7 ; ++i) 0335 { 0336 d->days[j*7+i].selected = !d->days[j*7+i].selected; 0337 } 0338 } 0339 0340 // Click on a day. 0341 0342 else if (r2.contains(e->pos())) 0343 { 0344 int i, j; 0345 i = (e->pos().x() - d->currw)/d->currw; 0346 j = (e->pos().y() - 3*d->currh)/d->currh; 0347 0348 if (e->modifiers() == Qt::ShiftModifier) 0349 { 0350 int endSelection = j*7+i; 0351 0352 if (endSelection > firstSelected) 0353 { 0354 for (int i2 = firstSelected ; i2 <= endSelection ; ++i2) 0355 { 0356 d->days[i2].selected = true; 0357 } 0358 } 0359 else if (endSelection < firstSelected) 0360 { 0361 for (int i2 = lastSelected ; i2 >= endSelection ; --i2) 0362 { 0363 d->days[i2].selected = true; 0364 } 0365 } 0366 } 0367 else 0368 { 0369 d->days[j * 7 + i].selected = !d->days[j * 7 + i].selected; 0370 } 0371 } 0372 0373 QList<QDateTime> filterDays; 0374 0375 for (int i = 0 ; i < 42 ; ++i) 0376 { 0377 if (d->days[i].selected && (d->days[i].day != -1)) 0378 { 0379 filterDays.append(QDateTime(QDate(d->year, d->month, d->days[i].day), QTime())); 0380 } 0381 } 0382 0383 if (d->model) 0384 { 0385 d->model->setDayFilter(filterDays); 0386 } 0387 0388 update(); 0389 } 0390 0391 void MonthWidget::setActive(bool val) 0392 { 0393 if (d->active == val) 0394 { 0395 return; 0396 } 0397 0398 d->active = val; 0399 0400 if (d->active) 0401 { 0402 connectModel(); 0403 triggerUpdateDays(); 0404 } 0405 else 0406 { 0407 QDate date = QDate::currentDate(); 0408 setYearMonth(date.year(), date.month()); 0409 0410 if (d->model) 0411 { 0412 d->model->setDayFilter(QList<QDateTime>()); 0413 disconnect(d->model, nullptr, this, nullptr); 0414 } 0415 } 0416 } 0417 0418 void MonthWidget::setItemModel(ItemFilterModel* model) 0419 { 0420 if (d->model) 0421 { 0422 disconnect(d->model, nullptr, this, nullptr); 0423 } 0424 0425 d->model = model; 0426 connectModel(); 0427 0428 triggerUpdateDays(); 0429 } 0430 0431 void MonthWidget::connectModel() 0432 { 0433 if (d->model) 0434 { 0435 connect(d->model, &ItemFilterModel::destroyed, 0436 this, &MonthWidget::slotModelDestroyed); 0437 0438 connect(d->model, &ItemFilterModel::rowsInserted, 0439 this, &MonthWidget::triggerUpdateDays); 0440 0441 connect(d->model, &ItemFilterModel::rowsRemoved, 0442 this, &MonthWidget::triggerUpdateDays); 0443 0444 connect(d->model, &ItemFilterModel::modelReset, 0445 this, &MonthWidget::triggerUpdateDays); 0446 /* 0447 connect(d->model, SIGNAL(triggerUpdateDays()), 0448 this, SLOT(triggerUpdateDays())); 0449 0450 connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), 0451 this, SLOT(triggerUpdateDays())); 0452 */ 0453 } 0454 } 0455 0456 void MonthWidget::triggerUpdateDays() 0457 { 0458 if (!d->timer->isActive()) 0459 { 0460 d->timer->start(); 0461 } 0462 } 0463 0464 void MonthWidget::resetDayCounts() 0465 { 0466 for (int i = 0 ; i < 42 ; ++i) 0467 { 0468 d->days[i].active = false; 0469 d->days[i].numImages = 0; 0470 } 0471 } 0472 0473 void MonthWidget::updateDays() 0474 { 0475 if (!d->active) 0476 { 0477 return; 0478 } 0479 0480 resetDayCounts(); 0481 0482 if (!d->model) 0483 { 0484 return; 0485 } 0486 0487 const int size = d->model->sourceItemModel()->rowCount(); 0488 0489 for (int i = 0 ; i < size ; ++i) 0490 { 0491 QModelIndex index = d->model->sourceItemModel()->index(i, 0); 0492 0493 if (!index.isValid()) 0494 { 0495 continue; 0496 } 0497 0498 QDateTime dt = d->model->sourceItemModel()->data(index, ItemModel::CreationDateRole).toDateTime(); 0499 0500 if (dt.isNull()) 0501 { 0502 continue; 0503 } 0504 0505 for (int j = 0 ; j < 42 ; ++j) 0506 { 0507 if (d->days[j].day == dt.date().day()) 0508 { 0509 d->days[j].active = true; 0510 d->days[j].numImages++; 0511 break; 0512 } 0513 } 0514 } 0515 0516 update(); 0517 } 0518 0519 void MonthWidget::slotModelDestroyed() 0520 { 0521 d->model = nullptr; 0522 resetDayCounts(); 0523 update(); 0524 } 0525 0526 } // namespace Digikam 0527 0528 #include "moc_monthwidget.cpp"