1 /*
2  *  messagedisplayhelper.h  -  helper class to display an alarm or error message
3  *  Program:  kalarm
4  *  SPDX-FileCopyrightText: 2001-2020 David Jarvie <djarvie@kde.org>
5  *
6  *  SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #pragma once
10 
11 #include "eventid.h"
12 #include "resources/resource.h"
13 #include "lib/file.h"
14 #include "lib/shellprocess.h"
15 
16 #include <KAlarmCal/KAEvent>
17 
18 #include <QObject>
19 #include <QHash>
20 #include <QPointer>
21 #include <QDateTime>
22 
23 class KConfigGroup;
24 class QTemporaryFile;
25 class AudioThread;
26 class PushButton;
27 class EditAlarmDlg;
28 class MessageDisplay;
29 
30 using namespace KAlarmCal;
31 
32 /**
33  * Class for use by alarm message display classes, to handle common functions
34  * for displaying an alarm.
35  * In order for this class to use signals and slots, it needs to derive from
36  * QObject. As a result, it has to be a separate class from MessageDisplay;
37  * otherwise, MessageWindow would derive from two QObject classes, which is
38  * prohibited.
39  */
40 class MessageDisplayHelper : public QObject
41 {
42     Q_OBJECT
43 public:
44     /** Contains the texts to display in the alarm. */
45     struct DisplayTexts
46     {
47         /** Identifiers for the fields in DisplayTexts. */
48         enum TextId
49         {
50             Title         = 0x01,    //!< DisplayTexts::title
51             Time          = 0x02,    //!< DisplayTexts::time
52             TimeFull      = 0x04,    //!< DisplayTexts::timeFull
53             FileName      = 0x08,    //!< DisplayTexts::fileName
54             Message       = 0x10,    //!< DisplayTexts::message
55             MessageAppend = 0x20,    //!< Text has been appended to DisplayTexts::message
56             RemainingTime = 0x40     //!< DisplayTexts::remainingTime
57         };
58         Q_DECLARE_FLAGS(TextIds, TextId)
59 
60         QString title;            // window/notification title
61         QString time;             // header showing alarm trigger time
62         QString timeFull;         // header showing alarm trigger time and "Reminder" if appropriate
63         QString fileName;         // if message is a file's contents, the file name
64         QString message;          // the alarm message
65         QString remainingTime;    // if advance reminder, the remaining time until the actual alarm
66         QString errorEmail[4];    // if email alarm error message, the 'To' and 'Subject' contents
67         File::FileType fileType;  // if message is a file's contents, the file type
68         bool    newLine {false};  // 'message' has a newline stripped from the end
69     };
70 
71     explicit MessageDisplayHelper(MessageDisplay* parent);     // for session management restoration only
72     MessageDisplayHelper(MessageDisplay* parent, const KAEvent&, const KAAlarm&, int flags);
73     MessageDisplayHelper(MessageDisplay* parent, const KAEvent&, const DateTime& alarmDateTime,
74                          const QStringList& errmsgs, const QString& dontShowAgain);
75     MessageDisplayHelper(const MessageDisplayHelper&) = delete;
76     MessageDisplayHelper& operator=(const MessageDisplayHelper&) = delete;
77     ~MessageDisplayHelper() override;
setParent(MessageDisplay * parent)78     void                setParent(MessageDisplay* parent)  { mParent = parent; }
setSilenceButton(PushButton * b)79     void                setSilenceButton(PushButton* b)    { mSilenceButton = b; }
80     void                repeat(const KAAlarm&);
dateTime()81     const DateTime&     dateTime()             { return mDateTime; }
alarmType()82     KAAlarm::Type       alarmType() const      { return mAlarmType; }
83     bool                cancelReminder(const KAEvent&, const KAAlarm&);
84     bool                updateDateTime(const KAEvent&, const KAAlarm&);
isValid()85     bool                isValid() const        { return !mInvalid; }
alwaysHidden()86     bool                alwaysHidden() const   { return mAlwaysHide; }
87     void                initTexts();
texts()88     const DisplayTexts& texts() const          { return mTexts; }
89     bool                activateAutoClose();
90     void                displayComplete(bool audio);
91     bool                alarmShowing(KAEvent&);
92     void                playAudio();
93     EditAlarmDlg*       createEdit();
94     void                executeEdit();
95     void                setDeferralLimit(const KAEvent&);
96 
97     /** Called when a close request has been received.
98      *  @return  true to close the alarm message, false to keep it open.
99      */
100     bool                closeEvent();
101 
102     bool                saveProperties(KConfigGroup&);
103     bool                readProperties(const KConfigGroup&);
104     bool                readPropertyValues(const KConfigGroup&);
105     bool                processPropertyValues();
106 
107     static int          instanceCount(bool excludeAlwaysHidden = false);
108     static bool         shouldShowError(const KAEvent& event, const QStringList& errmsgs, const QString& dontShowAgain = QString());
109     static MessageDisplay* findEvent(const EventId& eventId, MessageDisplay* exclude = nullptr);
110     static void         stopAudio(bool wait = false);
111     static bool         isAudioPlaying();
112 
113 Q_SIGNALS:
114     /** Signal emitted when texts in the alarm message have changed.
115      *  @param id      Which text has changed.
116      *  @param change  If id == MessageAppend, the text which has been appended.
117      */
118     void textsChanged(DisplayTexts::TextIds, const QString& change = QString());
119 
120     /** Signal emitted on completion of the command providing the alarm message text. */
121     void commandExited(bool success);
122 
123     /** Signal emitted when the alarm should close, after the auto-close time. */
124     void autoCloseNow();
125 
126 private Q_SLOTS:
127     void    showRestoredAlarm();
128     void    editCloseOk();
129     void    editCloseCancel();
130     void    checkDeferralLimit();
131     void    slotSpeak();
132     void    audioTerminating();
133     void    startAudio();
134     void    playReady();
135     void    playFinished();
slotSetRemainingTextDay()136     void    slotSetRemainingTextDay()     { setRemainingTextDay(true); }
slotSetRemainingTextMinute()137     void    slotSetRemainingTextMinute()  { setRemainingTextMinute(true); }
138     void    readProcessOutput(ShellProcess*);
139     void    commandCompleted(ShellProcess::Status);
140 
141 private:
142     QString dateTimeToDisplay() const;
143     void    setRemainingTextDay(bool notify);
144     void    setRemainingTextMinute(bool notify);
145     bool    haveErrorMessage(unsigned msg) const;
146     void    clearErrorMessage(unsigned msg) const;
147     void    redisplayAlarm();
148 
149     static QVector<MessageDisplayHelper*> mInstanceList;  // list of existing message displays
150     static QHash<EventId, unsigned> mErrorMessages; // error messages currently displayed, by event ID
151     // Sound file playing
152     static QPointer<AudioThread> mAudioThread;    // thread to play audio file
153 
154 public:
155     MessageDisplay*     mParent;
156     // Properties needed by readProperties()
157     QString             mMessage;
158     QFont               mFont;
159     QColor              mBgColour, mFgColour;
160     DateTime            mDateTime;                // date/time displayed in the message window
161     QDateTime           mCloseTime;               // UTC time at which window should be auto-closed
162     EventId             mEventId;
163     QString             mAudioFile;
164     float               mVolume;
165     float               mFadeVolume;
166     int                 mFadeSeconds;
167     int                 mDefaultDeferMinutes;
168     KAAlarm::Type       mAlarmType;
169     KAEvent::SubAction  mAction;
170     Akonadi::Item::Id   mAkonadiItemId;           // if email text, message's Akonadi Item ID, else -1
171     KAEvent::CmdErrType mCommandError;
172     QStringList         mErrorMsgs;
173     QString             mDontShowAgain;           // non-null for don't-show-again option with error message
174     int                 mAudioRepeatPause;
175     bool                mConfirmAck;
176     bool                mShowEdit;                // display the Edit button
177     bool                mNoDefer;                 // don't display a Defer option
178     bool                mInvalid;                 // restored window is invalid
179     // Miscellaneous
180     KAEvent             mEvent;                   // the whole event, for updating the calendar file
181     KAEvent             mOriginalEvent;           // the original event supplied to the constructor
182     Resource            mResource;                // resource which the event comes/came from
183     PushButton*         mSilenceButton {nullptr}; // button to stop audio, enabled when audio playing
184     EditAlarmDlg*       mEditDlg {nullptr};       // alarm edit dialog invoked by Edit button
185     QDateTime           mDeferLimit;              // last UTC time to which the message can currently be deferred
186     bool                mDisableDeferral {false}; // true if past deferral limit, so don't enable Defer button
187     bool                mNoCloseConfirm {false};  // the Defer or Edit button is closing the dialog
188     bool                mAlwaysHide {false};      // the window should never be displayed
189     bool                mErrorWindow {false};     // the window is simply an error message
190     bool                mNoPostAction;            // don't execute any post-alarm action
191     bool                mBeep;
192     bool                mSpeak;                   // the message should be spoken via kttsd
193 private:
194     DisplayTexts        mTexts;                   // texts to display in alarm message
195     QTemporaryFile*     mTempFile {nullptr};      // temporary file used to display image/HTML
196     QByteArray          mCommandOutput;           // cumulative output from command
197     bool                mCommandHaveStdout {false}; // true if some stdout has been received from command
198     bool                mNoRecordCmdError {false}; // don't record command alarm errors
199     bool                mInitialised {false};     // initTexts() has been called to create the alarm's texts
200     bool                mRescheduleEvent {false}; // true to delete event after message has been displayed
201 
202 //friend class MessageDisplay;
203 };
204 
205 Q_DECLARE_OPERATORS_FOR_FLAGS(MessageDisplayHelper::DisplayTexts::TextIds)
206 
207 
208 // vim: et sw=4:
209