File indexing completed on 2025-03-09 04:45:05

0001 /*
0002   SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
0003   SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0004   SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0005   SPDX-FileContributor: Kevin Krammer <krake@kdab.com>
0006 
0007   Marcus Bains line.
0008   SPDX-FileCopyrightText: 2001 Ali Rahimi <ali@mit.edu>
0009 
0010   SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0011 */
0012 #pragma once
0013 
0014 #include "agendaitem.h"
0015 #include "eventviews_export.h"
0016 #include "viewcalendar.h"
0017 
0018 #include <Akonadi/Item>
0019 
0020 #include <KCalendarCore/Todo>
0021 
0022 #include <QFrame>
0023 #include <QScrollArea>
0024 
0025 #include <memory>
0026 
0027 namespace Akonadi
0028 {
0029 class IncidenceChanger;
0030 }
0031 
0032 namespace EventViews
0033 {
0034 class Agenda;
0035 class AgendaItem;
0036 class AgendaView;
0037 
0038 class MarcusBainsPrivate;
0039 
0040 class MarcusBains : public QFrame
0041 {
0042     Q_OBJECT
0043 public:
0044     explicit MarcusBains(EventView *eventView, Agenda *agenda = nullptr);
0045     void updateLocationRecalc(bool recalculate = false);
0046     ~MarcusBains() override;
0047 
0048 public Q_SLOTS:
0049     void updateLocation();
0050 
0051 private:
0052     std::unique_ptr<MarcusBainsPrivate> const d;
0053 };
0054 
0055 class AgendaPrivate;
0056 
0057 class EVENTVIEWS_EXPORT Agenda : public QWidget
0058 {
0059     Q_OBJECT
0060 public:
0061     Agenda(AgendaView *agendaView, QScrollArea *scrollArea, int columns, int rows, int rowSize, bool isInteractive);
0062 
0063     Agenda(AgendaView *agendaView, QScrollArea *scrollArea, int columns, bool isInteractive);
0064 
0065     ~Agenda() override;
0066 
0067     [[nodiscard]] KCalendarCore::Incidence::Ptr selectedIncidence() const;
0068     [[nodiscard]] QDate selectedIncidenceDate() const;
0069     QSize sizeHint() const override;
0070     QSize minimumSizeHint() const override;
0071     QSize minimumSize() const;
0072     int minimumHeight() const;
0073     // QSizePolicy sizePolicy() const;
0074     [[nodiscard]] int contentsY() const
0075     {
0076         return -y();
0077     }
0078 
0079     [[nodiscard]] int contentsX() const
0080     {
0081         return x();
0082     }
0083 
0084     QScrollBar *verticalScrollBar() const;
0085 
0086     QScrollArea *scrollArea() const;
0087 
0088     [[nodiscard]] AgendaItem::List agendaItems(const QString &uid) const;
0089 
0090     /**
0091       Returns the uid of the last incidence that was selected. This
0092       persists across reloads and clear, so that if the same uid
0093       reappears, it can be reselected.
0094     */
0095     [[nodiscard]] QString lastSelectedItemUid() const;
0096 
0097     bool eventFilter(QObject *, QEvent *) override;
0098 
0099     void paintEvent(QPaintEvent *) override;
0100 
0101     [[nodiscard]] QPoint contentsToGrid(QPoint pos) const;
0102     [[nodiscard]] QPoint gridToContents(QPoint gpos) const;
0103 
0104     [[nodiscard]] int timeToY(QTime time) const;
0105     [[nodiscard]] QTime gyToTime(int y) const;
0106 
0107     [[nodiscard]] QList<int> minContentsY() const;
0108     [[nodiscard]] QList<int> maxContentsY() const;
0109 
0110     [[nodiscard]] int visibleContentsYMin() const;
0111     [[nodiscard]] int visibleContentsYMax() const;
0112 
0113     void setStartTime(QTime startHour);
0114 
0115     AgendaItem::QPtr insertItem(const KCalendarCore::Incidence::Ptr &incidence,
0116                                 const QDateTime &recurrenceId,
0117                                 int X,
0118                                 int YTop,
0119                                 int YBottom,
0120                                 int itemPos,
0121                                 int itemCount,
0122                                 bool isSelected);
0123 
0124     AgendaItem::QPtr insertAllDayItem(const KCalendarCore::Incidence::Ptr &event, const QDateTime &recurrenceId, int XBegin, int XEnd, bool isSelected);
0125 
0126     void
0127     insertMultiItem(const KCalendarCore::Incidence::Ptr &event, const QDateTime &recurrenceId, int XBegin, int XEnd, int YTop, int YBottom, bool isSelected);
0128 
0129     /**
0130       Removes an event and all its multi-items from the agenda. This function
0131       removes the items from the view, but doesn't delete them immediately.
0132       Instead, they are queued in mItemsToDelete and later deleted by the
0133       slot deleteItemsToDelete() (called by QTimer::singleShot ).
0134       @param incidence The pointer to the incidence that should be removed.
0135     */
0136     void removeIncidence(const KCalendarCore::Incidence::Ptr &incidence);
0137 
0138     void changeColumns(int columns);
0139 
0140     [[nodiscard]] int columns() const;
0141     [[nodiscard]] int rows() const;
0142 
0143     [[nodiscard]] double gridSpacingX() const;
0144     [[nodiscard]] double gridSpacingY() const;
0145 
0146     void clear();
0147 
0148     /** Update configuration from preference settings */
0149     void updateConfig();
0150 
0151     void checkScrollBoundaries();
0152 
0153     void setHolidayMask(QList<bool> *);
0154 
0155     void setDateList(const KCalendarCore::DateList &selectedDates);
0156     [[nodiscard]] KCalendarCore::DateList dateList() const;
0157 
0158     void setCalendar(const EventViews::MultiViewCalendar::Ptr &cal);
0159 
0160     void setIncidenceChanger(Akonadi::IncidenceChanger *changer);
0161 
0162 public Q_SLOTS:
0163     void scrollUp();
0164     void scrollDown();
0165 
0166     void checkScrollBoundaries(int);
0167 
0168     /** Deselect selected items. This function does not Q_EMIT any signals. */
0169     void deselectItem();
0170 
0171     void clearSelection();
0172 
0173     /**
0174       Select item. If the argument is 0, the currently selected item gets
0175       deselected. This function emits the itemSelected(bool) signal to inform
0176       about selection/deselection of events.
0177     */
0178     void selectItem(const AgendaItem::QPtr &);
0179 
0180     /**
0181       Selects the item associated with a given Akonadi Item id.
0182       Linear search, use carefully.
0183       @param id the item id of the item that should be selected. If no such
0184       item exists, the selection is not changed.
0185     */
0186     void selectIncidenceByUid(const QString &id);
0187     void selectItem(const Akonadi::Item &item);
0188 
0189     bool removeAgendaItem(const AgendaItem::QPtr &item);
0190     void showAgendaItem(const AgendaItem::QPtr &item);
0191 
0192 Q_SIGNALS:
0193     void newEventSignal();
0194     void newTimeSpanSignal(const QPoint &, const QPoint &);
0195     void newStartSelectSignal();
0196 
0197     void showIncidenceSignal(const KCalendarCore::Incidence::Ptr &);
0198     void editIncidenceSignal(const KCalendarCore::Incidence::Ptr &);
0199     void deleteIncidenceSignal(const KCalendarCore::Incidence::Ptr &);
0200     void showIncidencePopupSignal(const KCalendarCore::Incidence::Ptr &, const QDate &);
0201 
0202     void showNewEventPopupSignal();
0203 
0204     void incidenceSelected(const KCalendarCore::Incidence::Ptr &, const QDate &);
0205 
0206     void lowerYChanged(int);
0207     void upperYChanged(int);
0208 
0209     void startDragSignal(const KCalendarCore::Incidence::Ptr &);
0210     void droppedIncidences(const KCalendarCore::Incidence::List &, const QPoint &gpos, bool allDay);
0211     void droppedIncidences(const QList<QUrl> &, const QPoint &gpos, bool allDay);
0212 
0213     void enableAgendaUpdate(bool enable);
0214     void zoomView(const int delta, const QPoint &pos, const Qt::Orientation);
0215 
0216     void mousePosSignal(const QPoint &pos);
0217     void enterAgenda();
0218     void leaveAgenda();
0219 
0220     void gridSpacingYChanged(double);
0221 
0222 private:
0223     enum MouseActionType { NOP, MOVE, SELECT, RESIZETOP, RESIZEBOTTOM, RESIZELEFT, RESIZERIGHT };
0224 
0225     AgendaItem::QPtr
0226     createAgendaItem(const KCalendarCore::Incidence::Ptr &incidence, int itemPos, int itemCount, const QDateTime &recurrentId, bool isSelected);
0227 
0228 protected:
0229     /**
0230       Draw the background grid of the agenda.
0231       @p cw grid width
0232       @p ch grid height
0233     */
0234     void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
0235 
0236     int columnWidth(int column) const;
0237     void resizeEvent(QResizeEvent *) override;
0238 
0239     /** Handles mouse events. Called from eventFilter */
0240     virtual bool eventFilter_mouse(QObject *, QMouseEvent *);
0241 #ifndef QT_NO_WHEELEVENT
0242     /** Handles mousewheel events. Called from eventFilter */
0243     virtual bool eventFilter_wheel(QObject *, QWheelEvent *);
0244 #endif
0245     /** Handles key events. Called from eventFilter */
0246     virtual bool eventFilter_key(QObject *, QKeyEvent *);
0247 
0248     /** Handles drag and drop events. Called from eventFilter */
0249     virtual bool eventFilter_drag(QObject *, QDropEvent *);
0250 
0251     /** returns RESIZELEFT if pos is near the lower edge of the action item,
0252       RESIZERIGHT if pos is near the higher edge, and MOVE otherwise.
0253       If --reverse is used, RESIZELEFT still means resizing the beginning of
0254       the event, although that means moving to the right!
0255       horizontal is the same as mAllDayAgenda.
0256         @param horizontal Whether horizontal resizing is  possible
0257         @param pos The current mouse position
0258         @param item The affected item
0259     */
0260     MouseActionType isInResizeArea(bool horizontal, QPoint pos, const AgendaItem::QPtr &item);
0261     /** Return whether the cell specified by the grid point belongs to the current select
0262      */
0263     bool ptInSelection(QPoint gpos) const;
0264 
0265     /** Start selecting time span. */
0266     void startSelectAction(QPoint viewportPos);
0267 
0268     /** Select time span. */
0269     void performSelectAction(QPoint viewportPos);
0270 
0271     /** Emd selecting time span. */
0272     void endSelectAction(const QPoint &viewportPos);
0273 
0274     /** Start moving/resizing agenda item */
0275     void startItemAction(const QPoint &viewportPos);
0276 
0277     /** Move/resize agenda item */
0278     void performItemAction(QPoint viewportPos);
0279 
0280     /** End moving/resizing agenda item */
0281     void endItemAction();
0282 
0283     /** Set cursor, when no item action is in progress */
0284     void setNoActionCursor(const AgendaItem::QPtr &moveItem, QPoint viewportPos);
0285     /** Sets the cursor according to the given action type.
0286         @param actionType The type of action for which the cursor should be set.
0287         @param acting If true, the corresponding action is running (e.g. the
0288         item is currently being moved by the user). If false the
0289         cursor should just indicate that the corresponding
0290         action is possible */
0291     void setActionCursor(int actionType, bool acting = false);
0292 
0293     /** calculate the width of the column subcells of the given item */
0294     double calcSubCellWidth(const AgendaItem::QPtr &item);
0295     /** Move and resize the given item to the correct position */
0296     void placeAgendaItem(const AgendaItem::QPtr &item, double subCellWidth);
0297     /** Place agenda item in agenda and adjust other cells if necessary */
0298     void placeSubCells(const AgendaItem::QPtr &placeItem);
0299     /** Place the agenda item at the correct position (ignoring conflicting items) */
0300     void adjustItemPosition(const AgendaItem::QPtr &item);
0301 
0302     /** Process the keyevent, including the ignored keyevents of eventwidgets.
0303      * Implements pgup/pgdn and cursor key navigation in the view.
0304      */
0305     void keyPressEvent(QKeyEvent *) override;
0306 
0307     void calculateWorkingHours();
0308 
0309     virtual void contentsMousePressEvent(QMouseEvent *);
0310 
0311 protected Q_SLOTS:
0312     /** delete the items that are queued for deletion */
0313     void deleteItemsToDelete();
0314     /** Resizes all the child elements after the size of the agenda
0315         changed. This is needed because Qt seems to have a bug when
0316         the resizeEvent of one of the widgets in a splitter takes a
0317         lot of time / does a lot of resizes.... see bug 80114 */
0318     void resizeAllContents();
0319 
0320 private:
0321     EVENTVIEWS_NO_EXPORT void init();
0322     EVENTVIEWS_NO_EXPORT void marcus_bains();
0323 
0324 private:
0325     friend class AgendaPrivate;
0326     std::unique_ptr<AgendaPrivate> const d;
0327 };
0328 
0329 class AgendaScrollArea : public QScrollArea
0330 {
0331     Q_OBJECT
0332 public:
0333     AgendaScrollArea(bool allDay, AgendaView *agendaView, bool isInteractive, QWidget *parent);
0334     ~AgendaScrollArea() override;
0335 
0336     Agenda *agenda() const;
0337 
0338 private:
0339     Agenda *mAgenda = nullptr;
0340 };
0341 }