+// Copyright (c) 2012 Dennco Project
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+//
+// Created by tkawata on Dec-15, 2012.
+//
+#include "dccodeeditorscriptmanager.h"
+
+#include <QMap>
+#include <QCryptographicHash>
+#include <QDateTime>
+#include <QMutex>
+
+#include "dccell.h"
+#include "dccellcode.h"
+#include "dceditscriptfolder.h"
+
+DCCodeEditorScriptManager* DCCodeEditorScriptManager::instance = NULL;
+
+class ScriptWatcher
+{
+ bool d_scriptLoadedFromFile;
+ void setModified(DCCodeEditorScriptManager::Attacher *modifier, bool modified)
+ {
+ if (d_modified != modified)
+ {
+ d_modified = modified;
+ d_manager->callbackModificationStatusChanged(this, modifier, modified);
+ }
+ }
+ QMutex d_mutex;
+
+public:
+ enum E_TYPE { E_CUSTOMSCRIPT, E_CELLCODESCRIPT};
+
+protected:
+ DCCodeEditorScriptManager *d_manager;
+ E_TYPE d_type;
+ QString d_script;
+ qint64 d_loadedTime;
+ QByteArray d_hashForOriginalScript;
+ bool d_modified;
+ bool d_fileModifiedByExternalEditor;
+ QList<DCCodeEditorScriptManager::Attacher*> d_attachers;
+
+ ScriptWatcher(DCCodeEditorScriptManager *manager, E_TYPE type) :
+ d_scriptLoadedFromFile(false), d_manager(manager), d_type(type), d_loadedTime(0),
+ d_modified(false), d_fileModifiedByExternalEditor(false)
+ {
+ }
+
+ virtual QString loadScriptFromFile() = 0;
+ virtual bool saveScriptToFile() = 0;
+
+public:
+ virtual ~ScriptWatcher() {}
+
+ virtual QString getWorkFilePathScript() = 0;
+
+ E_TYPE getType() const { return d_type; }
+ void attach(DCCodeEditorScriptManager::Attacher *attacher)
+ {
+ d_attachers.append(attacher);
+ }
+
+ void deattach(DCCodeEditorScriptManager::Attacher *attacher)
+ {
+ d_attachers.removeOne(attacher);
+ }
+
+ int getRefCount() const
+ {
+ return d_attachers.count();
+ }
+
+ const QList<DCCodeEditorScriptManager::Attacher*>* getAttachers() { return &d_attachers; }
+
+ QString getCurrentScript(DCCodeEditorScriptManager::Attacher *attacher, bool forceReload = false)
+ {
+ if (!d_scriptLoadedFromFile || forceReload)
+ {
+ d_hashForOriginalScript.clear();
+ d_script = loadScriptFromFile();
+ d_loadedTime = QDateTime::currentMSecsSinceEpoch();
+ d_hashForOriginalScript = QCryptographicHash::hash(d_script.toLocal8Bit(), QCryptographicHash::Md5);
+ setModified(attacher,false);
+ d_scriptLoadedFromFile = true;
+ d_fileModifiedByExternalEditor = false;
+ }
+ return d_script;
+ }
+
+ bool getIsModified(const DCCodeEditorScriptManager::Attacher *attacher) const
+ {
+ return d_modified;
+ }
+
+ qint64 getLoadedTime() const
+ {
+ return d_loadedTime;
+ }
+
+ void setScript(DCCodeEditorScriptManager::Attacher *attacher, const QString& newScript)
+ {
+ d_script = newScript;
+ d_manager->callbackScriptChanged(this, attacher);
+ setModified(attacher, d_hashForOriginalScript != QCryptographicHash::hash(newScript.toLocal8Bit(), QCryptographicHash::Md5));
+ }
+
+ bool saveScript(DCCodeEditorScriptManager::Attacher *attacher)
+ {
+ bool succeeded = saveScriptToFile();
+ if (succeeded)
+ {
+ d_loadedTime = QDateTime::currentMSecsSinceEpoch();
+ d_fileModifiedByExternalEditor = false;
+ d_hashForOriginalScript = QCryptographicHash::hash(d_script.toLocal8Bit(), QCryptographicHash::Md5);
+ setModified(attacher,false);
+ }
+ return succeeded;
+ }
+
+ bool checkIfModifiedByExternalEditor()
+ {
+ QMutexLocker locker(&d_mutex);
+
+ if (!d_fileModifiedByExternalEditor)
+ {
+ d_fileModifiedByExternalEditor = d_hashForOriginalScript != QCryptographicHash::hash(loadScriptFromFile().toLocal8Bit(), QCryptographicHash::Md5);
+ }
+ return d_fileModifiedByExternalEditor;
+ }
+
+ bool getIsFileModifiedByExternalEditor() const
+ {
+ return d_fileModifiedByExternalEditor;
+ }
+};
+
+class CustomScriptWatcher : public ScriptWatcher
+{
+ DCCell *d_cell;
+
+
+protected:
+ virtual QString loadScriptFromFile()
+ {
+ return d_cell->getCustomScript();
+ }
+
+ virtual bool saveScriptToFile()
+ {
+ return d_cell->saveCustomScript(d_script);
+ }
+
+public:
+ CustomScriptWatcher(DCCodeEditorScriptManager *manager, DCCell *cell)
+ : ScriptWatcher(manager, E_CUSTOMSCRIPT), d_cell(cell)
+ {
+ }
+
+ virtual QString getWorkFilePathScript()
+ {
+ return d_cell->getWorkFilePathForCustomScript();
+ }
+};
+
+class CellCodeScriptWatcher : public ScriptWatcher
+{
+ DCCellCode *d_cellCode;
+
+protected:
+ virtual QString loadScriptFromFile()
+ {
+ return d_cellCode->getOwnScript();
+ }
+
+ virtual bool saveScriptToFile()
+ {
+ return d_cellCode->saveScript(d_script);
+ }
+
+public:
+ CellCodeScriptWatcher(DCCodeEditorScriptManager *manager, DCCellCode *cellCode)
+ : ScriptWatcher(manager, E_CELLCODESCRIPT), d_cellCode(cellCode)
+ {
+ }
+
+ virtual QString getWorkFilePathScript()
+ {
+ return d_cellCode->getWorkFilePathForCellCodeScript();
+ }
+};
+
+DCCodeEditorScriptManager::Attacher::Attacher(DCEditScriptFolder *folder, DCCodeEditorScriptManager *manager, ScriptWatcher *watcher)
+ : d_folder(folder), d_manager(manager), d_watcher(watcher)
+{
+}
+
+DCCodeEditorScriptManager::Attacher::~Attacher()
+{
+ d_manager->attacherDeleting(this);
+}
+
+ScriptWatcher* DCCodeEditorScriptManager::Attacher::getWatcher() const
+{
+ return d_watcher;
+}
+
+QString DCCodeEditorScriptManager::Attacher::getCurrentScript(bool forceReload)
+{
+ return d_manager->getCurrentScript(this, forceReload);
+}
+
+bool DCCodeEditorScriptManager::Attacher::getIsModified() const
+{
+ return d_manager->getIsModified(this);
+}
+
+bool DCCodeEditorScriptManager::Attacher::getIsFileModifiedByExternalEditor() const
+{
+ return d_watcher->getIsFileModifiedByExternalEditor();
+}
+
+qint64 DCCodeEditorScriptManager::Attacher::getLoadedTime() const
+{
+ return d_watcher->getLoadedTime();
+}
+
+void DCCodeEditorScriptManager::Attacher::setScript(const QString& newScript)
+{
+ d_manager->setScript(this, newScript);
+}
+
+bool DCCodeEditorScriptManager::Attacher::saveScript()
+{
+ return d_manager->saveScript(this);
+}
+
+void DCCodeEditorScriptManager::Attacher::callbackScriptChanged()
+{
+ d_folder->callbackScriptChanged();
+}
+
+void DCCodeEditorScriptManager::Attacher::callbackModificationStatusChanged(bool modified)
+{
+ d_folder->callbackModificationStatusChanged(modified);
+}
+
+void DCCodeEditorScriptManager::Attacher::callbackFileModifiedByExternalEditor(qint64 notifiedTime)
+{
+ d_folder->callbackFileModifiedByExternalEditor(notifiedTime);
+
+}
+
+//static
+DCCodeEditorScriptManager::Attacher* DCCodeEditorScriptManager::createCustomScriptAttacher(DCEditScriptFolder *folder, DCCell *cell)
+{
+ DCCodeEditorScriptManager *instance = getInstance();
+ return instance->createCustomScriptAttacherPrivate(folder, cell);
+}
+
+//static
+DCCodeEditorScriptManager::Attacher* DCCodeEditorScriptManager::createCellCodeScriptAttacher(DCEditScriptFolder *folder, DCCellCode *cell)
+{
+ DCCodeEditorScriptManager *instance = getInstance();
+ return instance->createCellCodeScriptAttacherPrivate(folder, cell);
+}
+
+QString DCCodeEditorScriptManager::getCurrentScript(DCCodeEditorScriptManager::Attacher *attacher, bool forceReload)
+{
+ return attacher->getWatcher()->getCurrentScript(attacher,forceReload);
+}
+
+bool DCCodeEditorScriptManager::getIsModified(const DCCodeEditorScriptManager::Attacher *attacher) const
+{
+ return attacher->getWatcher()->getIsModified(attacher);
+}
+
+void DCCodeEditorScriptManager::setScript(DCCodeEditorScriptManager::Attacher *attacher, const QString& newScript)
+{
+ attacher->getWatcher()->setScript(attacher, newScript);
+}
+
+bool DCCodeEditorScriptManager::saveScript(DCCodeEditorScriptManager::Attacher *attacher)
+{
+ return attacher->getWatcher()->saveScript(attacher);
+}
+
+
+//static
+DCCodeEditorScriptManager* DCCodeEditorScriptManager::getInstance()
+{
+ if (!instance)
+ {
+ instance = new DCCodeEditorScriptManager();
+ }
+ return instance;
+}
+
+DCCodeEditorScriptManager::DCCodeEditorScriptManager()
+{
+ d_customScriptFileWatcher = new QFileSystemWatcher;
+ d_cellCodeScriptFileWatcher = new QFileSystemWatcher;
+
+ connect(d_customScriptFileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(customScriptFileModified(QString)));
+ connect(d_cellCodeScriptFileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(cellCodeScriptFileModified(QString)));
+}
+
+
+DCCodeEditorScriptManager::~DCCodeEditorScriptManager()
+{
+ delete d_customScriptFileWatcher;
+ delete d_cellCodeScriptFileWatcher;
+}
+
+DCCodeEditorScriptManager::Attacher* DCCodeEditorScriptManager::createCustomScriptAttacherPrivate(DCEditScriptFolder *folder, DCCell *cell)
+{
+ QMap<DCCell*, ScriptWatcher*>::const_iterator it = d_customScriptWatchers.find(cell);
+
+ ScriptWatcher* watcher = NULL;
+ if (it != d_customScriptWatchers.end())
+ {
+ watcher = it.value();
+ }
+ else
+ {
+ watcher = new CustomScriptWatcher(this, cell);
+ d_customScriptWatchers.insert(cell, watcher);
+ d_customScriptFileWatcher->addPath(watcher->getWorkFilePathScript());
+ }
+
+ Attacher *attacher = new Attacher(folder, this, watcher);
+ watcher->attach(attacher);
+
+ return attacher;
+}
+
+DCCodeEditorScriptManager::Attacher* DCCodeEditorScriptManager::createCellCodeScriptAttacherPrivate(DCEditScriptFolder *folder, DCCellCode *cellCode)
+{
+ QMap<DCCellCode*, ScriptWatcher*>::const_iterator it = d_cellCodeScriptWatchers.find(cellCode);
+
+ ScriptWatcher* watcher = NULL;
+ if (it != d_cellCodeScriptWatchers.end())
+ {
+ watcher = it.value();
+ }
+ else
+ {
+ watcher = new CellCodeScriptWatcher(this, cellCode);
+ d_cellCodeScriptFileWatcher->addPath(watcher->getWorkFilePathScript());
+ d_cellCodeScriptWatchers.insert(cellCode, watcher);
+ }
+
+ Attacher *attacher = new Attacher(folder, this, watcher);
+ watcher->attach(attacher);
+
+ return attacher;
+}
+
+void DCCodeEditorScriptManager::attacherDeleting(Attacher *attacher)
+{
+ ScriptWatcher *watcher = attacher->getWatcher();
+ if (watcher)
+ {
+ watcher->deattach(attacher);
+ if (watcher->getRefCount() <= 0)
+ {
+ if (watcher->getType() == ScriptWatcher::E_CUSTOMSCRIPT)
+ {
+ d_customScriptFileWatcher->removePath(watcher->getWorkFilePathScript());
+ QMap<DCCell*, ScriptWatcher*>::iterator i = d_customScriptWatchers.begin();
+ while (i != d_customScriptWatchers.end())
+ {
+ if (i.value() == watcher)
+ {
+ d_customScriptWatchers.erase(i);
+ delete watcher;
+ break;
+ }
+ ++i;
+ }
+ }
+ else
+ {
+ d_cellCodeScriptFileWatcher->removePath(watcher->getWorkFilePathScript());
+ QMap<DCCellCode*, ScriptWatcher*>::iterator i = d_cellCodeScriptWatchers.begin();
+ while (i != d_cellCodeScriptWatchers.end())
+ {
+ if (i.value() == watcher)
+ {
+ d_cellCodeScriptWatchers.erase(i);
+ delete watcher;
+ break;
+ }
+ ++i;
+ }
+ }
+ }
+ }
+}
+
+void DCCodeEditorScriptManager::callbackScriptChanged(ScriptWatcher *watcher, const Attacher *modifier)
+{
+ const QList<Attacher*> *attachers = watcher->getAttachers();
+
+ QList<Attacher*>::const_iterator it = attachers->constBegin();
+ while (it != attachers->constEnd())
+ {
+ if (*it != modifier)
+ {
+ (*it)->callbackScriptChanged();
+ }
+ ++it;
+ }
+}
+
+void DCCodeEditorScriptManager::callbackModificationStatusChanged(ScriptWatcher *watcher, Attacher *modifier, bool modified)
+{
+ (void)modifier;
+
+ const QList<Attacher*> *attachers = watcher->getAttachers();
+
+ QList<Attacher*>::const_iterator it = attachers->constBegin();
+ while (it != attachers->constEnd())
+ {
+ (*it)->callbackModificationStatusChanged(modified);
+ ++it;
+ }
+}
+
+//SLOT
+void DCCodeEditorScriptManager::customScriptFileModified(const QString &path)
+{
+ qint64 notifiedTime = QDateTime::currentMSecsSinceEpoch();
+ DCCell *modifiedCell = NULL;
+ ScriptWatcher *watcher = NULL;
+ QMap<DCCell*, ScriptWatcher*>::iterator it = d_customScriptWatchers.begin();
+ while(it != d_customScriptWatchers.end())
+ {
+ DCCell *cell = it.key();
+ if (cell && cell->getWorkFilePathForCustomScript() == path)
+ {
+ modifiedCell = cell;
+ watcher = it.value();
+ break;
+ }
+ ++it;
+ }
+ if (watcher && watcher->checkIfModifiedByExternalEditor())
+ {
+ const QList<DCCodeEditorScriptManager::Attacher*> *attachers = watcher->getAttachers();
+ QList<DCCodeEditorScriptManager::Attacher*>::const_iterator it = attachers->begin();
+ while(it != attachers->end())
+ {
+ (*it)->callbackFileModifiedByExternalEditor(notifiedTime);
+ ++it;
+ }
+ }
+}
+
+//SLOT
+void DCCodeEditorScriptManager::cellCodeScriptFileModified(const QString &path)
+{
+ qint64 notifiedTime = QDateTime::currentMSecsSinceEpoch();
+ DCCellCode *modifiedCellCode = NULL;
+ ScriptWatcher *watcher = NULL;
+ QMap<DCCellCode*, ScriptWatcher*>::iterator it = d_cellCodeScriptWatchers.begin();
+ while(it != d_cellCodeScriptWatchers.end())
+ {
+ DCCellCode *cellCode = it.key();
+ if (cellCode && cellCode->getWorkFilePathForCellCodeScript() == path)
+ {
+ modifiedCellCode = cellCode;
+ watcher = it.value();
+ break;
+ }
+ ++it;
+ }
+ if (watcher && watcher->checkIfModifiedByExternalEditor())
+ {
+ const QList<DCCodeEditorScriptManager::Attacher*> *attachers = watcher->getAttachers();
+ QList<DCCodeEditorScriptManager::Attacher*>::const_iterator it = attachers->begin();
+ while(it != attachers->end())
+ {
+ (*it)->callbackFileModifiedByExternalEditor(notifiedTime);
+ ++it;
+ }
+ }
+}