1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "idocument.h"
27
28 #include <utils/fileutils.h>
29 #include <utils/infobar.h>
30 #include <utils/optional.h>
31 #include <utils/qtcassert.h>
32
33 #include <QFile>
34 #include <QFileInfo>
35
36 /*!
37 \class Core::IDocument
38 \inheaderfile coreplugin/idocument.h
39 \inmodule QtCreator
40
41 \brief The IDocument class describes a document that can be saved and
42 reloaded.
43
44 The class has two use cases.
45
46 \section1 Handling External Modifications
47
48 You can implement IDocument and register instances in DocumentManager to
49 let it handle external modifications of a file. When the file specified with
50 filePath() has changed externally, the DocumentManager asks the
51 corresponding IDocument instance what to do via reloadBehavior(). If that
52 returns \c IDocument::BehaviorAsk, the user is asked if the file should be
53 reloaded from disk. If the user requests the reload or reloadBehavior()
54 returns \c IDocument::BehaviorSilent, the DocumentManager calls reload()
55 to initiate a reload of the file from disk.
56
57 Core functions: setFilePath(), reload(), reloadBehavior().
58
59 If the content of the document can change in \QC, diverging from the
60 content on disk: isModified(), save(), isSaveAsAllowed(),
61 fallbackSaveAsPath(), fallbackSaveAsFileName().
62
63 \section1 Editor Document
64
65 The most common use case for implementing an IDocument subclass is as a
66 document for an IEditor implementation. Multiple editor instances can work
67 on the same document instance, for example if the document is visible in
68 multiple splits simultaneously. So the IDocument subclass should hold all
69 data that is independent from the specific IEditor instance, for example
70 the content and highlighting information.
71
72 Each IDocument subclass is only required to work with the corresponding
73 IEditor subclasses that it was designed to work with.
74
75 An IDocument can either be backed by a file, or solely represent some data
76 in memory. Documents backed by a file are automatically added to the
77 DocumentManager by the EditorManager.
78
79 Core functions: setId(), isModified(), contents(), setContents().
80
81 If the content of the document is backed by a file: open(), save(),
82 setFilePath(), mimeType(), shouldAutoSave(), setSuspendAllowed(), and
83 everything from \l{Handling External Modifications}.
84
85 If the content of the document is not backed by a file:
86 setPreferredDisplayName(), setTemporary().
87
88 \ingroup mainclasses
89 */
90
91 /*!
92 \enum IDocument::OpenResult
93
94 The OpenResult enum describes whether a file was successfully opened.
95
96 \value Success
97 The file was read successfully and can be handled by this document
98 type.
99 \value ReadError
100 The file could not be opened for reading, either because it does not
101 exist or because of missing permissions.
102 \value CannotHandle
103 This document type could not handle the file content.
104 */
105
106 /*!
107 \enum IDocument::ReloadSetting
108
109 \internal
110 */
111
112 /*!
113 \enum IDocument::ChangeTrigger
114
115 The ChangeTrigger enum describes whether a file was changed from \QC
116 internally or from the outside.
117
118 \value TriggerInternal
119 The file was changed by \QC.
120 \value TriggerExternal
121 The file was changed from the outside.
122
123 \sa IDocument::reloadBehavior()
124 */
125
126 /*!
127 \enum IDocument::ChangeType
128
129 The ChangeType enum describes the way in which the file changed.
130
131 \value TypeContents
132 The contents of the file changed.
133 \value TypeRemoved
134 The file was removed.
135
136 \sa IDocument::reloadBehavior()
137 \sa IDocument::reload()
138 */
139
140 /*!
141 \enum IDocument::ReloadFlag
142
143 The ReloadFlag enum describes if a file should be reloaded from disk.
144
145 \value FlagReload
146 The file should be reloaded.
147 \value FlagIgnore
148 The file should not be reloaded, but the document state should
149 reflect the change.
150
151 \sa IDocument::reload()
152 */
153
154 /*!
155 \fn Core::IDocument::changed()
156
157 This signal is emitted when the document's meta data, like file name or
158 modified state, changes.
159
160 \sa isModified()
161 \sa filePath()
162 \sa displayName()
163 */
164
165 /*!
166 \fn Core::IDocument::contentsChanged()
167
168 This signal is emitted when the document's content changes.
169
170 \sa contents()
171 */
172
173 /*!
174 \fn Core::IDocument::mimeTypeChanged()
175
176 This signal is emitted when the document content's MIME type changes.
177
178 \sa mimeType()
179 */
180
181 /*!
182 \fn Core::IDocument::aboutToReload()
183
184 This signal is emitted before the document is reloaded from the backing
185 file.
186
187 \sa reload()
188 */
189
190 /*!
191 \fn Core::IDocument::reloadFinished(bool success)
192
193 This signal is emitted after the document is reloaded from the backing
194 file, or if reloading failed.
195
196 The success state is passed in \a success.
197
198 \sa reload()
199 */
200
201 /*!
202 \fn Core::IDocument::filePathChanged(const Utils::FilePath &oldName, const Utils::FilePath &newName)
203
204 This signal is emitted after the file path changed from \a oldName to \a
205 newName.
206
207 \sa filePath()
208 */
209
210 using namespace Utils;
211
212 namespace Core {
213 namespace Internal {
214
215 class IDocumentPrivate
216 {
217 public:
~IDocumentPrivate()218 ~IDocumentPrivate()
219 {
220 delete infoBar;
221 }
222
223 QString mimeType;
224 Utils::FilePath filePath;
225 QString preferredDisplayName;
226 QString uniqueDisplayName;
227 Utils::FilePath autoSavePath;
228 Utils::InfoBar *infoBar = nullptr;
229 Id id;
230 optional<bool> fileIsReadOnly;
231 bool temporary = false;
232 bool hasWriteWarning = false;
233 bool restored = false;
234 bool isSuspendAllowed = false;
235 };
236
237 } // namespace Internal
238
239 /*!
240 Creates an IDocument with \a parent.
241
242 \note Using the \a parent for ownership of the document is generally a
243 bad idea if the IDocument is intended for use with IEditor. It is better to
244 use shared ownership in that case.
245 */
IDocument(QObject * parent)246 IDocument::IDocument(QObject *parent) : QObject(parent),
247 d(new Internal::IDocumentPrivate)
248 {
249 }
250
251 /*!
252 Destroys the IDocument.
253 If there was an auto save file for this document, it is removed.
254
255 \sa shouldAutoSave()
256 */
~IDocument()257 IDocument::~IDocument()
258 {
259 removeAutoSaveFile();
260 delete d;
261 }
262
263 /*!
264 \fn void IDocument::setId(Utils::Id id)
265
266 Sets the ID for this document type to \a id. This is coupled with the
267 corresponding IEditor implementation and the \l{IEditorFactory::id()}{id()}
268 of the IEditorFactory. If the IDocument implementation only works with a
269 single IEditor type, this is preferably set in the IDocuments's
270 constructor.
271
272 \sa id()
273 */
setId(Id id)274 void IDocument::setId(Id id)
275 {
276 d->id = id;
277 }
278
279 /*!
280 Returns the ID for this document type.
281
282 \sa setId()
283 */
id() const284 Id IDocument::id() const
285 {
286 QTC_CHECK(d->id.isValid());
287 return d->id;
288 }
289
290 /*!
291 The open() method is used to load the contents of a file when a document is
292 opened in an editor.
293
294 If the document is opened from an auto save file, \a realFilePath is the
295 name of the auto save file that should be loaded, and \a filePath is the
296 file name of the resulting file. In that case, the contents of the auto
297 save file should be loaded, the file name of the IDocument should be set to
298 \a filePath, and the document state be set to modified.
299
300 If the editor is opened from a regular file, \a filePath and \a
301 filePath are the same.
302
303 Use \a errorString to return an error message if this document cannot
304 handle the file contents.
305
306 Returns whether the file was opened and read successfully.
307
308 The default implementation does nothing and returns
309 CannotHandle.
310
311 \sa EditorManager::openEditor()
312 \sa shouldAutoSave()
313 \sa setFilePath()
314 */
open(QString * errorString,const Utils::FilePath & filePath,const Utils::FilePath & realFilePath)315 IDocument::OpenResult IDocument::open(QString *errorString, const Utils::FilePath &filePath, const Utils::FilePath &realFilePath)
316 {
317 Q_UNUSED(errorString)
318 Q_UNUSED(filePath)
319 Q_UNUSED(realFilePath)
320 return OpenResult::CannotHandle;
321 }
322
323 /*!
324 Saves the contents of the document to the \a filePath on disk.
325
326 If \a autoSave is \c true, the saving is done for an auto-save, so the
327 document should avoid cleanups or other operations that it does for
328 user-requested saves.
329
330 Use \a errorString to return an error message if saving failed.
331
332 Returns whether saving was successful.
333
334 The default implementation does nothing and returns \c false.
335
336 \sa shouldAutoSave()
337 */
save(QString * errorString,const Utils::FilePath & filePath,bool autoSave)338 bool IDocument::save(QString *errorString, const Utils::FilePath &filePath, bool autoSave)
339 {
340 Q_UNUSED(errorString)
341 Q_UNUSED(filePath)
342 Q_UNUSED(autoSave)
343 return false;
344 }
345
346 /*!
347 Returns the current contents of the document. The default implementation
348 returns an empty QByteArray.
349
350 \sa setContents()
351 \sa contentsChanged()
352 */
contents() const353 QByteArray IDocument::contents() const
354 {
355 return QByteArray();
356 }
357
358 /*!
359 The setContents() method is for example used by
360 EditorManager::openEditorWithContents() to set the \a contents of this
361 document.
362
363 Returns whether setting the contents was successful.
364
365 The default implementation does nothing and returns false.
366
367 \sa contents()
368 \sa EditorManager::openEditorWithContents()
369 */
setContents(const QByteArray & contents)370 bool IDocument::setContents(const QByteArray &contents)
371 {
372 Q_UNUSED(contents)
373 return false;
374 }
375
376 /*!
377 Returns the absolute path of the file that this document refers to. May be
378 empty for documents that are not backed by a file.
379
380 \sa setFilePath()
381 */
filePath() const382 const Utils::FilePath &IDocument::filePath() const
383 {
384 return d->filePath;
385 }
386
387 /*!
388 The reloadBehavior() method is used by the DocumentManager to ask what to
389 do if the file backing this document has changed on disk.
390
391 The \a trigger specifies if the change was triggered by some operation in
392 \QC. The \a type specifies if the file changed permissions or contents, or
393 was removed completely.
394
395 Returns whether the user should be asked or the document should be
396 reloaded silently.
397
398 The default implementation requests a silent reload if only the permissions
399 changed or if the contents have changed but the \a trigger is internal and
400 the document is not modified.
401
402 \sa isModified()
403 */
reloadBehavior(ChangeTrigger trigger,ChangeType type) const404 IDocument::ReloadBehavior IDocument::reloadBehavior(ChangeTrigger trigger, ChangeType type) const
405 {
406 if (type == TypeContents && trigger == TriggerInternal && !isModified())
407 return BehaviorSilent;
408 return BehaviorAsk;
409 }
410
411 /*!
412 Reloads the document from the backing file when that changed on disk.
413
414 If \a flag is FlagIgnore the file should not actually be loaded, but the
415 document should reflect the change in its \l{isModified()}{modified state}.
416
417 The \a type specifies whether only the file permissions changed or if the
418 contents of the file changed.
419
420 Use \a errorString to return an error message, if this document cannot
421 handle the file contents.
422
423 Returns if the file was reloaded successfully.
424
425 The default implementation does nothing and returns \c true.
426
427 Subclasses should emit aboutToReload() before, and reloadFinished() after
428 reloading the file.
429
430 \sa isModified()
431 \sa aboutToReload()
432 \sa reloadFinished()
433 \sa changed()
434 */
reload(QString * errorString,ReloadFlag flag,ChangeType type)435 bool IDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type)
436 {
437 Q_UNUSED(errorString)
438 Q_UNUSED(flag)
439 Q_UNUSED(type)
440 return true;
441 }
442
443 /*!
444 Updates the cached information about the read-only status of the backing file.
445 */
checkPermissions()446 void IDocument::checkPermissions()
447 {
448 bool previousReadOnly = d->fileIsReadOnly.value_or(false);
449 if (!filePath().isEmpty()) {
450 d->fileIsReadOnly = !filePath().toFileInfo().isWritable();
451 } else {
452 d->fileIsReadOnly = false;
453 }
454 if (previousReadOnly != *(d->fileIsReadOnly))
455 emit changed();
456 }
457
458 /*!
459 Returns whether the document should automatically be saved at a user-defined
460 interval.
461
462 The default implementation returns \c false.
463 */
shouldAutoSave() const464 bool IDocument::shouldAutoSave() const
465 {
466 return false;
467 }
468
469 /*!
470 Returns whether the document has been modified after it was loaded from a
471 file.
472
473 The default implementation returns \c false. Re-implementations should emit
474 changed() when this property changes.
475
476 \sa changed()
477 */
isModified() const478 bool IDocument::isModified() const
479 {
480 return false;
481 }
482
483 /*!
484 Returns whether the document may be saved under a different file name.
485
486 The default implementation returns \c false.
487
488 \sa save()
489 */
isSaveAsAllowed() const490 bool IDocument::isSaveAsAllowed() const
491 {
492 return false;
493 }
494
495 /*!
496 Returns whether the document may be suspended.
497
498 The EditorManager can automatically suspend editors and its corresponding
499 documents if the document is backed by a file, is not modified, and is not
500 temporary. Suspended IEditor and IDocument instances are deleted and
501 removed from memory, but are still visually accessible as if the document
502 was still opened in \QC.
503
504 The default is \c false.
505
506 \sa setSuspendAllowed()
507 \sa isModified()
508 \sa isTemporary()
509 */
isSuspendAllowed() const510 bool IDocument::isSuspendAllowed() const
511 {
512 return d->isSuspendAllowed;
513 }
514
515 /*!
516 Sets whether the document may be suspended to \a value.
517
518 \sa isSuspendAllowed()
519 */
setSuspendAllowed(bool value)520 void IDocument::setSuspendAllowed(bool value)
521 {
522 d->isSuspendAllowed = value;
523 }
524
525 /*!
526 Returns whether the file backing this document is read-only, or \c false if
527 the document is not backed by a file.
528 */
isFileReadOnly() const529 bool IDocument::isFileReadOnly() const
530 {
531 if (filePath().isEmpty())
532 return false;
533 if (!d->fileIsReadOnly)
534 const_cast<IDocument *>(this)->checkPermissions();
535 return d->fileIsReadOnly.value_or(false);
536 }
537
538 /*!
539 Returns if the document is temporary, and should for example not be
540 considered when saving or restoring the session state, or added to the recent
541 files list.
542
543 The default is \c false.
544
545 \sa setTemporary()
546 */
isTemporary() const547 bool IDocument::isTemporary() const
548 {
549 return d->temporary;
550 }
551
552 /*!
553 Sets whether the document is \a temporary.
554
555 \sa isTemporary()
556 */
setTemporary(bool temporary)557 void IDocument::setTemporary(bool temporary)
558 {
559 d->temporary = temporary;
560 }
561
562 /*!
563 Returns a path to use for the \uicontrol{Save As} file dialog in case the
564 document is not backed by a file.
565
566 \sa fallbackSaveAsFileName()
567 */
fallbackSaveAsPath() const568 QString IDocument::fallbackSaveAsPath() const
569 {
570 return QString();
571 }
572
573 /*!
574 Returns a file name to use for the \uicontrol{Save As} file dialog in case
575 the document is not backed by a file.
576
577 \sa fallbackSaveAsPath()
578 */
fallbackSaveAsFileName() const579 QString IDocument::fallbackSaveAsFileName() const
580 {
581 return QString();
582 }
583
584 /*!
585 Returns the MIME type of the document content, if applicable.
586
587 Subclasses should set this with setMimeType() after setting or loading
588 content.
589
590 The default MIME type is empty.
591
592 \sa setMimeType()
593 \sa mimeTypeChanged()
594 */
mimeType() const595 QString IDocument::mimeType() const
596 {
597 return d->mimeType;
598 }
599
600 /*!
601 Sets the MIME type of the document content to \a mimeType.
602
603 \sa mimeType()
604 */
setMimeType(const QString & mimeType)605 void IDocument::setMimeType(const QString &mimeType)
606 {
607 if (d->mimeType != mimeType) {
608 d->mimeType = mimeType;
609 emit mimeTypeChanged();
610 }
611 }
612
613 /*!
614 \internal
615 */
autoSave(QString * errorString,const FilePath & filePath)616 bool IDocument::autoSave(QString *errorString, const FilePath &filePath)
617 {
618 if (!save(errorString, filePath, true))
619 return false;
620 d->autoSavePath = filePath;
621 return true;
622 }
623
624 static const char kRestoredAutoSave[] = "RestoredAutoSave";
625
626 /*!
627 \internal
628 */
setRestoredFrom(const Utils::FilePath & path)629 void IDocument::setRestoredFrom(const Utils::FilePath &path)
630 {
631 d->autoSavePath = path;
632 d->restored = true;
633 Utils::InfoBarEntry info(Id(kRestoredAutoSave),
634 tr("File was restored from auto-saved copy. "
635 "Select Save to confirm or Revert to Saved to discard changes."));
636 infoBar()->addInfo(info);
637 }
638
639 /*!
640 \internal
641 */
removeAutoSaveFile()642 void IDocument::removeAutoSaveFile()
643 {
644 if (!d->autoSavePath.isEmpty()) {
645 QFile::remove(d->autoSavePath.toString());
646 d->autoSavePath.clear();
647 if (d->restored) {
648 d->restored = false;
649 infoBar()->removeInfo(Id(kRestoredAutoSave));
650 }
651 }
652 }
653
654 /*!
655 \internal
656 */
hasWriteWarning() const657 bool IDocument::hasWriteWarning() const
658 {
659 return d->hasWriteWarning;
660 }
661
662 /*!
663 \internal
664 */
setWriteWarning(bool has)665 void IDocument::setWriteWarning(bool has)
666 {
667 d->hasWriteWarning = has;
668 }
669
670 /*!
671 Returns the document's Utils::InfoBar, which is shown at the top of an
672 editor.
673 */
infoBar()674 Utils::InfoBar *IDocument::infoBar()
675 {
676 if (!d->infoBar)
677 d->infoBar = new Utils::InfoBar;
678 return d->infoBar;
679 }
680
681 /*!
682 Sets the absolute \a filePath of the file that backs this document. The
683 default implementation sets the file name and sends the filePathChanged() and
684 changed() signals.
685
686 \sa filePath()
687 \sa filePathChanged()
688 \sa changed()
689 */
setFilePath(const Utils::FilePath & filePath)690 void IDocument::setFilePath(const Utils::FilePath &filePath)
691 {
692 if (d->filePath == filePath)
693 return;
694 Utils::FilePath oldName = d->filePath;
695 d->filePath = filePath;
696 emit filePathChanged(oldName, d->filePath);
697 emit changed();
698 }
699
700 /*!
701 Returns the string to display for this document, for example in the
702 \uicontrol{Open Documents} view and the documents drop down.
703
704 The display name is one of the following, in order:
705
706 \list 1
707 \li Unique display name set by the document model
708 \li Preferred display name set by the owner
709 \li Base name of the document's file name
710 \endlist
711
712 \sa setPreferredDisplayName()
713 \sa filePath()
714 \sa changed()
715 */
displayName() const716 QString IDocument::displayName() const
717 {
718 return d->uniqueDisplayName.isEmpty() ? plainDisplayName() : d->uniqueDisplayName;
719 }
720
721 /*!
722 Sets the preferred display \a name for this document.
723
724 \sa preferredDisplayName()
725 \sa displayName()
726 */
setPreferredDisplayName(const QString & name)727 void IDocument::setPreferredDisplayName(const QString &name)
728 {
729 if (name == d->preferredDisplayName)
730 return;
731 d->preferredDisplayName = name;
732 emit changed();
733 }
734
735 /*!
736 Returns the preferred display name for this document.
737
738 The default preferred display name is empty, which means that the display
739 name is preferably the file name of the file backing this document.
740
741 \sa setPreferredDisplayName()
742 \sa displayName()
743 */
preferredDisplayName() const744 QString IDocument::preferredDisplayName() const
745 {
746 return d->preferredDisplayName;
747 }
748
749 /*!
750 \internal
751 Returns displayName without disambiguation.
752 */
plainDisplayName() const753 QString IDocument::plainDisplayName() const
754 {
755 return d->preferredDisplayName.isEmpty() ? d->filePath.fileName() : d->preferredDisplayName;
756 }
757
758 /*!
759 \internal
760 Sets unique display name for the document. Used by the document model.
761 */
setUniqueDisplayName(const QString & name)762 void IDocument::setUniqueDisplayName(const QString &name)
763 {
764 d->uniqueDisplayName = name;
765 }
766
767 /*!
768 \internal
769 */
uniqueDisplayName() const770 QString IDocument::uniqueDisplayName() const
771 {
772 return d->uniqueDisplayName;
773 }
774
775 } // namespace Core
776