diff --git a/core/app/date/ddatetable.cpp b/core/app/date/ddatetable.cpp index c4ee7be1f4..5cb94bb333 100644 --- a/core/app/date/ddatetable.cpp +++ b/core/app/date/ddatetable.cpp @@ -1,706 +1,767 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : Date selection table. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ -#include "ddatetable.h" #include "ddatetable_p.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include // Local includes #include "digikam_debug.h" namespace Digikam { DDateTable::DDateTable(const QDate& date, QWidget* const parent) : QWidget(parent), d(new Private(this)) { initWidget(date); } DDateTable::DDateTable(QWidget* const parent) : QWidget(parent), d(new Private(this)) { initWidget(QDate::currentDate()); } DDateTable::~DDateTable() { delete d; } void DDateTable::initWidget(const QDate& date) { d->numWeekRows = 7; setFontSize(10); setFocusPolicy(Qt::StrongFocus); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); initAccels(); setAttribute(Qt::WA_Hover, true); setDate(date); } void DDateTable::initAccels() { QAction* const next = new QAction(this); next->setObjectName(QLatin1String("next")); next->setShortcuts(QKeySequence::keyBindings(QKeySequence::Forward)); next->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(next, SIGNAL(triggered(bool)), d, SLOT(nextMonth())); QAction* const prior = new QAction(this); prior->setObjectName(QLatin1String("prior")); prior->setShortcuts(QKeySequence::keyBindings(QKeySequence::Back)); prior->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(prior, SIGNAL(triggered(bool)), d, SLOT(previousMonth())); QAction* const beginMonth = new QAction(this); beginMonth->setObjectName(QLatin1String("beginMonth")); beginMonth->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToStartOfDocument)); beginMonth->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(beginMonth, SIGNAL(triggered(bool)), d, SLOT(beginningOfMonth())); QAction* const endMonth = new QAction(this); endMonth->setObjectName(QLatin1String("endMonth")); endMonth->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToEndOfDocument)); endMonth->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(endMonth, SIGNAL(triggered(bool)), d, SLOT(endOfMonth())); QAction* const beginWeek = new QAction(this); beginWeek->setObjectName(QLatin1String("beginWeek")); beginWeek->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToStartOfLine)); beginWeek->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(beginWeek, SIGNAL(triggered(bool)), d, SLOT(beginningOfWeek())); QAction* const endWeek = new QAction(this); endWeek->setObjectName(QLatin1String("endWeek")); endWeek->setShortcuts(QKeySequence::keyBindings(QKeySequence::MoveToEndOfLine)); endWeek->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(endWeek, SIGNAL(triggered(bool)), d, SLOT(endOfWeek())); } int DDateTable::posFromDate(const QDate& date) { int initialPosition = date.day(); int offset = (d->weekDayFirstOfMonth - locale().firstDayOfWeek() + d->numDayColumns) % d->numDayColumns; // make sure at least one day of the previous month is visible. // adjust this < 1 if more days should be forced visible: + if (offset < 1) { offset += d->numDayColumns; } return initialPosition + offset; } QDate DDateTable::dateFromPos(int position) { int offset = (d->weekDayFirstOfMonth - locale().firstDayOfWeek() + d->numDayColumns) % d->numDayColumns; // make sure at least one day of the previous month is visible. // adjust this < 1 if more days should be forced visible: + if (offset < 1) { offset += d->numDayColumns; } return QDate(d->date.year(), d->date.month(), 1).addDays(position - offset); } void DDateTable::paintEvent(QPaintEvent *e) { QPainter p(this); const QRect& rectToUpdate = e->rect(); double cellWidth = width() / (double) d->numDayColumns; double cellHeight = height() / (double) d->numWeekRows; int leftCol = (int)std::floor(rectToUpdate.left() / cellWidth); int topRow = (int)std::floor(rectToUpdate.top() / cellHeight); int rightCol = (int)std::ceil(rectToUpdate.right() / cellWidth); int bottomRow = (int)std::ceil(rectToUpdate.bottom() / cellHeight); bottomRow = qMin(bottomRow, d->numWeekRows - 1); rightCol = qMin(rightCol, d->numDayColumns - 1); if (layoutDirection() == Qt::RightToLeft) { p.translate((d->numDayColumns - leftCol - 1) * cellWidth, topRow * cellHeight); } else { p.translate(leftCol * cellWidth, topRow * cellHeight); } - for (int i = leftCol; i <= rightCol; ++i) + for (int i = leftCol ; i <= rightCol ; ++i) { - for (int j = topRow; j <= bottomRow; ++j) + for (int j = topRow ; j <= bottomRow ; ++j) { paintCell(&p, j, i); p.translate(0, cellHeight); } if (layoutDirection() == Qt::RightToLeft) { p.translate(-cellWidth, 0); } else { p.translate(cellWidth, 0); } p.translate(0, -cellHeight * (bottomRow - topRow + 1)); } } void DDateTable::paintCell(QPainter* painter, int row, int col) { double w = (width() / (double) d->numDayColumns) - 1; double h = (height() / (double) d->numWeekRows) - 1; QRectF cell = QRectF(0, 0, w, h); QString cellText; QPen pen; QColor cellBackgroundColor, cellTextColor; QFont cellFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); bool workingDay = false; int cellWeekDay, pos; - //Calculate the position of the cell in the grid + // Calculate the position of the cell in the grid + pos = d->numDayColumns * (row - 1) + col; - //Calculate what day of the week the cell is - if (col + locale().firstDayOfWeek() <= d->numDayColumns) + // Calculate what day of the week the cell is + + if ((col + locale().firstDayOfWeek()) <= d->numDayColumns) { cellWeekDay = col + locale().firstDayOfWeek(); } else { cellWeekDay = col + locale().firstDayOfWeek() - d->numDayColumns; } - //FIXME This is wrong if the widget is not using the global! - //See if cell day is normally a working day + // FIXME This is wrong if the widget is not using the global! + // See if cell day is normally a working day + if (locale().weekdays().first() <= locale().weekdays().last()) { - if (cellWeekDay >= locale().weekdays().first() && - cellWeekDay <= locale().weekdays().last()) + if ((cellWeekDay >= locale().weekdays().first()) && + (cellWeekDay <= locale().weekdays().last())) { workingDay = true; } } else { - if (cellWeekDay >= locale().weekdays().first() || - cellWeekDay <= locale().weekdays().last()) + if ((cellWeekDay >= locale().weekdays().first()) || + (cellWeekDay <= locale().weekdays().last())) { workingDay = true; } } if (row == 0) { - //We are drawing a header cell + // We are drawing a header cell + + // If not a normal working day, then use "do not work today" color - //If not a normal working day, then use "do not work today" color if (workingDay) { cellTextColor = palette().color(QPalette::WindowText); } else { cellTextColor = Qt::darkRed; } cellBackgroundColor = palette().color(QPalette::Window); - //Set the text to the short day name and bold it + // Set the text to the short day name and bold it + cellFont.setBold(true); cellText = locale().standaloneDayName(cellWeekDay, QLocale::ShortFormat); - } else { - //We are drawing a day cell + // We are drawing a day cell + + // Calculate the date the cell represents - //Calculate the date the cell represents QDate cellDate = dateFromPos(pos); bool validDay = cellDate.isValid(); // Draw the day number in the cell, if the date is not valid then we don't want to show it + if (validDay) { cellText = QString::number(cellDate.day()); } else { cellText = QLatin1String(""); } - if (! validDay || cellDate.month() != d->date.month()) + if (! validDay || (cellDate.month() != d->date.month())) { // we are either // ° painting an invalid day // ° painting a day of the previous month or // ° painting a day of the following month or + cellBackgroundColor = palette().color(backgroundRole()); cellTextColor = palette().color(QPalette::Disabled, QPalette::Text); } else { - //Paint a day of the current month + // Paint a day of the current month // Background Colour priorities will be (high-to-low): // * Selected Day Background Colour // * Customized Day Background Colour // * Normal Day Background Colour // Background Shape priorities will be (high-to-low): // * Customized Day Shape // * Normal Day Shape // Text Colour priorities will be (high-to-low): // * Customized Day Colour // * Day of Pray Colour (Red letter) // * Selected Day Colour // * Normal Day Colour - //Determine various characteristics of the cell date + // Determine various characteristics of the cell date + bool selectedDay = (cellDate == date()); bool currentDay = (cellDate == QDate::currentDate()); bool dayOfPray = (cellDate.dayOfWeek() == Qt::Sunday); + // TODO: Uncomment if QLocale ever gets the feature... - //bool dayOfPray = ( cellDate.dayOfWeek() == locale().dayOfPray() ); +/* + bool dayOfPray = ( cellDate.dayOfWeek() == locale().dayOfPray() ); +*/ bool customDay = (d->useCustomColors && d->customPaintingModes.contains(cellDate.toJulianDay())); - //Default values for a normal cell + // Default values for a normal cell + cellBackgroundColor = palette().color(backgroundRole()); cellTextColor = palette().color(foregroundRole()); // If we are drawing the current date, then draw it bold and active + if (currentDay) { cellFont.setBold(true); cellTextColor = palette().color(QPalette::LinkVisited); } // if we are drawing the day cell currently selected in the table + if (selectedDay) { // set the background to highlighted + cellBackgroundColor = palette().color(QPalette::Highlight); cellTextColor = palette().color(QPalette::HighlightedText); } - //If custom colors or shape are required for this date + // If custom colors or shape are required for this date + if (customDay) { Private::DatePaintingMode mode = d->customPaintingModes[cellDate.toJulianDay()]; if (mode.bgMode != NoBgMode) { if (!selectedDay) { cellBackgroundColor = mode.bgColor; } } cellTextColor = mode.fgColor; } - //If the cell day is the day of religious observance, then always color text red unless Custom overrides + // If the cell day is the day of religious observance, then always color text red unless Custom overrides + if (! customDay && dayOfPray) { cellTextColor = Qt::darkRed; } - } } - //Draw the background - if (row == 0) + // Draw the background + + if (row == 0) { painter->setPen(cellBackgroundColor); painter->setBrush(cellBackgroundColor); painter->drawRect(cell); } - else if (cellBackgroundColor != palette().color(backgroundRole()) || pos == d->hoveredPos) + else if ((cellBackgroundColor != palette().color(backgroundRole())) || (pos == d->hoveredPos)) { QStyleOptionViewItem opt; opt.initFrom(this); opt.rect = cell.toRect(); if (cellBackgroundColor != palette().color(backgroundRole())) { opt.palette.setBrush(QPalette::Highlight, cellBackgroundColor); opt.state |= QStyle::State_Selected; } - if (pos == d->hoveredPos && opt.state & QStyle::State_Enabled) + if ((pos == d->hoveredPos) && (opt.state & QStyle::State_Enabled)) { opt.state |= QStyle::State_MouseOver; } else { opt.state &= ~QStyle::State_MouseOver; } opt.showDecorationSelected = true; opt.viewItemPosition = QStyleOptionViewItem::OnlyOne; style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, this); } - //Draw the text + // Draw the text + painter->setPen(cellTextColor); painter->setFont(cellFont); painter->drawText(cell, Qt::AlignCenter, cellText, &cell); - //Draw the base line + // Draw the base line + if (row == 0) { painter->setPen(palette().color(foregroundRole())); painter->drawLine(QPointF(0, h), QPointF(w, h)); } // If the day cell we just drew is bigger than the current max cell sizes, // then adjust the max to the current cell + if (cell.width() > d->maxCell.width()) { d->maxCell.setWidth(cell.width()); } if (cell.height() > d->maxCell.height()) { d->maxCell.setHeight(cell.height()); } } void DDateTable::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Up: // setDate does validity checking for us + setDate(d->date.addDays(- d->numDayColumns)); break; + case Qt::Key_Down: // setDate does validity checking for us + setDate(d->date.addDays(d->numDayColumns)); break; + case Qt::Key_Left: // setDate does validity checking for us + setDate(d->date.addDays(-1)); break; + case Qt::Key_Right: // setDate does validity checking for us + setDate(d->date.addDays(1)); break; + case Qt::Key_Minus: // setDate does validity checking for us + setDate(d->date.addDays(-1)); break; + case Qt::Key_Plus: // setDate does validity checking for us + setDate(d->date.addDays(1)); break; + case Qt::Key_N: // setDate does validity checking for us + setDate(QDate::currentDate()); break; + case Qt::Key_Return: case Qt::Key_Enter: emit tableClicked(); break; + case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_Meta: case Qt::Key_Shift: // Don't beep for modifiers + break; + default: + if (!e->modifiers()) - { // hm + { + // hm + QApplication::beep(); } } } void DDateTable::setFontSize(int size) { QFontMetricsF metrics(fontMetrics()); QRectF rect; // ----- store rectangles: + d->fontsize = size; // ----- find largest day name: + d->maxCell.setWidth(0); d->maxCell.setHeight(0); - for (int weekday = 1; weekday <= 7; ++weekday) + for (int weekday = 1 ; weekday <= 7 ; ++weekday) { rect = metrics.boundingRect(locale().standaloneDayName(weekday, QLocale::ShortFormat)); d->maxCell.setWidth(qMax(d->maxCell.width(), rect.width())); d->maxCell.setHeight(qMax(d->maxCell.height(), rect.height())); } // ----- compare with a real wide number and add some space: + rect = metrics.boundingRect(QLatin1String("88")); d->maxCell.setWidth(qMax(d->maxCell.width() + 2, rect.width())); d->maxCell.setHeight(qMax(d->maxCell.height() + 4, rect.height())); } void DDateTable::wheelEvent(QWheelEvent *e) { setDate(d->date.addMonths(-(int)(e->angleDelta().y() / 120))); e->accept(); } bool DDateTable::event(QEvent *ev) { switch (ev->type()) { case QEvent::HoverMove: { - QHoverEvent *e = static_cast(ev); - const int row = e->pos().y() * d->numWeekRows / height(); + QHoverEvent* const e = static_cast(ev); + const int row = e->pos().y() * d->numWeekRows / height(); int col; if (layoutDirection() == Qt::RightToLeft) { col = d->numDayColumns - (e->pos().x() * d->numDayColumns / width()) - 1; } else { col = e->pos().x() * d->numDayColumns / width(); } const int pos = row < 1 ? -1 : (d->numDayColumns * (row - 1)) + col; if (pos != d->hoveredPos) { d->hoveredPos = pos; update(); } + break; } case QEvent::HoverLeave: + { if (d->hoveredPos != -1) { d->hoveredPos = -1; update(); } + break; + } default: + { break; + } } return QWidget::event(ev); } void DDateTable::mousePressEvent(QMouseEvent *e) { if (e->type() != QEvent::MouseButtonPress) { - // the KDatePicker only reacts on mouse press events: + // the Date Picker only reacts on mouse press events: + return; } if (!isEnabled()) { QApplication::beep(); + return; } int row, col, pos; QPoint mouseCoord = e->pos(); - row = mouseCoord.y() * d->numWeekRows / height(); + row = mouseCoord.y() * d->numWeekRows / height(); if (layoutDirection() == Qt::RightToLeft) { col = d->numDayColumns - (mouseCoord.x() * d->numDayColumns / width()) - 1; } else { col = mouseCoord.x() * d->numDayColumns / width(); } - if (row < 1 || col < 0) - { // the user clicked on the frame of the table + if ((row < 1) || (col < 0)) + { + // the user clicked on the frame of the table + return; } // Rows and columns are zero indexed. The (row - 1) below is to avoid counting // the row with the days of the week in the calculation. // new position and date + pos = (d->numDayColumns * (row - 1)) + col; QDate clickedDate = dateFromPos(pos); // set the new date. If it is in the previous or next month, the month will // automatically be changed, no need to do that manually... // validity checking done inside setDate + setDate(clickedDate); // This could be optimized to only call update over the regions // of old and new cell, but 99% of times there is also a call to // setDate that already calls update() so no need to optimize that // much here + update(); emit tableClicked(); - if (e->button() == Qt::RightButton && d->popupMenuEnabled) + if ((e->button() == Qt::RightButton) && d->popupMenuEnabled) { QMenu* const menu = new QMenu(); menu->addSection(locale().toString(d->date)); emit aboutToShowContextMenu(menu, clickedDate); menu->popup(e->globalPos()); } } bool DDateTable::setDate(const QDate& toDate) { if (!toDate.isValid()) { return false; } if (toDate == date()) { return true; } QDate oldDate = date(); d->setDate(toDate); emit dateChanged(date(), oldDate); emit dateChanged(date()); update(); return true; } const QDate& DDateTable::date() const { return d->date; } void DDateTable::focusInEvent(QFocusEvent* e) { QWidget::focusInEvent(e); } void DDateTable::focusOutEvent(QFocusEvent* e) { QWidget::focusOutEvent(e); } QSize DDateTable::sizeHint() const { - if (d->maxCell.height() > 0 && d->maxCell.width() > 0) + if ((d->maxCell.height() > 0) && (d->maxCell.width() > 0)) { - return QSize( qRound(d->maxCell.width() * d->numDayColumns), - (qRound(d->maxCell.height() + 2) * d->numWeekRows)); + return QSize( + qRound(d->maxCell.width() * d->numDayColumns), + (qRound(d->maxCell.height() + 2) * d->numWeekRows) + ); } else { //qCDebug(DIGIKAM_GENERAL_LOG) << "DDateTable::sizeHint: obscure failure - " << endl; + return QSize(-1, -1); } } void DDateTable::setPopupMenuEnabled(bool enable) { d->popupMenuEnabled = enable; } bool DDateTable::popupMenuEnabled() const { return d->popupMenuEnabled; } void DDateTable::setCustomDatePainting(const QDate& date, const QColor& fgColor, BackgroundMode bgMode, const QColor& bgColor) { if (!fgColor.isValid()) { unsetCustomDatePainting(date); return; } Private::DatePaintingMode mode; - mode.bgMode = bgMode; - mode.fgColor = fgColor; - mode.bgColor = bgColor; + mode.bgMode = bgMode; + mode.fgColor = fgColor; + mode.bgColor = bgColor; d->customPaintingModes.insert(date.toJulianDay(), mode); d->useCustomColors = true; update(); } void DDateTable::unsetCustomDatePainting(const QDate& date) { d->customPaintingModes.remove(date.toJulianDay()); if (d->customPaintingModes.isEmpty()) { d->useCustomColors = false; } + update(); } } // namespace Digikam diff --git a/core/app/date/ddatetable_p.cpp b/core/app/date/ddatetable_p.cpp index aab0945004..5a295255f0 100644 --- a/core/app/date/ddatetable_p.cpp +++ b/core/app/date/ddatetable_p.cpp @@ -1,111 +1,117 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : Date selection table. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ddatetable_p.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include namespace Digikam { DDateTable::Private::Private(DDateTable* const qq) : QObject(qq), - q(qq) + q(qq), + weekDayFirstOfMonth(0), + numDaysThisMonth(0), + numWeekRows(0), + numDayColumns(0), + fontsize(0), + popupMenuEnabled(false), + useCustomColors(false), + hoveredPos(-1) { - weekDayFirstOfMonth = 0; - numDaysThisMonth = 0; - numWeekRows = 0; - numDayColumns = 0; - fontsize = 0; - popupMenuEnabled = false; - useCustomColors = false; - hoveredPos = -1; setDate(QDate::currentDate()); } DDateTable::Private::~Private() { } void DDateTable::Private::nextMonth() { // setDate does validity checking for us + q->setDate(date.addMonths(1)); } void DDateTable::Private::previousMonth() { // setDate does validity checking for us + q->setDate(date.addMonths(-1)); } void DDateTable::Private::beginningOfMonth() { // setDate does validity checking for us + q->setDate(QDate(date.year(), date.month(), 1)); } void DDateTable::Private::endOfMonth() { // setDate does validity checking for us + q->setDate(QDate(date.year(), date.month() + 1, 0)); } void DDateTable::Private::beginningOfWeek() { // setDate does validity checking for us + q->setDate(date.addDays(1 - date.dayOfWeek())); } void DDateTable::Private::endOfWeek() { // setDate does validity checking for us + q->setDate(date.addDays(7 - date.dayOfWeek())); } void DDateTable::Private::setDate(const QDate& dt) { date = dt; weekDayFirstOfMonth = QDate(date.year(), date.month(), 1).dayOfWeek(); numDaysThisMonth = date.daysInMonth(); numDayColumns = 7; } } // namespace Digikam diff --git a/core/app/date/ddatetable_p.h b/core/app/date/ddatetable_p.h index cf72a57f73..ffa2dbbe49 100644 --- a/core/app/date/ddatetable_p.h +++ b/core/app/date/ddatetable_p.h @@ -1,124 +1,126 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : Date selection table. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DDATE_TABLE_PRIVATE_H #define DIGIKAM_DDATE_TABLE_PRIVATE_H #include "ddatetable.h" // C++ includes #include // Qt includes #include #include #include #include #include namespace Digikam { class Q_DECL_HIDDEN DDateTable::Private : public QObject { Q_OBJECT public: - struct DatePaintingMode + class Q_DECL_HIDDEN DatePaintingMode { + public: + QColor fgColor; QColor bgColor; BackgroundMode bgMode; }; public: explicit Private(DDateTable* const qq); ~Private(); public Q_SLOTS: void setDate(const QDate&); void nextMonth(); void previousMonth(); void beginningOfMonth(); void endOfMonth(); void beginningOfWeek(); void endOfWeek(); public: DDateTable* q; /** - * The currently selected date. - */ + * The currently selected date. + */ QDate date; /** * The weekday number of the first day in the month [1..daysInWeek()]. */ int weekDayFirstOfMonth; /** * The number of days in the current month. */ int numDaysThisMonth; /** * Save the size of the largest used cell content. */ QRectF maxCell; /** * How many week rows we are to draw. */ int numWeekRows; /** * How many day columns we are to draw, i.e. days in a week. */ int numDayColumns; /** * The font size of the displayed text. */ int fontsize; bool popupMenuEnabled; bool useCustomColors; QHash customPaintingModes; int hoveredPos; }; } // namespace Digikam #endif // DIGIKAM_DDATE_TABLE_PRIVATE_H diff --git a/core/app/date/dpopupframe.cpp b/core/app/date/dpopupframe.cpp index a85720676a..4f06ada9b8 100644 --- a/core/app/date/dpopupframe.cpp +++ b/core/app/date/dpopupframe.cpp @@ -1,240 +1,246 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 1997-04-21 * Description : Frame with popup menu behavior. * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 1997 by Tim D. Gilman * Copyright (C) 1998-2001 by Mirko Boehm * Copyright (C) 2007 by John Layt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dpopupframe.h" // Qt includes #include #include #include #include #include namespace Digikam { class Q_DECL_HIDDEN DPopupFrame::Private { public: explicit Private(DPopupFrame* const qq); ~Private(); public: DPopupFrame* q; /** * The result. It is returned from exec() when the popup window closes. */ int result; /** * The only subwidget that uses the whole dialog window. */ QWidget* main; class OutsideClickCatcher; OutsideClickCatcher* outsideClickCatcher; }; class Q_DECL_HIDDEN DPopupFrame::Private::OutsideClickCatcher : public QObject { public: explicit OutsideClickCatcher(QObject* const parent = nullptr) : QObject(parent), m_popup(nullptr) { } ~OutsideClickCatcher() { } void setPopupFrame(DPopupFrame* const popup) { m_popup = popup; popup->installEventFilter(this); } bool eventFilter(QObject* object, QEvent* event) Q_DECL_OVERRIDE { Q_UNUSED(object); // To catch outside clicks, it is sufficient to check for // hide events on Qt::Popup type widgets + if (event->type() == QEvent::Hide && m_popup) { // do not set d->result here, because the popup // hides itself after leaving the event loop. + emit m_popup->leaveModality(); } return false; } public: DPopupFrame* m_popup; }; DPopupFrame::Private::Private(DPopupFrame* const qq) : q(qq), result(0), // rejected main(nullptr), outsideClickCatcher(new OutsideClickCatcher) { outsideClickCatcher->setPopupFrame(q); } DPopupFrame::Private::~Private() { delete outsideClickCatcher; } DPopupFrame::DPopupFrame(QWidget* const parent) : QFrame(parent, Qt::Popup), d(new Private(this)) { setFrameStyle(QFrame::Box | QFrame::Raised); setMidLineWidth(2); } DPopupFrame::~DPopupFrame() { delete d; } void DPopupFrame::keyPressEvent(QKeyEvent* e) { if (e->key() == Qt::Key_Escape) { d->result = 0; // rejected emit leaveModality(); } } void DPopupFrame::hideEvent(QHideEvent *e) { QFrame::hideEvent(e); } void DPopupFrame::close(int r) { d->result = r; emit leaveModality(); } void DPopupFrame::setMainWidget(QWidget* const m) { d->main = m; if (d->main) { resize(d->main->width() + 2 * frameWidth(), d->main->height() + 2 * frameWidth()); } } void DPopupFrame::resizeEvent(QResizeEvent* e) { Q_UNUSED(e); if (d->main) { d->main->setGeometry(frameWidth(), frameWidth(), width() - 2 * frameWidth(), height() - 2 * frameWidth()); } } void DPopupFrame::popup(const QPoint& p) { // Make sure the whole popup is visible. QScreen* screen = qApp->primaryScreen(); if (QWidget* const widget = nativeParentWidget()) { if (QWindow* const window = widget->windowHandle()) + { screen = window->screen(); + } } QRect desktopGeometry = screen->geometry(); int x = p.x(); int y = p.y(); int w = width(); int h = height(); - if (x + w > desktopGeometry.x() + desktopGeometry.width()) + if ((x + w) > (desktopGeometry.x() + desktopGeometry.width())) { x = desktopGeometry.width() - w; } - if (y + h > desktopGeometry.y() + desktopGeometry.height()) + if ((y + h) > (desktopGeometry.y() + desktopGeometry.height())) { y = desktopGeometry.height() - h; } if (x < desktopGeometry.x()) { x = 0; } if (y < desktopGeometry.y()) { y = 0; } // Pop the thingy up. + move(x, y); show(); d->main->setFocus(); } int DPopupFrame::exec(const QPoint& p) { popup(p); repaint(); d->result = 0; // rejected QEventLoop eventLoop; connect(this, &DPopupFrame::leaveModality, &eventLoop, &QEventLoop::quit); eventLoop.exec(); hide(); + return d->result; } int DPopupFrame::exec(int x, int y) { return exec(QPoint(x, y)); } } // namespace Digikam diff --git a/core/app/date/monthwidget.cpp b/core/app/date/monthwidget.cpp index 28f602621e..f470275a1e 100644 --- a/core/app/date/monthwidget.cpp +++ b/core/app/date/monthwidget.cpp @@ -1,520 +1,532 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-02 * Description : a widget to perform month selection. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2011 by Andi Clemens * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "monthwidget.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include // Local includes #include "itemfiltermodel.h" #include "itemmodel.h" namespace Digikam { class Q_DECL_HIDDEN MonthWidget::Private { public: - struct Month + class Q_DECL_HIDDEN Month { + public: + Month() + : active(false), + selected(false), + day(0), + numImages(0) { - active = false; - selected = false; - day = 0; - numImages = 0; } bool active; bool selected; int day; int numImages; }; public: explicit Private() : active(true), model(nullptr), timer(nullptr), year(0), month(0), width(0), height(0), currw(0), currh(0) { } - bool active; + bool active; ItemFilterModel* model; - QTimer* timer; + QTimer* timer; - int year; - int month; - int width; - int height; - int currw; - int currh; + int year; + int month; + int width; + int height; + int currw; + int currh; - struct Month days[42]; + Month days[42]; }; MonthWidget::MonthWidget(QWidget* const parent) : QWidget(parent), d(new Private) { init(); QDate date = QDate::currentDate(); setYearMonth(date.year(), date.month()); setActive(false); - d->timer = new QTimer(this); + d->timer = new QTimer(this); d->timer->setSingleShot(true); d->timer->setInterval(150); connect(d->timer, &QTimer::timeout, this, &MonthWidget::updateDays); } MonthWidget::~MonthWidget() { delete d; } void MonthWidget::init() { QFont fn(font()); fn.setBold(true); fn.setPointSize(fn.pointSize()+1); QFontMetrics fm(fn); QRect r(fm.boundingRect(QLatin1String("XX"))); r.setWidth(r.width() + 2); r.setHeight(r.height() + 4); d->width = r.width(); d->height = r.height(); setMinimumWidth(d->width * 8); setMinimumHeight(d->height * 9); } void MonthWidget::setYearMonth(int year, int month) { d->year = year; d->month = month; for (int i = 0 ; i < 42 ; ++i) { d->days[i].active = false; d->days[i].selected = false; d->days[i].day = -1; d->days[i].numImages = 0; } QDate date(year, month, 1); int s = date.dayOfWeek(); for (int i = s ; i < (s+date.daysInMonth()) ; ++i) { d->days[i-1].day = i-s+1; } update(); } QSize MonthWidget::sizeHint() const { return QSize(d->width * 8, d->height * 9); } void MonthWidget::resizeEvent(QResizeEvent* e) { QWidget::resizeEvent(e); d->currw = contentsRect().width()/8; d->currh = contentsRect().height()/9; } void MonthWidget::paintEvent(QPaintEvent*) { QRect cr(contentsRect()); QPixmap pix(cr.width(), cr.height()); QFont fnBold(font()); QFont fnOrig(font()); fnBold.setBold(true); fnOrig.setBold(false); QPainter p(&pix); p.fillRect(0, 0, cr.width(), cr.height(), palette().color(QPalette::Window)); QRect r(0, 0, d->currw, d->currh); QRect rsmall; int sx, sy; int index = 0; bool weekvisible; for (int j = 3 ; j < 9 ; ++j) { sy = d->currh * j; weekvisible = false; for (int i = 1 ; i < 8 ; ++i) { sx = d->currw * i; r.moveTopLeft(QPoint(sx,sy)); rsmall = QRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); if (d->days[index].day != -1) { if (d->days[index].selected) { p.fillRect(r, palette().color(QPalette::Highlight)); p.setPen(palette().color(QPalette::HighlightedText)); if (d->days[index].active) { p.setFont(fnBold); } else { p.setFont(fnOrig); } } else { if (d->days[index].active) { p.setPen(palette().color(QPalette::Text)); p.setFont(fnBold); } else { p.setPen(palette().color(QPalette::Mid)); p.setFont(fnOrig); } } p.drawText(rsmall, Qt::AlignVCenter|Qt::AlignHCenter, QString::number(d->days[index].day)); if (!weekvisible) { - int weeknr = QDate(d->year, d->month, d->days[index].day).weekNumber(); + int weeknr = QDate(d->year, d->month, d->days[index].day).weekNumber(); p.setPen(d->active ? Qt::black : Qt::gray); p.setFont(fnBold); p.fillRect(1, sy, d->currw-1, d->currh-1, QColor(210, 210, 210)); p.drawText(1, sy, d->currw-1, d->currh-1, Qt::AlignVCenter|Qt::AlignHCenter, QString::number(weeknr)); weekvisible = true; } } ++index; } } p.setPen(d->active ? Qt::black : Qt::gray); p.setFont(fnBold); - sy = 2*d->currh; + sy = 2 * d->currh; for (int i = 1 ; i < 8 ; ++i) { sx = d->currw * i; r.moveTopLeft(QPoint(sx+1,sy+1)); rsmall = r; rsmall.setWidth(r.width() - 2); rsmall.setHeight(r.height() - 2); p.drawText(rsmall, Qt::AlignVCenter|Qt::AlignHCenter, QLocale().standaloneDayName(i, QLocale::ShortFormat).remove(2, 1)); ++index; } r = QRect(0, 0, cr.width(), 2*d->currh); fnBold.setPointSize(fnBold.pointSize()+2); p.setFont(fnBold); p.drawText(r, Qt::AlignCenter, QString::fromUtf8("%1 %2") .arg(QLocale().standaloneMonthName(d->month, QLocale::LongFormat)) .arg(QDate(d->year, d->month, 1).year())); p.end(); QPainter p2(this); p2.drawPixmap(cr.x(), cr.y(), pix); p2.end(); } void MonthWidget::mousePressEvent(QMouseEvent* e) { - int firstSelected = 0, lastSelected = 0; + int firstSelected = 0; + int lastSelected = 0; if (e->modifiers() != Qt::ControlModifier) { for (int i = 0 ; i < 42 ; ++i) { if (d->days[i].selected) { if (firstSelected==0) { firstSelected = i; } lastSelected =i; } d->days[i].selected = false; } } QRect r1(0, d->currh*3, d->currw, d->currh*6); QRect r2(d->currw, d->currh*3, d->currw*7, d->currh*6); QRect r3(d->currw, d->currh*2, d->currw*7, d->currh); // Click on a weekday - if (r3.contains(e->pos())) + + if (r3.contains(e->pos())) { int j = (e->pos().x() - d->currw)/d->currw; for (int i = 0 ; i < 6 ; ++i) { d->days[i*7+j].selected = !d->days[i*7+j].selected; } } + // Click on a week + else if (r1.contains(e->pos())) { int j = (e->pos().y() - 3*d->currh)/d->currh; for (int i = 0 ; i < 7 ; ++i) { d->days[j*7+i].selected = !d->days[j*7+i].selected; } } + // Click on a day. + else if (r2.contains(e->pos())) { int i, j; i = (e->pos().x() - d->currw)/d->currw; j = (e->pos().y() - 3*d->currh)/d->currh; if (e->modifiers() == Qt::ShiftModifier) { int endSelection = j*7+i; if (endSelection > firstSelected) + { for (int i2=firstSelected ; i2 <= endSelection ; ++i2) { d->days[i2].selected = true; } + } else if (endSelection < firstSelected) + { for (int i2=lastSelected ; i2 >= endSelection ; --i2) { d->days[i2].selected = true; } + } } else { - d->days[j*7+i].selected = !d->days[j*7+i].selected; + d->days[j * 7 + i].selected = !d->days[j * 7 + i].selected; } } QList filterDays; for (int i = 0 ; i < 42 ; ++i) { - if (d->days[i].selected && d->days[i].day != -1) + if (d->days[i].selected && (d->days[i].day != -1)) { filterDays.append(QDateTime(QDate(d->year, d->month, d->days[i].day), QTime())); } } if (d->model) { d->model->setDayFilter(filterDays); } update(); } void MonthWidget::setActive(bool val) { if (d->active == val) { return; } d->active = val; if (d->active) { connectModel(); triggerUpdateDays(); } else { QDate date = QDate::currentDate(); setYearMonth(date.year(), date.month()); if (d->model) { d->model->setDayFilter(QList()); disconnect(d->model, nullptr, this, nullptr); } } } void MonthWidget::setItemModel(ItemFilterModel* model) { if (d->model) { disconnect(d->model, nullptr, this, nullptr); } d->model = model; connectModel(); triggerUpdateDays(); } void MonthWidget::connectModel() { if (d->model) { connect(d->model, &ItemFilterModel::destroyed, this, &MonthWidget::slotModelDestroyed); connect(d->model, &ItemFilterModel::rowsInserted, this, &MonthWidget::triggerUpdateDays); connect(d->model, &ItemFilterModel::rowsRemoved, this, &MonthWidget::triggerUpdateDays); connect(d->model, &ItemFilterModel::modelReset, this, &MonthWidget::triggerUpdateDays); /* connect(d->model, SIGNAL(triggerUpdateDays()), this, SLOT(triggerUpdateDays())); connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(triggerUpdateDays())); */ } } void MonthWidget::triggerUpdateDays() { if (!d->timer->isActive()) { d->timer->start(); } } void MonthWidget::resetDayCounts() { for (int i = 0 ; i < 42 ; ++i) { d->days[i].active = false; d->days[i].numImages = 0; } } void MonthWidget::updateDays() { if (!d->active) { return; } resetDayCounts(); if (!d->model) { return; } const int size = d->model->rowCount(); for (int i = 0 ; i < size ; ++i) { QModelIndex index = d->model->index(i, 0); if (!index.isValid()) { continue; } QDateTime dt = d->model->data(index, ItemModel::CreationDateRole).toDateTime(); if (dt.isNull()) { continue; } for (int j = 0 ; j < 42 ; ++j) { if (d->days[j].day == dt.date().day()) { d->days[j].active = true; d->days[j].numImages++; break; } } } update(); } void MonthWidget::slotModelDestroyed() { d->model = nullptr; resetDayCounts(); update(); } } // namespace Digikam diff --git a/core/app/date/monthwidget.h b/core/app/date/monthwidget.h index b79008ccbd..566f31e775 100644 --- a/core/app/date/monthwidget.h +++ b/core/app/date/monthwidget.h @@ -1,89 +1,89 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-02 * Description : a widget to perform month selection. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2006-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_MONTH_WIDGET_H #define DIGIKAM_MONTH_WIDGET_H // Qt includes #include // Local includes #include "iteminfo.h" class QResizeEvent; class QPaintEvent; class QMouseEvent; namespace Digikam { class ItemFilterModel; class MonthWidget : public QWidget { Q_OBJECT public: explicit MonthWidget(QWidget* const parent); ~MonthWidget(); void setItemModel(ItemFilterModel* const model); void setYearMonth(int year, int month); - QSize sizeHint() const override; + QSize sizeHint() const override; void setActive(bool val); protected: - void resizeEvent(QResizeEvent* e) override; - void paintEvent(QPaintEvent*) override; - void mousePressEvent(QMouseEvent* e) override; + void resizeEvent(QResizeEvent* e) override; + void paintEvent(QPaintEvent*) override; + void mousePressEvent(QMouseEvent* e) override; private: void init(); private Q_SLOTS: void triggerUpdateDays(); void updateDays(); void slotModelDestroyed(); private: void resetDayCounts(); void connectModel(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_MONTH_WIDGET_H