scide: DocManager - report warnings via the status bar
[supercollider.git] / editors / sc-ide / core / doc_manager.cpp
blobd26d9d7282d864f9345b483772ef3ced6e5afcbb
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 "main_window.hpp"
24 #include "settings/manager.hpp"
25 #include "../../common/SC_TextUtils.hpp"
27 #include <QPlainTextDocumentLayout>
28 #include <QDebug>
29 #include <QDir>
30 #include <QFile>
31 #include <QMessageBox>
32 #include <QTextBlock>
33 #include <QApplication>
35 using namespace ScIDE;
37 Document::Document():
38 mId( QUuid::createUuid().toString().toAscii() ),
39 mDoc( new QTextDocument(this) ),
40 mTitle( "Untitled" ),
41 mIndentWidth(4)
43 mDoc->setDocumentLayout( new QPlainTextDocumentLayout(mDoc) );
45 new SyntaxHighlighter(mDoc);
47 connect( Main::instance(), SIGNAL(applySettingsRequest(Settings::Manager*)),
48 this, SLOT(applySettings(Settings::Manager*)) );
50 applySettings( Main::settings() );
53 void Document::applySettings( Settings::Manager *settings )
55 QFont font = settings->codeFont();
56 int indentWidth = settings->value("IDE/editor/indentWidth").toInt();
58 setDefaultFont(font);
59 setIndentWidth(indentWidth);
62 void Document::deleteTrailingSpaces()
64 QTextCursor cursor (textDocument());
65 cursor.beginEditBlock();
66 cursor.movePosition(QTextCursor::EndOfBlock);
67 QTextDocument * doc = textDocument();
69 while( !cursor.atEnd() ) {
70 while( (cursor.block().length() > 1) && doc->characterAt(cursor.position() - 1).isSpace())
71 cursor.deletePreviousChar();
73 cursor.movePosition(QTextCursor::NextBlock);
74 cursor.movePosition(QTextCursor::EndOfBlock);
76 cursor.endEditBlock();
79 void Document::setDefaultFont( const QFont & font )
81 mDoc->setDefaultFont( font );
82 // update tab stop, since it depends on font:
83 setIndentWidth( mIndentWidth );
84 emit defaultFontChanged();
87 void Document::resetDefaultFont()
89 Settings::Manager *settings = Main::settings();
90 setDefaultFont( settings->codeFont() );
93 void Document::setIndentWidth( int numSpaces )
95 mIndentWidth = numSpaces;
97 QFontMetricsF fontMetrics( mDoc->defaultFont() );
98 qreal tabStop = fontMetrics.width(' ') * numSpaces;
100 QTextOption options = mDoc->defaultTextOption();
101 options.setTabStop(tabStop);
102 mDoc->setDefaultTextOption(options);
106 DocumentManager::DocumentManager( Main *main, Settings::Manager * settings ):
107 QObject(main)
109 connect(&mFsWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onFileChanged(QString)));
111 connect(main, SIGNAL(storeSettingsRequest(Settings::Manager*)),
112 this, SLOT(storeSettings(Settings::Manager*)));
114 loadRecentDocuments( settings );
117 Document * DocumentManager::createDocument()
119 Document *doc = new Document();
120 mDocHash.insert( doc->id(), doc );
121 return doc;
124 void DocumentManager::create()
126 Document *doc = createDocument();
128 Q_EMIT( opened(doc, 0, 0) );
131 Document *DocumentManager::open( const QString & path, int initialCursorPosition, int selectionLength, bool toRecent )
133 QFileInfo info(path);
134 QString cpath = info.canonicalFilePath();
135 info.setFile(cpath);
137 if (cpath.isEmpty()) {
138 MainWindow::instance()->showStatusMessage(QString("Cannot open file: %1 (file does not exist)").arg(path));
139 return 0;
142 // Check if file already opened
143 for( DocIterator it = mDocHash.begin(); it != mDocHash.end(); ++it ) {
144 Document *doc = it.value();
145 if(doc->mFilePath == cpath) {
146 Q_EMIT( showRequest(doc, initialCursorPosition) );
147 if (toRecent) addToRecent(doc);
148 return doc;
152 // Open the file
153 QFile file(cpath);
154 if(!file.open(QIODevice::ReadOnly)) {
155 MainWindow::instance()->showStatusMessage(QString("Cannot open file for reading: %1").arg(cpath));
156 return 0;
158 QByteArray bytes( file.readAll() );
159 file.close();
161 // strip .rtf
162 bool isRTF = false;
163 QString filePath = cpath;
164 if (info.suffix() == QString("rtf")) {
165 isRTF = true;
167 filePath += QString(".scd");
168 int result = rtf2txt(bytes.data());
169 bytes = bytes.left(result);
170 QMessageBox::warning(NULL, QString(tr("Opening RTF File")),
171 QString(tr("Warning: RTF file will be converted to plain-text scd file.")));
174 Document *doc = createDocument();
175 doc->mDoc->setPlainText( QString::fromUtf8( bytes.data(), bytes.size() ) );
176 doc->mDoc->setModified(false);
177 doc->mFilePath = filePath;
178 doc->mTitle = info.fileName();
180 mDocHash.insert( doc->id(), doc );
182 if (!isRTF)
183 mFsWatcher.addPath(cpath);
185 Q_EMIT( opened(doc, initialCursorPosition, selectionLength) );
187 if (toRecent) this->addToRecent(doc);
189 return doc;
192 bool DocumentManager::reload( Document *doc )
194 Q_ASSERT(doc);
196 if (doc->mFilePath.isEmpty())
197 return false;
199 QFile file(doc->mFilePath);
200 if(!file.open(QIODevice::ReadOnly)) {
201 MainWindow::instance()->showStatusMessage(QString("Cannot open file for reading: %1").arg(doc->mFilePath));
202 return false;
205 QByteArray bytes( file.readAll() );
206 file.close();
208 doc->mDoc->setPlainText( QString::fromUtf8( bytes.data(), bytes.size() ) );
209 doc->mDoc->setModified(false);
211 if (!mFsWatcher.files().contains(doc->mFilePath))
212 mFsWatcher.addPath(doc->mFilePath);
214 return true;
217 void DocumentManager::close( Document *doc )
219 Q_ASSERT(doc);
221 if( mDocHash.remove(doc->id()) == 0 ) {
222 qWarning("DocumentManager: trying to close an unmanaged document.");
223 return;
226 if (!doc->mFilePath.isEmpty())
227 mFsWatcher.removePath(doc->mFilePath);
229 Q_EMIT( closed(doc) );
230 delete doc;
233 bool DocumentManager::save( Document *doc )
235 Q_ASSERT(doc);
237 return doSaveAs( doc, doc->mFilePath );
240 bool DocumentManager::saveAs( Document *doc, const QString & path )
242 Q_ASSERT(doc);
244 if (path.isEmpty()) {
245 qWarning() << "DocumentManager: the saving path is empty.";
246 return false;
249 bool ok = doSaveAs( doc, path );
250 if (ok)
251 addToRecent(doc);
252 return ok;
255 bool DocumentManager::doSaveAs( Document *doc, const QString & path )
257 Q_ASSERT(doc);
259 doc->deleteTrailingSpaces();
261 QFile file(path);
262 if(!file.open(QIODevice::WriteOnly)) {
263 qWarning() << "DocumentManager: the file" << path << "could not be opened for writing.";
264 return false;
267 QString str = doc->textDocument()->toPlainText();
268 file.write(str.toUtf8());
269 file.close();
271 QFileInfo info(path);
272 QString cpath = info.canonicalFilePath();
274 doc->mFilePath = cpath;
275 doc->mTitle = info.fileName();
276 doc->mDoc->setModified(false);
277 doc->mSaveTime = info.lastModified();
279 // Always try to start watching, because the file could have been removed:
280 if (!mFsWatcher.files().contains(cpath))
281 mFsWatcher.addPath(cpath);
283 Q_EMIT(saved(doc));
285 return true;
288 void DocumentManager::onFileChanged( const QString & path )
290 DocIterator it;
291 for( it = mDocHash.begin(); it != mDocHash.end(); ++it )
293 Document *doc = it.value();
294 if (doc->mFilePath == path) {
295 QFileInfo info(doc->mFilePath);
296 if (doc->mSaveTime < info.lastModified()) {
297 doc->mDoc->setModified(true);
298 emit changedExternally(doc);
304 void DocumentManager::addToRecent( Document *doc )
306 const QString &path = doc->mFilePath;
307 int i = mRecent.indexOf(path);
308 if (i != -1)
309 mRecent.move( i, 0 );
310 else {
311 mRecent.prepend(path);
312 if (mRecent.count() > mMaxRecent)
313 mRecent.removeLast();
316 emit recentsChanged();
319 void DocumentManager::clearRecents()
321 mRecent.clear();
322 emit recentsChanged();
325 void DocumentManager::loadRecentDocuments( Settings::Manager *settings )
327 QVariantList list = settings->value("IDE/recentDocuments").value<QVariantList>();
328 mRecent.clear();
329 foreach (const QVariant & var, list)
330 mRecent << var.toString();
333 void DocumentManager::storeSettings( Settings::Manager *settings )
335 QVariantList list;
336 foreach (const QString & path, mRecent)
337 list << QVariant(path);
339 settings->setValue("IDE/recentDocuments", QVariant::fromValue<QVariantList>(list));