1/*
2 This file is part of the kcalcore library.
3
4 Copyright (c) 2001-2003 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (c) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6 Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net>
7 Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
8 Contact: Alvaro Manera <alvaro.manera@nokia.com>
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
19
20 You should have received a copy of the GNU Library General Public License
21 along with this library; see the file COPYING.LIB. If not, write to
22 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 Boston, MA 02110-1301, USA.
24*/
25/**
26 @file
27 This file is part of the API for handling calendar data and
28 defines the IncidenceBase class.
29
30 @author Cornelius Schumacher \<schumacher@kde.org\>
31 @author Reinhold Kainhofer \<reinhold@kainhofer.com\>
32 @author Rafal Rzepecki \<divide@users.sourceforge.net\>
33
34 @glossary @anchor incidence @b incidence:
35 General term for a calendar component.
36 Examples are events, to-dos, and journals.
37
38 @glossary @anchor event @b event:
39 An @ref incidence that has a start and end time, typically representing some
40 occurrence of social or personal importance. May be recurring.
41 Examples are appointments, meetings, or holidays.
42
43 @glossary @anchor to-do @b to-do:
44 An @ref incidence that has an optional start time and an optional due time
45 typically representing some undertaking to be performed. May be recurring.
46 Examples are "fix the bug" or "pay the bills".
47
48 @glossary @anchor todo @b todo:
49 See @ref to-do.
50
51 @glossary @anchor journal @b journal:
52 An @ref incidence with a start date that represents a diary or daily record
53 of one's activities. May @b not be recurring.
54*/
55
56#ifndef KCALCORE_INCIDENCEBASE_H
57#define KCALCORE_INCIDENCEBASE_H
58
59#include "attendee.h"
60#include "customproperties.h"
61#include "duration.h"
62#include "sortablelist.h"
63
64#include <KDE/KDateTime>
65
66#include <QtCore/QSet>
67#include <QtCore/QUrl>
68#include <QDataStream>
69
70class KUrl;
71class QDate;
72
73namespace KCalCore {
74
75/** List of dates */
76typedef SortableList<QDate> DateList;
77
78/** List of times */
79typedef SortableList<KDateTime> DateTimeList;
80
81class Event;
82class Todo;
83class Journal;
84class FreeBusy;
85class Visitor;
86
87/**
88 @brief
89 An abstract class that provides a common base for all calendar incidence
90 classes.
91
92 define: organizer (person)
93 define: uid (same as the attendee uid?)
94
95 Several properties are not allowed for VFREEBUSY objects (see rfc:2445),
96 so they are not in IncidenceBase. The hierarchy is:
97
98 IncidenceBase
99 + FreeBusy
100 + Incidence
101 + Event
102 + Todo
103 + Journal
104
105 So IncidenceBase contains all properties that are common to all classes,
106 and Incidence contains all additional properties that are common to
107 Events, Todos and Journals, but are not allowed for FreeBusy entries.
108*/
109class KCALCORE_EXPORT IncidenceBase : public CustomProperties
110{
111public:
112 /**
113 A shared pointer to an IncidenceBase.
114 */
115 typedef QSharedPointer<IncidenceBase> Ptr;
116
117 /**
118 The different types of incidences, per RFC2445.
119 @see type(), typeStr()
120 */
121 enum IncidenceType {
122 TypeEvent = 0, /**< Type is an event */
123 TypeTodo, /**< Type is a to-do */
124 TypeJournal, /**< Type is a journal */
125 TypeFreeBusy, /**< Type is a free/busy */
126 TypeUnknown /**< Type unknown */
127 };
128
129 /**
130 The different types of incidence date/times roles.
131 @see dateTime()
132 */
133 enum DateTimeRole {
134 RoleAlarmStartOffset = 0,/**< Role for an incidence alarm's starting offset date/time */
135 RoleAlarmEndOffset, /**< Role for an incidence alarm's ending offset date/time */
136 RoleSort, /**< Role for an incidence's date/time used when sorting */
137 RoleCalendarHashing, /**< Role for looking up an incidence in a Calendar */
138 RoleStartTimeZone, /**< Role for determining an incidence's starting timezone */
139 RoleEndTimeZone, /**< Role for determining an incidence's ending timezone */
140 RoleEndRecurrenceBase,
141 RoleEnd, /**< Role for determining an incidence's dtEnd, will return
142 an invalid KDateTime if the incidence does not support dtEnd */
143 RoleDisplayEnd, /**< Role used for display purposes, represents the end boundary
144 if an incidence supports dtEnd */
145 RoleAlarm, /**< Role for determining the date/time of the first alarm.
146 Returns invalid time if the incidence doesn't have any alarm */
147 RoleRecurrenceStart, /**< Role for determining the start of the recurrence.
148 Currently that's DTSTART for an event and DTDUE for a to-do.
149 (NOTE: If the incidence is a to-do, recurrence should be
150 calculated having DTSTART for a reference, not DT-DUE.
151 This is one place KCalCore isn't compliant with RFC2445) */
152 RoleDisplayStart, /**< Role for display purposes, represents the start boundary of an
153 incidence. To-dos return dtDue here, for historical reasons */
154 RoleDnD /**< Role for determining new start and end dates after a DnD */
155 };
156
157 /**
158 The different types of incidence fields.
159 */
160 enum Field {
161 FieldDtStart, ///> Field representing the DTSTART component.
162 FieldDtEnd, ///> Field representing the DTEND component.
163 FieldLastModified, ///> Field representing the LAST-MODIFIED component.
164 FieldDescription, ///> Field representing the DESCRIPTION component.
165 FieldSummary, ///> Field representing the SUMMARY component.
166 FieldLocation, ///> Field representing the LOCATION component.
167 FieldCompleted, ///> Field representing the COMPLETED component.
168 FieldPercentComplete, ///> Field representing the PERCENT-COMPLETE component.
169 FieldDtDue, ///> Field representing the DUE component.
170 FieldCategories, ///> Field representing the CATEGORIES component.
171 FieldRelatedTo, ///> Field representing the RELATED-TO component.
172 FieldRecurrence, ///> Field representing the EXDATE, EXRULE, RDATE, and RRULE components.
173 FieldAttachment, ///> Field representing the ATTACH component.
174 FieldSecrecy, ///> Field representing the CLASS component.
175 FieldStatus, ///> Field representing the STATUS component.
176 FieldTransparency, ///> Field representing the TRANSPARENCY component.
177 FieldResources, ///> Field representing the RESOURCES component.
178 FieldPriority, ///> Field representing the PRIORITY component.
179 FieldGeoLatitude, ///> Field representing the latitude part of the GEO component.
180 FieldGeoLongitude, ///> Field representing the longitude part of the GEO component.
181 FieldRecurrenceId, ///> Field representing the RECURRENCE-ID component.
182 FieldAlarms, ///> Field representing the VALARM component.
183 FieldSchedulingId, ///> Field representing the X-KDE-LIBKCAL-ID component.
184 FieldAttendees, ///> Field representing the ATTENDEE component.
185 FieldOrganizer, ///> Field representing the ORGANIZER component.
186 FieldCreated, ///> Field representing the CREATED component.
187 FieldRevision, ///> Field representing the SEQUENCE component.
188 FieldDuration, ///> Field representing the DURATION component.
189 FieldContact, ///> Field representing the CONTACT component.
190 FieldComment, ///> Field representing the COMMENT component.
191 FieldUid, ///> Field representing the UID component.
192 FieldUnknown, ///> Something changed. Always set when you use the assignment operator.
193 FieldUrl ///> Field representing the URL component.
194 };
195
196 /**
197 The IncidenceObserver class.
198 */
199 class KCALCORE_EXPORT IncidenceObserver
200 {
201 public:
202
203 /**
204 Destroys the IncidenceObserver.
205 */
206 virtual ~IncidenceObserver();
207
208 /**
209 The IncidenceObserver interface.
210 This function is called before any changes are made.
211 @param uid is the string containing the incidence @ref uid.
212 @param recurrenceId is possible recurrenceid of incidence.
213 */
214 virtual void incidenceUpdate(const QString &uid, const KDateTime &recurrenceId) = 0;
215
216 /**
217 The IncidenceObserver interface.
218 This function is called after changes are completed.
219 @param uid is the string containing the incidence @ref uid.
220 @param recurrenceId is possible recurrenceid of incidence.
221 */
222 virtual void incidenceUpdated(const QString &uid, const KDateTime &recurrenceId) = 0;
223 };
224
225 /**
226 Constructs an empty IncidenceBase.
227 */
228 IncidenceBase();
229
230 /**
231 Destroys the IncidenceBase.
232 */
233 virtual ~IncidenceBase();
234
235 /**
236 Assignment operator.
237 All data belonging to derived classes are also copied. @see assign().
238 The caller guarantees that both types match.
239
240 @code
241 if ( i1.type() == i2.type() ) {
242 i1 = i2;
243 } else {
244 kDebug() << "Invalid assignment!";
245 }
246 @endcode
247
248 Dirty field FieldUnknown will be set.
249
250 @param other is the IncidenceBase to assign.
251 */
252 IncidenceBase &operator=(const IncidenceBase &other);
253
254 /**
255 Compares this with IncidenceBase @p ib for equality.
256 All data belonging to derived classes are also compared. @see equals().
257 @param ib is the IncidenceBase to compare against.
258 @return true if the incidences are equal; false otherwise.
259 */
260 bool operator==(const IncidenceBase &ib) const;
261
262 /**
263 Compares this with IncidenceBase @p ib for inequality.
264 @param ib is the IncidenceBase to compare against.
265 @return true if the incidences are /not/ equal; false otherwise.
266 */
267 bool operator!=(const IncidenceBase &ib) const;
268
269 /**
270 Accept IncidenceVisitor. A class taking part in the visitor mechanism
271 has to provide this implementation:
272 <pre>
273 bool accept(Visitor &v) { return v.visit(this); }
274 </pre>
275
276 @param v is a reference to a Visitor object.
277 @param incidence is a valid IncidenceBase object for visting.
278 */
279 virtual bool accept(Visitor &v, IncidenceBase::Ptr incidence);
280
281 /**
282 Returns the incidence type.
283 */
284 virtual IncidenceType type() const = 0;
285
286 /**
287 Prints the type of incidence as a string.
288 */
289 virtual QByteArray typeStr() const = 0;
290
291 /**
292 Sets the unique id for the incidence to @p uid.
293 @param uid is the string containing the incidence @ref uid.
294 @see uid()
295 */
296 void setUid(const QString &uid);
297
298 /**
299 Returns the unique id (@ref uid) for the incidence.
300 @see setUid()
301 */
302 QString uid() const;
303
304 /**
305 Returns the uri for the incidence, of form urn:x-ical:\<uid\>
306 */
307 KUrl uri() const;
308
309 /**
310 Sets the time the incidence was last modified to @p lm.
311 It is stored as a UTC date/time.
312
313 @param lm is the KDateTime when the incidence was last modified.
314
315 @see lastModified()
316 */
317 virtual void setLastModified(const KDateTime &lm);
318
319 /**
320 Returns the time the incidence was last modified.
321 @see setLastModified()
322 */
323 KDateTime lastModified() const;
324
325 /**
326 Sets the organizer for the incidence.
327
328 @param organizer is a non-null Person to use as the incidence @ref organizer.
329 @see organizer(), setOrganizer(const QString &)
330 */
331 void setOrganizer(const Person::Ptr &organizer);
332
333 /**
334 Sets the incidence organizer to any string @p organizer.
335
336 @param organizer is a string to use as the incidence @ref organizer.
337 @see organizer(), setOrganizer(const Person &)
338 */
339 void setOrganizer(const QString &organizer);
340
341 /**
342 Returns the Person associated with this incidence.
343 If no Person was set through setOrganizer(), a default Person()
344 is returned.
345 @see setOrganizer(const QString &), setOrganizer(const Person &)
346 */
347 Person::Ptr organizer() const;
348
349 /**
350 Sets readonly status.
351
352 @param readOnly if set, the incidence is read-only; else the incidence
353 can be modified.
354 @see isReadOnly().
355 */
356 virtual void setReadOnly(bool readOnly);
357
358 /**
359 Returns true the object is read-only; false otherwise.
360 @see setReadOnly()
361 */
362 bool isReadOnly() const;
363
364 /**
365 Sets the incidence's starting date/time with a KDateTime.
366 The incidence's all-day status is set according to whether @p dtStart
367 is a date/time (not all-day) or date-only (all-day).
368
369 @param dtStart is the incidence start date/time.
370 @see dtStart().
371 */
372 virtual void setDtStart(const KDateTime &dtStart);
373
374 /**
375 Returns an incidence's starting date/time as a KDateTime.
376 @see setDtStart().
377 */
378 virtual KDateTime dtStart() const;
379
380 /**
381 Sets the incidence duration.
382
383 @param duration the incidence duration
384
385 @see duration()
386 */
387 virtual void setDuration(const Duration &duration);
388
389 /**
390 Returns the length of the incidence duration.
391 @see setDuration()
392 */
393 Duration duration() const;
394
395 /**
396 Sets if the incidence has a duration.
397 @param hasDuration true if the incidence has a duration; false otherwise.
398 @see hasDuration()
399 */
400 void setHasDuration(bool hasDuration);
401
402 /**
403 Returns true if the incidence has a duration; false otherwise.
404 @see setHasDuration()
405 */
406 bool hasDuration() const;
407
408 /**
409 Returns true or false depending on whether the incidence is all-day.
410 i.e. has a date but no time attached to it.
411 @see setAllDay()
412 */
413 bool allDay() const;
414
415 /**
416 Sets whether the incidence is all-day, i.e. has a date but no time
417 attached to it.
418
419 @param allDay sets whether the incidence is all-day.
420
421 @see allDay()
422 */
423 void setAllDay(bool allDay);
424
425 /**
426 Shift the times of the incidence so that they appear at the same clock
427 time as before but in a new time zone. The shift is done from a viewing
428 time zone rather than from the actual incidence time zone.
429
430 For example, shifting an incidence whose start time is 09:00
431 America/New York, using an old viewing time zone (@p oldSpec)
432 of Europe/London, to a new time zone (@p newSpec) of Europe/Paris,
433 will result in the time being shifted from 14:00 (which is the London
434 time of the incidence start) to 14:00 Paris time.
435
436 @param oldSpec the time specification which provides the clock times
437 @param newSpec the new time specification
438 */
439 virtual void shiftTimes(const KDateTime::Spec &oldSpec,
440 const KDateTime::Spec &newSpec);
441
442 /**
443 Adds a comment to the incidence. Does not add a linefeed character; simply
444 appends the text as specified.
445
446 @param comment is the QString containing the comment to add.
447 @see removeComment().
448 */
449 void addComment(const QString &comment);
450
451 /**
452 Removes a comment from the incidence. Removes the first comment whose
453 string is an exact match for the specified string in @p comment.
454
455 @param comment is the QString containing the comment to remove.
456 @return true if match found, false otherwise.
457 @see addComment().
458 */
459 bool removeComment(const QString &comment);
460
461 /**
462 Deletes all incidence comments.
463 */
464 void clearComments();
465
466 /**
467 Returns all incidence comments as a list of strings.
468 */
469 QStringList comments() const;
470
471 /**
472 Adds a contact to thieincidence. Does not add a linefeed character; simply
473 appends the text as specified.
474
475 @param contact is the QString containing the contact to add.
476 @see removeContact().
477 */
478 void addContact(const QString &contact);
479
480 /**
481 Removes a contact from the incidence. Removes the first contact whose
482 string is an exact match for the specified string in @p contact.
483
484 @param contact is the QString containing the contact to remove.
485 @return true if match found, false otherwise.
486 @see addContact().
487 */
488 bool removeContact(const QString &contact);
489
490 /**
491 Deletes all incidence contacts.
492 */
493 void clearContacts();
494
495 /**
496 Returns all incidence contacts as a list of strings.
497 */
498 QStringList contacts() const;
499
500 /**
501 Add Attendee to this incidence. IncidenceBase takes ownership of the
502 Attendee object.
503
504 @param attendee a pointer to the attendee to add
505 @param doUpdate If true the Observers are notified, if false they are not.
506 */
507 void addAttendee(const Attendee::Ptr &attendee,
508 bool doUpdate = true);
509
510 /**
511 Removes all attendees from the incidence.
512 */
513 void clearAttendees();
514
515 /**
516 Delete single attendee from the incidence.
517
518 The given attendee will be delete()d at the end of this call.
519
520 @param attendee The attendee to be removeComment
521 @param doUpdate If true the Observers are notified, if false they are not.
522 */
523 void deleteAttendee(const Attendee::Ptr &attendee,
524 bool doUpdate = true);
525
526 /**
527 Returns a list of incidence attendees.
528 All pointers in the list are valid.
529 */
530 Attendee::List attendees() const;
531
532 /**
533 Returns the number of incidence attendees.
534 */
535 int attendeeCount() const;
536
537 /**
538 Returns the attendee with the specified email address.
539
540 @param email is a QString containing an email address of the
541 form "FirstName LastName <emailaddress>".
542 @see attendeeByMails(), attendeesByUid().
543 */
544 Attendee::Ptr attendeeByMail(const QString &email) const;
545
546 /**
547 Returns the first incidence attendee with one of the specified
548 email addresses.
549
550 @param emails is a list of QStrings containing email addresses of the
551 form "FirstName LastName <emailaddress>".
552 @param email is a QString containing a single email address to search
553 in addition to the list specified in @p emails.
554 @see attendeeByMail(), attendeesByUid().
555 */
556 Attendee::Ptr attendeeByMails(const QStringList &emails,
557 const QString &email = QString()) const;
558
559 /**
560 Returns the incidence attendee with the specified attendee @acronym UID.
561
562 @param uid is a QString containing an attendee @acronym UID.
563 @see attendeeByMail(), attendeeByMails().
564 */
565 Attendee::Ptr attendeeByUid(const QString &uid) const;
566
567 /**
568 Sets the incidences url.
569
570 This property can be used to point to a more dynamic rendition of the incidence.
571 I.e. a website related to the incidence.
572
573 @param url of the incience.
574 @see url()
575 @since 4.12
576 */
577 void setUrl(const QUrl &url);
578
579 /**
580 Returns the url.
581 @return incidences url value
582 @see setUrl()
583 @since 4.12
584 */
585 QUrl url() const;
586
587 /**
588 Register observer. The observer is notified when the observed object
589 changes.
590
591 @param observer is a pointer to an IncidenceObserver object that will be
592 watching this incidence.
593 @see unRegisterObserver()
594 */
595 void registerObserver(IncidenceObserver *observer);
596
597 /**
598 Unregister observer. It isn't notified anymore about changes.
599
600 @param observer is a pointer to an IncidenceObserver object that will be
601 watching this incidence.
602 @see registerObserver().
603 */
604 void unRegisterObserver(IncidenceObserver *observer);
605
606 /**
607 Call this to notify the observers after the IncidenceBase object will be
608 changed.
609 */
610 void update();
611
612 /**
613 Call this to notify the observers after the IncidenceBase object has
614 changed.
615 */
616 void updated();
617
618 /**
619 Call this when a group of updates is going to be made. This suppresses
620 change notifications until endUpdates() is called, at which point
621 updated() will automatically be called.
622 */
623 void startUpdates();
624
625 /**
626 Call this when a group of updates is complete, to notify observers that
627 the instance has changed. This should be called in conjunction with
628 startUpdates().
629 */
630 void endUpdates();
631
632 /**
633 Returns a date/time corresponding to the specified DateTimeRole.
634 @param role is a DateTimeRole.
635 */
636 virtual KDateTime dateTime(DateTimeRole role) const = 0;
637
638 /**
639 Sets the date/time corresponding to the specified DateTimeRole.
640 @param dateTime is KDateTime value to set.
641 @param role is a DateTimeRole.
642 */
643 virtual void setDateTime(const KDateTime &dateTime, DateTimeRole role) = 0;
644
645 /**
646 Returns the Akonadi specific sub MIME type of a KCalCore::IncidenceBase item,
647 e.g. getting "application/x-vnd.akonadi.calendar.event" for a KCalCore::Event.
648 */
649 virtual QLatin1String mimeType() const = 0;
650
651 /**
652 Returns the incidence recurrenceId.
653 @return incidences recurrenceId value
654 @see setRecurrenceId().
655 */
656 virtual KDateTime recurrenceId() const;
657
658 /**
659 Returns a QSet with all Fields that were changed since the incidence was created
660 or resetDirtyFields() was called.
661
662 @see resetDirtyFields()
663 */
664 QSet<IncidenceBase::Field> dirtyFields() const;
665
666 /**
667 Sets which fields are dirty.
668 @see dirtyFields()
669 @since 4.8
670 */
671 void setDirtyFields(const QSet<IncidenceBase::Field> &);
672
673 /**
674 Resets dirty fields.
675 @see dirtyFields()
676 */
677 void resetDirtyFields();
678
679 /**
680 * Constant that identifies KCalCore data in a binary stream.
681 *
682 * @since 4.12
683 */
684 static quint32 magicSerializationIdentifier();
685
686protected:
687
688 /**
689 Marks Field @p field as dirty.
690 @param field is the Field type to mark as dirty.
691 @see dirtyFields()
692 */
693 void setFieldDirty(IncidenceBase::Field field);
694
695 /**
696 @copydoc
697 CustomProperties::customPropertyUpdate()
698 */
699 virtual void customPropertyUpdate();
700
701 /**
702 @copydoc
703 CustomProperties::customPropertyUpdated()
704 */
705 virtual void customPropertyUpdated();
706
707 /**
708 Constructs an IncidenceBase as a copy of another IncidenceBase object.
709 @param ib is the IncidenceBase to copy.
710 */
711 IncidenceBase(const IncidenceBase &ib);
712
713 /**
714 Provides polymorfic comparison for equality.
715 Only called by IncidenceBase::operator==() which guarantees that
716 @p incidenceBase is of the right type.
717 @param incidenceBase is the IncidenceBase to compare against.
718 @return true if the incidences are equal; false otherwise.
719 */
720 virtual bool equals(const IncidenceBase &incidenceBase) const;
721
722 /**
723 Provides polymorfic assignment.
724 @param other is the IncidenceBase to assign.
725 */
726 virtual IncidenceBase &assign(const IncidenceBase &other);
727
728 /**
729 Standard trick to add virtuals later.
730
731 @param id is any integer unique to this class which we will use to identify the method
732 to be called.
733 @param data is a pointer to some glob of data, typically a struct.
734 // TODO_KDE5: change from int to VirtualHook type.
735 */
736 virtual void virtual_hook(int id, void *data) = 0;
737
738 enum VirtualHook {
739 SerializerHook,
740 DeserializerHook
741 };
742
743 /**
744 Identifies a read-only incidence.
745 */
746 bool mReadOnly;
747
748private:
749 //@cond PRIVATE
750 class Private;
751 Private *const d;
752 //@endcond
753
754 friend KCALCORE_EXPORT QDataStream &operator<<(QDataStream &stream,
755 const KCalCore::IncidenceBase::Ptr &);
756
757 friend KCALCORE_EXPORT QDataStream &operator>>(QDataStream &stream,
758 const KCalCore::IncidenceBase::Ptr &);
759};
760
761/**
762 * Incidence serializer.
763 * Uses the virtual_hook internally to avoid slicing.
764 *
765 * // TODO_KDE5: Provide a virtual serialize() method, as done with assign() and equals().
766 *
767 * @since 4.12
768 */
769KCALCORE_EXPORT QDataStream &operator<<(QDataStream &out, const KCalCore::IncidenceBase::Ptr &);
770
771/**
772 * Incidence deserializer.
773 * Uses the virtual_hook internally to avoid slicing.
774 *
775 * // TODO_KDE5: Provide a virtual serialize() method, as done with assign() and equals().
776 *
777 * @since 4.12
778 */
779KCALCORE_EXPORT QDataStream &operator>>(QDataStream &in, const KCalCore::IncidenceBase::Ptr &);
780
781}
782
783Q_DECLARE_METATYPE(KCalCore::IncidenceBase *)
784Q_DECLARE_METATYPE(KCalCore::IncidenceBase::Ptr)
785
786#endif
787