File indexing completed on 2024-05-12 05:21:23

0001 /*
0002   This file is part of KOrganizer.
0003 
0004   SPDX-FileCopyrightText: 2001 Eitzenberger Thomas <thomas.eitzenberger@siemens.at>
0005   SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
0006   SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0007 
0008   SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0009 */
0010 
0011 #pragma once
0012 
0013 #include <Akonadi/CollectionCalendar>
0014 
0015 #include <KCalendarCore/IncidenceBase> //for KCalendarCore::DateList typedef
0016 
0017 #include <QDate>
0018 #include <QFrame>
0019 
0020 /**
0021  *  Replacement for kdpdatebuton.cpp that used 42 widgets for the day
0022  *  matrix to be displayed. Cornelius thought this was a waste of memory
0023  *  and a lot of overhead. In addition the selection was not very intuitive
0024  *  so I decided to rewrite it using a QFrame that draws the labels and
0025  *  allows for dragging selection while maintaining nearly full compatibility
0026  *  in behavior with its predecessor.
0027  *
0028  *  The following functionality has been changed:
0029  *
0030  *  o when shifting events in the agenda view from one day to another
0031  *    the day matrix is updated now
0032  *
0033  *  o dragging an event to the matrix will MOVE not COPY the event to the
0034  *    new date.
0035  *
0036  *  o no support for Ctrl+click to create groups of dates
0037  *    (This has not really been supported in the predecessor.
0038  *    It was not very intuitive nor was it user friendly.)
0039  *    This feature has been replaced with dragging a selection on the matrix.
0040  *    The matrix will automatically choose the appropriate selection (e.g. you
0041  *    are not any longer able to select two distinct groups of date selections
0042  *    as in the old class)
0043  *
0044  *  o now that you can select more than a week it can happen that not all
0045  *    selected days are displayed in the matrix. However this is preferred
0046  *    to the alternative which would mean to adjust the selection and leave
0047  *    some days undisplayed while scrolling through the months
0048  *
0049  *  @short day matrix widget of the KDateNavigator
0050  *
0051  *  @author Eitzenberger Thomas
0052  */
0053 class KODayMatrix : public QFrame, public KCalendarCore::Calendar::CalendarObserver
0054 {
0055     Q_OBJECT
0056 public:
0057     /** constructor to create a day matrix widget.
0058      *
0059      *  @param parent widget that is the parent of the day matrix.
0060      *  Normally this should be a KDateNavigator
0061      */
0062     explicit KODayMatrix(QWidget *parent);
0063 
0064     /** destructor that deallocates all dynamically allocated private members.
0065      */
0066     ~KODayMatrix() override;
0067 
0068     /** returns the first and last date of the 6*7 matrix that displays @p month
0069      * @param month The month we want to get matrix boundaries
0070      */
0071     [[nodiscard]] static QPair<QDate, QDate> matrixLimits(QDate month);
0072 
0073     /**
0074       Associate a calendar with this day matrix. If there is a calendar, the
0075       day matrix will accept drops and days with events will be highlighted.
0076     */
0077     void addCalendar(const Akonadi::CollectionCalendar::Ptr &calendar);
0078     void removeCalendar(const Akonadi::CollectionCalendar::Ptr &calendar);
0079 
0080     /** updates the day matrix to start with the given date. Does all the
0081      *  necessary checks for holidays or events on a day and stores them
0082      *  for display later on.
0083      *  Does NOT update the view visually. Call repaint() for this.
0084      *
0085      *  @param actdate recalculates the day matrix to show NUMDAYS starting
0086      *  from this date.
0087      */
0088     void updateView(QDate actdate);
0089 
0090     /**
0091       Update incidence states of dates. Depending of the preferences days with
0092       incidences are highlighted in some way.
0093     */
0094     void updateIncidences();
0095 
0096     /**
0097      * Returns the QDate object associated with day indexed by the supplied
0098      * offset.
0099      */
0100     const QDate &getDate(int offset) const;
0101 
0102     /**
0103      * Returns the official name of this holy day or 0 if there is no label
0104      * for this day.
0105      */
0106     QString getHolidayLabel(int offset) const;
0107 
0108     /**
0109      * Adds all actual selected days from mSelStart to mSelEnd to the supplied
0110      * DateList.
0111      */
0112     void addSelectedDaysTo(KCalendarCore::DateList &);
0113 
0114     /**
0115      * Sets the actual to be displayed selection in the day matrix starting
0116      * from start and ending with end. Theview must be manually updated by
0117      * calling repaint. (?)
0118      * @param start start of the new selection
0119      * @param end end date of the new selection
0120      */
0121     void setSelectedDaysFrom(QDate start, QDate end);
0122 
0123     /**
0124       Clear all selections.
0125     */
0126     void clearSelection();
0127 
0128     /** Is today visible in the view? Keep this in sync with
0129      * the values today (below) can take.
0130      */
0131     [[nodiscard]] bool isTodayVisible() const
0132     {
0133         return mToday >= 0;
0134     }
0135 
0136     /**
0137      * If today is visible, then we can find out if today is
0138      * near the beginning or the end of the month.
0139      * This is dependent on today remaining the index
0140      * in the array of visible dates and going from
0141      * top left (0) to bottom right (41).
0142      */
0143     [[nodiscard]] bool isBeginningOfMonth() const
0144     {
0145         return mToday <= 8;
0146     }
0147 
0148     [[nodiscard]] bool isEndOfMonth() const
0149     {
0150         return mToday >= 27;
0151     }
0152 
0153     /**
0154      *  Reimplemented from Akonadi::ETMCalendar
0155      *  They set mPendingChanges to true
0156      */
0157     void calendarIncidenceAdded(const KCalendarCore::Incidence::Ptr &incidence) override;
0158     void calendarIncidenceChanged(const KCalendarCore::Incidence::Ptr &incidence) override;
0159     void calendarIncidenceDeleted(const KCalendarCore::Incidence::Ptr &incidence, const KCalendarCore::Calendar *calendar) override;
0160 
0161     /** Sets which incidences should be highlighted */
0162     void setHighlightMode(bool highlightEvents, bool highlightTodos, bool highlightJournals);
0163     void setUpdateNeeded();
0164 public Q_SLOTS:
0165     /**
0166      * Recalculates all the flags of the days in the matrix like holidays or
0167      * events on a day (Actually calls above method with the actual startdate).
0168      */
0169     void updateView();
0170 
0171     /**
0172      * Calculates which square in the matrix should be highlighted to indicate
0173      * the square is on "today".
0174      */
0175     void recalculateToday();
0176 
0177     /**
0178      * Handle resource changes.
0179      */
0180     void resourcesChanged();
0181 
0182 Q_SIGNALS:
0183     /**
0184      * Emitted if the user selects a block of days with the mouse by dragging
0185      * a rectangle inside the matrix
0186      *
0187      * @param daylist list of days that have been selected by the user
0188      */
0189     void selected(const KCalendarCore::DateList &daylist);
0190 
0191     void newEventSignal(const QDate &date, const QDate &Date);
0192     void newTodoSignal(const QDate &date);
0193     void newJournalSignal(const QDate &date);
0194 
0195     /**
0196      * Emitted if the user has dropped an incidence (event or todo) inside
0197      * the matrix.
0198      *
0199      * @param incidence the dropped calendar incidence
0200      * @param dt QDate that has been selected
0201      */
0202     void incidenceDropped(const Akonadi::Item &item, const QDate &dt);
0203 
0204     /**
0205      * Emitted if the user has dropped an event inside the matrix and chose
0206      * to move it instead of copy
0207      *
0208      * @param oldincidence the new calendar incidence
0209      * @param dt QDate that has been selected
0210      */
0211     void incidenceDroppedMove(const Akonadi::Item &item, const QDate &dt);
0212 
0213 protected:
0214     bool event(QEvent *e) override;
0215 
0216     void paintEvent(QPaintEvent *ev) override;
0217 
0218     void mousePressEvent(QMouseEvent *e) override;
0219 
0220     void mouseReleaseEvent(QMouseEvent *e) override;
0221 
0222     void mouseMoveEvent(QMouseEvent *e) override;
0223 
0224     void resizeEvent(QResizeEvent *) override;
0225 
0226     void dragEnterEvent(QDragEnterEvent *e) override;
0227 
0228     void dragMoveEvent(QDragMoveEvent *e) override;
0229 
0230     void dragLeaveEvent(QDragLeaveEvent *e) override;
0231 
0232     void dropEvent(QDropEvent *e) override;
0233 
0234 private:
0235     /**
0236      * Pop-up a context menu for creating a new Event, To-do, or Journal.
0237      */
0238     void popupMenu(const QDate &date, const QDate &date2);
0239 
0240     /** returns the index of the day located at the matrix's widget (x,y) position.
0241      *
0242      *  @param x horizontal coordinate
0243      *  @param y vertical coordinate
0244      */
0245     int getDayIndexFrom(int x, int y) const;
0246 
0247     /** calculates a "shaded" color from the supplied color object.
0248      *  (Copied from Cornelius's kdpdatebutton.cpp)
0249      *
0250      *  @param color source based on which a shaded color should be calculated.
0251      */
0252     QColor getShadedColor(const QColor &color) const;
0253 
0254     /** updates mEvent list with all days that have events */
0255     void updateEvents();
0256 
0257     /** updates mEvent list with all days that have to-dos with due date */
0258     void updateTodos();
0259 
0260     /** updates mEvent list with all days that have journals */
0261     void updateJournals();
0262 
0263     /** number of days to be displayed. For now there is no support for any
0264         other number than 42. so change it at your own risk :o) */
0265     static const int NUMDAYS;
0266 
0267     /** calendar instance to be queried for holidays, events, ... */
0268     QList<Akonadi::CollectionCalendar::Ptr> mCalendars;
0269 
0270     /** starting date of the matrix */
0271     QDate mStartDate;
0272 
0273     /** array of day labels to optimeize drawing performance. */
0274     QString *mDayLabels = nullptr;
0275 
0276     /** array of days displayed to reduce memory consumption by
0277         subsequently calling QDate::addDays(). */
0278     QDate *mDays = nullptr;
0279 
0280     /** List for storing days which should be drawn using bold font. */
0281     QList<QDate> mEvents;
0282 
0283     /** stores holiday names of the days shown in the matrix. */
0284     QMap<int, QString> mHolidays;
0285 
0286     /** index of today or -1 if today is not visible in the matrix. */
0287     int mToday;
0288 
0289     /** index of day where dragged selection was initiated.
0290         used to detect "negative" timely selections */
0291     int mSelInit;
0292 
0293     /** if mSelStart has this value it indicates that there is no
0294         actual selection in the matrix. */
0295     static const int NOSELECTION;
0296 
0297     /** index of first selected day. */
0298     int mSelStart;
0299 
0300     /** index of last selected day. */
0301     int mSelEnd;
0302 
0303     /** default width of the frame drawn around today if it is visible
0304         in the matrix. */
0305     int mTodayMarginWidth;
0306 
0307     /** stores actual size of each day in the widget so we don't need to
0308      *  ask on every repaint.
0309      */
0310     QRect mDaySize;
0311 
0312     /**
0313      * Indicate pending calendar changes.
0314      */
0315     bool mPendingChanges = false;
0316 
0317     /** Whether days with events are highlighted */
0318     bool mHighlightEvents = false;
0319 
0320     /** Whether days with to-dos (with due date) are highlighted */
0321     bool mHighlightTodos = false;
0322 
0323     /** Whether days with journals are highlighted */
0324     bool mHighlightJournals = false;
0325 };