From 9c9fa8c4848be3fcb1fd874d96fde9b9892bc0b8 Mon Sep 17 00:00:00 2001 From: Don Ho Date: Thu, 27 Mar 2014 01:30:31 +0000 Subject: [PATCH] [NEW_FEATURE] Automatic Backup System (in progress). git-svn-id: svn://svn.tuxfamily.org/svnroot/notepadplus/repository/trunk@1203 f5eea248-9336-0410-98b8-ebc06183d4e3 --- PowerEditor/src/Notepad_plus.cpp | 21 ++++ PowerEditor/src/Notepad_plus.h | 5 + PowerEditor/src/Notepad_plus_Window.cpp | 3 + PowerEditor/src/NppNotification.cpp | 11 +- PowerEditor/src/ScitillaComponent/Buffer.cpp | 124 ++++++++++++++++++- PowerEditor/src/ScitillaComponent/Buffer.h | 20 ++- PowerEditor/src/functionList.xml | 2 +- 7 files changed, 174 insertions(+), 12 deletions(-) diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp index ff6b2435..260a4c5a 100644 --- a/PowerEditor/src/Notepad_plus.cpp +++ b/PowerEditor/src/Notepad_plus.cpp @@ -5797,6 +5797,27 @@ void Notepad_plus::showQuoteFromIndex(int index) const ::CloseHandle(hThread); } +void Notepad_plus::launchDocumentBackupTask() +{ + HANDLE hThread = ::CreateThread(NULL, 0, backupDocument, this, 0, NULL); + ::CloseHandle(hThread); +} + +DWORD WINAPI Notepad_plus::backupDocument(void *param) +{ + Notepad_plus *notepad_plus = static_cast(param); + //static int i = 0; + while (notepad_plus) + { + ::Sleep(3000); + //printInt(i++); + + // if current document is dirty, write it in the backup system + Buffer *currentBuffer = notepad_plus->getCurrentBuffer(); + MainFileManager->backupBuffer((BufferID)currentBuffer, currentBuffer->getFullPathName()); + } + return TRUE; +} #pragma warning( disable : 4127 ) //--FLS: undoStreamComment: New function to undo stream comment around or within selection end-points. diff --git a/PowerEditor/src/Notepad_plus.h b/PowerEditor/src/Notepad_plus.h index 4fd870ad..3d05fccc 100644 --- a/PowerEditor/src/Notepad_plus.h +++ b/PowerEditor/src/Notepad_plus.h @@ -305,6 +305,10 @@ public: return _accelerator.getAccTable(); }; bool emergency(generic_string emergencySavedDir); + Buffer * getCurrentBuffer() { + return _pEditView->getCurrentBuffer(); + }; + void launchDocumentBackupTask(); private: @@ -640,6 +644,7 @@ private: return randomNumber; return (rand() % rangeMax); }; + static DWORD WINAPI backupDocument(void *params); }; diff --git a/PowerEditor/src/Notepad_plus_Window.cpp b/PowerEditor/src/Notepad_plus_Window.cpp index 313131c1..4d7a3dab 100644 --- a/PowerEditor/src/Notepad_plus_Window.cpp +++ b/PowerEditor/src/Notepad_plus_Window.cpp @@ -222,6 +222,9 @@ void Notepad_plus_Window::init(HINSTANCE hInst, HWND parent, const TCHAR *cmdLin sprintf(dest, "Loading time : %.0lf seconds", loadTime); ::MessageBoxA(NULL, dest, "", MB_OK); } + + // Lauch backup task + //_notepad_plus_plus_core.launchDocumentBackupTask(); } bool Notepad_plus_Window::isDlgsMsg(MSG *msg, bool unicodeSupported) const diff --git a/PowerEditor/src/NppNotification.cpp b/PowerEditor/src/NppNotification.cpp index 62ac7651..8bc52da7 100644 --- a/PowerEditor/src/NppNotification.cpp +++ b/PowerEditor/src/NppNotification.cpp @@ -55,12 +55,17 @@ BOOL Notepad_plus::notify(SCNotification *notification) ::InvalidateRect(notifyView->getHSelf(), NULL, TRUE); } + if (notification->modificationType & (SC_MOD_DELETETEXT | SC_MOD_INSERTTEXT | SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) + { + // for the backup system + _pEditView->getCurrentBuffer()->setModifiedStatus(true); + } + if (notification->modificationType & SC_MOD_CHANGEFOLD) { if (prevWasEdit) { - notifyView->foldChanged(notification->line, - notification->foldLevelNow, notification->foldLevelPrev); + notifyView->foldChanged(notification->line, notification->foldLevelNow, notification->foldLevelPrev); prevWasEdit = false; } } @@ -68,6 +73,8 @@ BOOL Notepad_plus::notify(SCNotification *notification) { prevWasEdit = false; } + + } break; diff --git a/PowerEditor/src/ScitillaComponent/Buffer.cpp b/PowerEditor/src/ScitillaComponent/Buffer.cpp index 1b8ad2eb..03553c6c 100644 --- a/PowerEditor/src/ScitillaComponent/Buffer.cpp +++ b/PowerEditor/src/ScitillaComponent/Buffer.cpp @@ -47,7 +47,8 @@ const int LF = 0x0A; Buffer::Buffer(FileManager * pManager, BufferID id, Document doc, DocFileStatus type, const TCHAR *fileName) //type must be either DOC_REGULAR or DOC_UNNAMED : _pManager(pManager), _id(id), _isDirty(false), _doc(doc), _isFileReadOnly(false), _isUserReadOnly(false), _recentTag(-1), _references(0), - _canNotify(false), _timeStamp(0), _needReloading(false), _encoding(-1) + _canNotify(false), _timeStamp(0), _needReloading(false), _encoding(-1), _backupFileName(TEXT("")), _isModified(false) + //, _deleteBackupNotification(false), _backupModifiedTimeStamp(0) { NppParameters *pNppParamInst = NppParameters::getInstance(); const NewDocDefaultSettings & ndds = (pNppParamInst->getNppGUI()).getNewDocDefaultSettings(); @@ -612,7 +613,123 @@ bool FileManager::moveFile(BufferID id, const TCHAR * newFileName) return true; } -bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, generic_string * error_msg) { +bool FileManager::backupBuffer(BufferID id, const TCHAR * filename) +{ + Buffer * buffer = getBufferByID(id); + + TCHAR fullpath[MAX_PATH]; + ::GetFullPathName(filename, MAX_PATH, fullpath, NULL); + ::GetLongPathName(fullpath, fullpath, MAX_PATH); + +/* + time_t currentBakModifTimestamp = buffer->getBackupModifiedTimeStamp(); + time_t lastBakModifTimestamp = 0; + + if (PathFileExists(fullpath)) + { + struct _stat statBuf; + if (!generic_stat(fullpath, &statBuf)) + { + if (currentBakModifTimestamp == statBuf.st_mtime) + return true; + + lastBakModifTimestamp = statBuf.st_mtime; + } + } +*/ + if (buffer->isDirty()) + { + if (buffer->isModified()) // buffer dirty and modified, write the backup file + { + UniMode mode = buffer->getUnicodeMode(); + if (mode == uniCookie) + mode = uni8Bit; //set the mode to ANSI to prevent converter from adding BOM and performing conversions, Scintilla's data can be copied directly + + Utf8_16_Write UnicodeConvertor; + UnicodeConvertor.setEncoding(mode); + + int encoding = buffer->getEncoding(); + + FILE *fp = UnicodeConvertor.fopen(fullpath, TEXT("wb")); + if (fp) + { + _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, buffer->_doc); //generate new document + + int lengthDoc = _pscratchTilla->getCurrentDocLen(); + char* buf = (char*)_pscratchTilla->execute(SCI_GETCHARACTERPOINTER); //to get characters directly from Scintilla buffer + size_t items_written = 0; + if (encoding == -1) //no special encoding; can be handled directly by Utf8_16_Write + { + items_written = UnicodeConvertor.fwrite(buf, lengthDoc); + if (lengthDoc == 0) + items_written = 1; + } + else + { + WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance(); + int grabSize; + for (int i = 0; i < lengthDoc; i += grabSize) + { + grabSize = lengthDoc - i; + if (grabSize > blockSize) + grabSize = blockSize; + + int newDataLen = 0; + int incompleteMultibyteChar = 0; + const char *newData = wmc->encode(SC_CP_UTF8, encoding, buf+i, grabSize, &newDataLen, &incompleteMultibyteChar); + grabSize -= incompleteMultibyteChar; + items_written = UnicodeConvertor.fwrite(newData, newDataLen); + } + if (lengthDoc == 0) + items_written = 1; + } + UnicodeConvertor.fclose(); + + // Error, we didn't write the entire document to disk. + // Note that fwrite() doesn't return the number of bytes written, but rather the number of ITEMS. + if(items_written != 1) + { + return false; + } + + _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, _scratchDocDefault); + /* + if (lastBakModifTimestamp != 0) + buffer->setBackupModifiedTimeStamp(lastBakModifTimestamp); + else + { + struct _stat statBuf; + if (!generic_stat(fullpath, &statBuf)) + { + buffer->setBackupModifiedTimeStamp(statBuf.st_mtime); + } + } + */ + + buffer->setModifiedStatus(false); + + return true; //all done + } + return false; // fopen failed + } + + return true; // buffer dirty nut unmodified + } + else // buffer not dirty, sync: delete the backup file + { + generic_string backupFilePath = buffer->getBackupFileName(); + if (backupFilePath != TEXT("")) + { + // delete backup file + + return true; // backup file deleted + } + return true; // no backup file to delete + } +} + +bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, generic_string * error_msg) +{ Buffer * buffer = getBufferByID(id); bool isHidden = false; bool isSys = false; @@ -696,7 +813,8 @@ bool FileManager::saveBuffer(BufferID id, const TCHAR * filename, bool isCopy, g if (isSys) ::SetFileAttributes(fullpath, attrib | FILE_ATTRIBUTE_SYSTEM); - if (isCopy) { + if (isCopy) + { _pscratchTilla->execute(SCI_SETDOCPOINTER, 0, _scratchDocDefault); return true; //all done } diff --git a/PowerEditor/src/ScitillaComponent/Buffer.h b/PowerEditor/src/ScitillaComponent/Buffer.h index 3b11135b..be9453a3 100644 --- a/PowerEditor/src/ScitillaComponent/Buffer.h +++ b/PowerEditor/src/ScitillaComponent/Buffer.h @@ -95,20 +95,15 @@ public: bool reloadBuffer(BufferID id); bool reloadBufferDeferred(BufferID id); bool saveBuffer(BufferID id, const TCHAR * filename, bool isCopy = false, generic_string * error_msg = NULL); + bool backupBuffer(BufferID id, const TCHAR * filename); bool deleteFile(BufferID id); bool moveFile(BufferID id, const TCHAR * newFilename); - bool createEmptyFile(const TCHAR * path); - static FileManager * getInstance() {return _pSelf;}; void destroyInstance() { delete _pSelf; }; - void increaseDocNr() {_nextNewNumber++;}; - int getFileNameFromBuffer(BufferID id, TCHAR * fn2copy); - int docLength(Buffer * buffer) const; - int getEOLFormatForm(const char *data) const; private: @@ -324,6 +319,13 @@ public : generic_string getFileTime(fileTimeType ftt); Lang * getCurrentLang() const; + + //time_t getBackupModifiedTimeStamp() const {return _backupModifiedTimeStamp;}; + //void setBackupModifiedTimeStamp(time_t timestamp2Set) {_backupModifiedTimeStamp = timestamp2Set;}; + bool isModified() const {return _isModified;}; + void setModifiedStatus(bool isModified) {_isModified = isModified;}; + generic_string getBackupFileName() const {return _backupFileName;}; + private : FileManager * _pManager; bool _canNotify; @@ -351,6 +353,7 @@ private : //Environment properties DocFileStatus _currentStatus; time_t _timeStamp; // 0 if it's a new doc + //time_t _backupModifiedTimeStamp; // 0 if backup file is not created bool _isFileReadOnly; generic_string _fullPathName; TCHAR * _fileName; //points to filename part in _fullPathName @@ -359,6 +362,11 @@ private : long _recentTag; static long _recentTagCtr; + // For backup system + generic_string _backupFileName; // default: "" + bool _isModified; // default: false + //bool _deleteBackupNotification; // default: false + void updateTimeStamp(); int indexOfReference(const ScintillaEditView * identifier) const; diff --git a/PowerEditor/src/functionList.xml b/PowerEditor/src/functionList.xml index b00c9a22..57e6de0c 100644 --- a/PowerEditor/src/functionList.xml +++ b/PowerEditor/src/functionList.xml @@ -171,7 +171,7 @@ http://notepad-plus-plus.org/features/function-list.html