scide: implement selectionLength for openDocument
[supercollider.git] / editors / sc-ide / core / doc_manager.cpp
blob683dca1338dd704d8ef9a0114911a0820cbe3d41
1 /*
2 SuperCollider Qt IDE
3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "doc_manager.hpp"
22 #include "main.hpp"
23 #include "settings/manager.hpp"
24 #include "../../common/SC_TextUtils.hpp"
26 #include <QPlainTextDocumentLayout>
27 #include <QDebug>
28 #include <QDir>
29 #include <QFile>
30 #include <QMessageBox>
31 #include <QTextBlock>
32 #include <QApplication>
34 using namespace ScIDE;
36 Document::Document():
37 mId( QUuid::createUuid().toString().toAscii() ),
38 mDoc( new QTextDocument(this) ),
39 mTitle( "Untitled" ),
40 mIndentWidth(4)
42 mDoc->setDocumentLayout( new QPlainTextDocumentLayout(mDoc) );
44 new SyntaxHighlighter(mDoc);
46 connect( Main::instance(), SIGNAL(applySettingsRequest(Settings::Manager*)),
47 this, SLOT(applySettings(Settings::Manager*)) );
49 applySettings( Main::settings() );
52 void Document::applySettings( Settings::Manager *settings )
54 QFont font = settings->codeFont();
55 int indentWidth = settings->value("IDE/editor/indentWidth").toInt();
57 setDefaultFont(font);
58 setIndentWidth(indentWidth);
61 void Document::deleteTrailingSpaces()
63 QTextCursor cursor (textDocument());
64 cursor.beginEditBlock();
65 cursor.movePosition(QTextCursor::EndOfBlock);
66 QTextDocument * doc = textDocument();
68 while( !cursor.atEnd() ) {
69 while( (cursor.block().length() > 1) && doc->characterAt(cursor.position() - 1).isSpace())
70 cursor.deletePreviousChar();
72 cursor.movePosition(QTextCursor::NextBlock);
73 cursor.movePosition(QTextCursor::EndOfBlock);
75 cursor.endEditBlock();
78 void Document::setDefaultFont( const QFont & font )
80 mDoc->setDefaultFont( font );
81 // update tab stop, since it depends on font:
82 setIndentWidth( mIndentWidth );
83 emit defaultFontChanged();
86 void Document::resetDefaultFont()
88 Settings::Manager *settings = Main::settings();
89 setDefaultFont( settings->codeFont() );
92 void Document::setIndentWidth( int numSpaces )
94 mIndentWidth = numSpaces;
96 QFontMetricsF fontMetrics( mDoc->defaultFont() );
97 qreal tabStop = fontMetrics.width(' ') * numSpaces;
99 QTextOption options = mDoc->defaultTextOption();
100 options.setTabStop(tabStop);
101 mDoc->setDefaultTextOption(options);
105 DocumentManager::DocumentManager( Main *main, Settings::Manager * settings ):
106 QObject(main)
108 connect(&mFsWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onFileChanged(QString)));
110 connect(main, SIGNAL(storeSettingsRequest(Settings::Manager*)),
111 this, SLOT(storeSettings(Settings::Manager*)));
113 loadRecentDocuments( settings );
116 Document * DocumentManager::createDocument()
118 Document *doc = new Document();
119 mDocHash.insert( doc->id(), doc );
120 return doc;
123 void DocumentManager::create()
125 Document *doc = createDocument();
127 Q_EMIT( opened(doc, 0, 0) );
130 Document *DocumentManager::open( const QString & path, int initialCursorPosition, int selectionLength, bool toRecent )
132 QFileInfo info(path);
133 QString cpath = info.canonicalFilePath();
134 info.setFile(cpath);
136 if (cpath.isEmpty()) {
137 qWarning() << "DocumentManager: Can not open file: canonical path is empty.";
138 return 0;
141 // Check if file already opened
142 for( DocIterator it = mDocHash.begin(); it != mDocHash.end(); ++it ) {
143 Document *doc = it.value();
144 if(doc->mFilePath == cpath) {
145 Q_EMIT( showRequest(doc, initialCursorPosition) );
146 if (toRecent) addToRecent(doc);
147 return doc;
151 // Open the file
152 QFile file(cpath);
153 if(!file.open(QIODevice::ReadOnly)) {
154 qWarning() << "DocumentManager: the file" << cpath << "could not be opened for reading.";
155 return 0;
157 QByteArray bytes( file.readAll() );
158 file.close();
160 // strip .rtf
161 bool isRTF = false;
162 QString filePath = cpath;
163 if (info.suffix() == QString("rtf")) {
164 isRTF = true;
166 filePath += QString(".scd");
167 int result = rtf2txt(bytes.data());
168 bytes = bytes.left(result);
169 QMessageBox::warning(NULL, QString(tr("Opening RTF File")),
170 QString(tr("Warning: RTF file will be converted to plain-text scd file.")));
173 Document *doc = createDocument();
174 doc->mDoc->setPlainText( QString::fromUtf8( bytes.data(), bytes.size() ) );
175 doc->mDoc->setModified(false);
176 doc->mFilePath = filePath;
177 doc->mTitle = info.fileName();
179 mDocHash.insert( doc->id(), doc );
181 if (!isRTF)
182 mFsWatcher.addPath(cpath);
184 Q_EMIT( opened(doc, initialCursorPosition, selectionLength) );
186 if (toRecent) this->addToRecent(doc);
188 return doc;
191 bool DocumentManager::reload( Document *doc )
193 Q_ASSERT(doc);
195 if (doc->mFilePath.isEmpty())
196 return false;
198 QFile file(doc->mFilePath);
199 if(!file.open(QIODevice::ReadOnly)) {
200 qWarning() << "DocumentManager: the file" << doc->mFilePath << "could not be opened for reading.";
201 return false;
203 QByteArray bytes( file.readAll() );
204 file.close();
206 doc->mDoc->setPlainText( QString::fromUtf8( bytes.data(), bytes.size() ) );
207 doc->mDoc->setModified(false);
209 if (!mFsWatcher.files().contains(doc->mFilePath))
210 mFsWatcher.addPath(doc->mFilePath);
212 return true;
215 void DocumentManager::close( Document *doc )
217 Q_ASSERT(doc);
219 if( mDocHash.remove(doc->id()) == 0 ) {
220 qWarning("DocumentManager: trying to close an unmanaged document.");
221 return;
224 if (!doc->mFilePath.isEmpty())
225 mFsWatcher.removePath(doc->mFilePath);
227 Q_EMIT( closed(doc) );
228 delete doc;
231 bool DocumentManager::save( Document *doc )
233 Q_ASSERT(doc);
235 return doSaveAs( doc, doc->mFilePath );
238 bool DocumentManager::saveAs( Document *doc, const QString & path )
240 Q_ASSERT(doc);
242 if (path.isEmpty()) {
243 qWarning() << "DocumentManager: the saving path is empty.";
244 return false;
247 bool ok = doSaveAs( doc, path );
248 if (ok)
249 addToRecent(doc);
250 return ok;
253 bool DocumentManager::doSaveAs( Document *doc, const QString & path )
255 Q_ASSERT(doc);
257 doc->deleteTrailingSpaces();
259 QFile file(path);
260 if(!file.open(QIODevice::WriteOnly)) {
261 qWarning() << "DocumentManager: the file" << path << "could not be opened for writing.";
262 return false;
265 QString str = doc->textDocument()->toPlainText();
266 file.write(str.toUtf8());
267 file.close();
269 QFileInfo info(path);
270 QString cpath = info.canonicalFilePath();
272 doc->mFilePath = cpath;
273 doc->mTitle = info.fileName();
274 doc->mDoc->setModified(false);
275 doc->mSaveTime = info.lastModified();
277 // Always try to start watching, because the file could have been removed:
278 if (!mFsWatcher.files().contains(cpath))
279 mFsWatcher.addPath(cpath);
281 Q_EMIT(saved(doc));
283 return true;
286 void DocumentManager::onFileChanged( const QString & path )
288 DocIterator it;
289 for( it = mDocHash.begin(); it != mDocHash.end(); ++it )
291 Document *doc = it.value();
292 if (doc->mFilePath == path) {
293 QFileInfo info(doc->mFilePath);
294 if (doc->mSaveTime < info.lastModified()) {
295 doc->mDoc->setModified(true);
296 emit changedExternally(doc);
302 void DocumentManager::addToRecent( Document *doc )
304 const QString &path = doc->mFilePath;
305 int i = mRecent.indexOf(path);
306 if (i != -1)
307 mRecent.move( i, 0 );
308 else {
309 mRecent.prepend(path);
310 if (mRecent.count() > mMaxRecent)
311 mRecent.removeLast();
314 emit recentsChanged();
317 void DocumentManager::clearRecents()
319 mRecent.clear();
320 emit recentsChanged();
323 void DocumentManager::loadRecentDocuments( Settings::Manager *settings )
325 QVariantList list = settings->value("IDE/recentDocuments").value<QVariantList>();
326 mRecent.clear();
327 foreach (const QVariant & var, list)
328 mRecent << var.toString();
331 void DocumentManager::storeSettings( Settings::Manager *settings )
333 QVariantList list;
334 foreach (const QString & path, mRecent)
335 list << QVariant(path);
337 settings->setValue("IDE/recentDocuments", QVariant::fromValue<QVariantList>(list));