diff options
Diffstat (limited to 'src/plugins/texteditor/basetextdocument.cpp')
-rw-r--r-- | src/plugins/texteditor/basetextdocument.cpp | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/src/plugins/texteditor/basetextdocument.cpp b/src/plugins/texteditor/basetextdocument.cpp new file mode 100644 index 0000000000..916bc11f2f --- /dev/null +++ b/src/plugins/texteditor/basetextdocument.cpp @@ -0,0 +1,349 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basetextdocument.h" +#include "basetexteditor.h" +#include "storagesettings.h" + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QTextCodec> +#include <QtGui/QMainWindow> +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QApplication> + +#ifndef TEXTEDITOR_STANDALONE +#include <utils/reloadpromptutils.h> +#include "coreplugin/icore.h" +#endif + +using namespace TextEditor; + +#if defined (Q_OS_WIN) +# define NATIVE_LINE_TERMINATOR CRLFLineTerminator +#else +# define NATIVE_LINE_TERMINATOR LFLineTerminator +#endif + +DocumentMarker::DocumentMarker(QTextDocument *doc) + : ITextMarkable(doc), document(doc) +{ +} + +BaseTextDocument::BaseTextDocument() + : m_document(new QTextDocument(this)), + m_highlighter(0) +{ + m_documentMarker = new DocumentMarker(m_document); + m_lineTerminatorMode = NativeLineTerminator; + m_isBinaryData = false; + m_codec = QTextCodec::codecForLocale(); + m_hasDecodingError = false; +} + +BaseTextDocument::~BaseTextDocument() +{ + QTextBlock block = m_document->begin(); + while (block.isValid()) { + if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) + data->documentClosing(); + block = block.next(); + } + delete m_document; + m_document = 0; +} + +QString BaseTextDocument::mimeType() const +{ + return m_mimeType; +} + +void BaseTextDocument::setMimeType(const QString &mt) +{ + m_mimeType = mt; +} + +bool BaseTextDocument::save(const QString &fileName) +{ + QTextCursor cursor(m_document); + + cursor.beginEditBlock(); + if (m_storageSettings.m_cleanWhitespace) + cleanWhitespace(cursor, m_storageSettings.m_inEntireDocument); + if (m_storageSettings.m_addFinalNewLine) + ensureFinalNewLine(cursor); + cursor.endEditBlock(); + + QString fName = m_fileName; + if (!fileName.isEmpty()) + fName = fileName; + + QFile file(fName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + return false; + + QString plainText = m_document->toPlainText(); + + if (m_lineTerminatorMode == CRLFLineTerminator) + plainText.replace(QLatin1Char('\n'), QLatin1String("\r\n")); + + file.write(m_codec->fromUnicode(plainText)); + if (!file.flush()) + return false; + file.close(); + + const QFileInfo fi(fName); + m_fileName = fi.absoluteFilePath(); + + m_document->setModified(false); + emit titleChanged(fi.fileName()); + emit changed(); + + m_isBinaryData = false; + m_hasDecodingError = false; + m_decodingErrorSample.clear(); + + return true; +} + +bool BaseTextDocument::isReadOnly() const +{ + if (m_isBinaryData || m_hasDecodingError) + return true; + if (m_fileName.isEmpty()) //have no corresponding file, so editing is ok + return false; + const QFileInfo fi(m_fileName); + return !fi.isWritable(); +} + +bool BaseTextDocument::isModified() const +{ + return m_document->isModified(); +} + +bool BaseTextDocument::open(const QString &fileName) +{ + QString title = tr("untitled"); + if (!fileName.isEmpty()) { + const QFileInfo fi(fileName); + m_fileName = fi.absoluteFilePath(); + + QFile file(fileName); + if (!file.exists()) + return false; + + if (!fi.isReadable()) + return false; + + if (!fi.isWritable()) { + if (!file.open(QIODevice::ReadOnly)) + return false; + } else { + if (!file.open(QIODevice::ReadWrite)) + return false; + } + title = fi.fileName(); + + QByteArray buf = file.readAll(); + int bytesRead = buf.size(); + + QTextCodec *codec = m_codec; + + // code taken from qtextstream + if (bytesRead >= 4 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe && uchar(buf[2]) == 0 && uchar(buf[3]) == 0) + || (uchar(buf[0]) == 0 && uchar(buf[1]) == 0 && uchar(buf[2]) == 0xfe && uchar(buf[3]) == 0xff))) { + codec = QTextCodec::codecForName("UTF-32"); + } else if (bytesRead >= 2 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe) + || (uchar(buf[0]) == 0xfe && uchar(buf[1]) == 0xff))) { + codec = QTextCodec::codecForName("UTF-16"); + } else if (!codec) { + codec = QTextCodec::codecForLocale(); + } + // end code taken from qtextstream + + m_codec = codec; + +#if 0 // should work, but does not, Qt bug with "system" codec + QTextDecoder *decoder = m_codec->makeDecoder(); + QString text = decoder->toUnicode(buf); + m_hasDecodingError = (decoder->hasFailure()); + delete decoder; +#else + QString text = m_codec->toUnicode(buf); + QByteArray verifyBuf = m_codec->fromUnicode(text); // slow + // the minSize trick lets us ignore unicode headers + int minSize = qMin(verifyBuf.size(), buf.size()); + m_hasDecodingError = (minSize < buf.size()- 4 + || memcmp(verifyBuf.constData() + verifyBuf.size() - minSize, + buf.constData() + buf.size() - minSize, minSize)); +#endif + + if (m_hasDecodingError) { + int p = buf.indexOf('\n', 16384); + if (p < 0) + m_decodingErrorSample = buf; + else + m_decodingErrorSample = buf.left(p); + } else { + m_decodingErrorSample.clear(); + } + + int lf = text.indexOf('\n'); + if (lf > 0 && text.at(lf-1) == QLatin1Char('\r')) { + m_lineTerminatorMode = CRLFLineTerminator; + } else if (lf >= 0) { + m_lineTerminatorMode = LFLineTerminator; + } else { + m_lineTerminatorMode = NativeLineTerminator; + } + + m_document->setModified(false); + m_document->setUndoRedoEnabled(false); + if (m_isBinaryData) + m_document->setHtml(tr("<em>Binary data</em>")); + else + m_document->setPlainText(text); + m_document->setUndoRedoEnabled(true); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(m_document->documentLayout()); + Q_ASSERT(documentLayout); + documentLayout->lastSaveRevision = 0; + m_document->setModified(false); + emit titleChanged(title); + emit changed(); + } + return true; +} + +void BaseTextDocument::reload(QTextCodec *codec) +{ + Q_ASSERT(codec); + m_codec = codec; + reload(); +} + +void BaseTextDocument::reload() +{ + emit aboutToReload(); + if (open(m_fileName)) { + emit reloaded(); + } +} + +void BaseTextDocument::modified(Core::IFile::ReloadBehavior *behavior) +{ + switch (*behavior) { + case Core::IFile::ReloadNone: + return; + case Core::IFile::ReloadAll: + reload(); + return; + case Core::IFile::ReloadPermissions: + emit changed(); + return; + case Core::IFile::AskForReload: + break; + } + +#ifndef TEXTEDITOR_STANDALONE + switch (Core::Utils::reloadPrompt(m_fileName, QApplication::activeWindow())) { + case Core::Utils::ReloadCurrent: + reload(); + break; + case Core::Utils::ReloadAll: + reload(); + *behavior = Core::IFile::ReloadAll; + break; + case Core::Utils::ReloadSkipCurrent: + break; + case Core::Utils::ReloadNone: + *behavior = Core::IFile::ReloadNone; + break; + } +#endif +} + +void BaseTextDocument::setSyntaxHighlighter(QSyntaxHighlighter *highlighter) +{ + if (m_highlighter) + delete m_highlighter; + m_highlighter = highlighter; + m_highlighter->setParent(this); + m_highlighter->setDocument(m_document); +} + +void BaseTextDocument::cleanWhitespace(QTextCursor& cursor, bool inEntireDocument) +{ + + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(m_document->documentLayout()); + + QTextBlock block = m_document->firstBlock(); + while (block.isValid()) { + + if (inEntireDocument || block.revision() > documentLayout->lastSaveRevision) { + + QString blockText = block.text(); + if (int trailing = m_tabSettings.trailingWhitespaces(blockText)) { + cursor.setPosition(block.position() + block.length() - 1); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, trailing); + cursor.removeSelectedText(); + } + if (!m_tabSettings.isIndentationClean(blockText)) { + cursor.setPosition(block.position()); + int firstNonSpace = m_tabSettings.firstNonSpace(blockText); + if (firstNonSpace == blockText.length()) { + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + } else { + int column = m_tabSettings.columnAt(blockText, firstNonSpace); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace); + QString indentationString = m_tabSettings.indentationString(0, column); + cursor.insertText(indentationString); + } + } + } + + block = block.next(); + } +} + +void BaseTextDocument::ensureFinalNewLine(QTextCursor& cursor) +{ + cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); + bool emptyFile = !cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + + if (!emptyFile && cursor.selectedText().at(0) != QChar::ParagraphSeparator) + { + cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); + cursor.insertText(QLatin1String("\n")); + } +} |