#include "stdafx.h" #include "Scintilla.h" #include "WcharMbcsConverter.h" #include "NotepadPlusWrapper.h" #include "PromptDialog.h" #include "MenuManager.h" #include "PluginInterface.h" #include "ScintillaWrapper.h" #include "PythonScriptVersion.h" #include "GILManager.h" #include "MutexHolder.h" #include "NotAllowedInCallbackException.h" #include "MainThread.h" #include "ScintillaCallbackCounter.h" #include "InvalidValueProvidedException.h" #include "Enums.h" namespace NppPythonScript { NotepadPlusWrapper::NotepadPlusWrapper(HINSTANCE hInst, HWND nppHandle) : m_nppHandle(nppHandle), m_hInst(hInst), m_notificationsEnabled(false), m_callbackMutex(::CreateMutex(NULL, FALSE, NULL)) { hwnd = (intptr_t)nppHandle; } NotepadPlusWrapper::~NotepadPlusWrapper() { try { m_callbacks.clear(); m_notificationsEnabled = false; } catch (...) { // I don't know what to do with that, but a destructor should never throw, so... } // To please Lint, let's NULL these handles and pointers m_nppHandle = NULL; m_hInst = NULL; hwnd = NULL; } void NotepadPlusWrapper::notify(SCNotification *notifyCode) { if (!m_notificationsEnabled) return; // Optimisation. Count the number of callbacks registered for this code, // if there are none, then we can simply return without claiming the GIL. // This is especially helpful as N++ forwards WM_NOTIFY messages from child windows, so we // get all manor of garbage from RebarWindows etc, that we just don't care about. // Overall, it's slightly slower in the case of a real callback (we count them, then // find them all with the equal_range() call below, but much quicker in the case of a // notification that we don't care about, as we don't need to grab the GIL then give it back. // We use count, because *ANY* operation that involves the boost::python::object (e.g. creating // an iterator) requires the GIL to manage the refcounts. A count() only involves the integer keys, // so we're safe to do that without the GIL. callbackT::size_type count = m_callbacks.count(notifyCode->nmhdr.code); if (0 == count) return; DEBUG_TRACE_S(("Notepad notify with code %d\n", notifyCode->nmhdr.code)); GILLock gilLock; std::pair callbackIter = m_callbacks.equal_range(notifyCode->nmhdr.code); if (callbackIter.first != callbackIter.second) { // Create the parameters for the callback boost::python::dict params; params["code"] = notifyCode->nmhdr.code; switch(notifyCode->nmhdr.code) { case NPPN_FILEBEFORECLOSE: case NPPN_FILEOPENED: case NPPN_FILECLOSED: case NPPN_FILEBEFOREOPEN: case NPPN_FILEBEFORESAVE: case NPPN_FILESAVED: case NPPN_BUFFERACTIVATED: case NPPN_LANGCHANGED: case NPPN_WORDSTYLESUPDATED: case NPPN_FILELOADFAILED: case NPPN_SNAPSHOTDIRTYFILELOADED: case NPPN_FILEBEFORERENAME: case NPPN_FILERENAMECANCEL: case NPPN_FILERENAMED: case NPPN_FILEBEFOREDELETE: case NPPN_FILEDELETEFAILED: case NPPN_FILEDELETED: params["bufferID"] = notifyCode->nmhdr.idFrom; break; case NPPN_FILEBEFORELOAD: case NPPN_READY: case NPPN_SHUTDOWN: case NPPN_TBMODIFICATION: // I hope no-one ever has to use this! case NPPN_BEFORESHUTDOWN: case NPPN_CANCELSHUTDOWN: // No additional parameters break; case NPPN_READONLYCHANGED: params["bufferID"] = reinterpret_cast(notifyCode->nmhdr.hwndFrom); params["readonly"] = (notifyCode->nmhdr.idFrom & 1) ? true : false; params["dirty"] = (notifyCode->nmhdr.idFrom & 2) ? true : false; break; case NPPN_SHORTCUTREMAPPED: { params["commandID"] = notifyCode->nmhdr.idFrom; ShortcutKey *key = reinterpret_cast(notifyCode->nmhdr.hwndFrom); params["key"] = key->_key; params["isCtrl"] = key->_isCtrl; params["isAlt"] = key->_isAlt; params["isShift"] = key->_isShift; } break; case NPPN_DOCORDERCHANGED: params["newIndex"] = reinterpret_cast(notifyCode->nmhdr.hwndFrom); params["bufferID"] = notifyCode->nmhdr.idFrom; break; default: // Unknown notification, so just fill in the parameters as integers. params["idFrom"] = notifyCode->nmhdr.idFrom; params["hwndFrom"] = reinterpret_cast(notifyCode->nmhdr.hwndFrom); break; } std::list callbacks; while (callbackIter.first != callbackIter.second) { callbacks.push_back(callbackIter.first->second); ++callbackIter.first; } for (std::list::iterator listIter = callbacks.begin(); listIter != callbacks.end(); ++listIter) { try { // Call the callback (*listIter)(params); } catch(...) { if (PyErr_Occurred()) { DEBUG_TRACE(L"Python error occurred in Notepad++ callback"); PyErr_Print(); } else { DEBUG_TRACE(L"Non-Python error occurred in Notepad++ callback"); } } } } } bool NotepadPlusWrapper::addCallback(boost::python::object callback, boost::python::list events) { MutexHolder hold(m_callbackMutex); if (PyCallable_Check(callback.ptr())) { size_t eventCount = _len(events); for(idx_t i = 0; i < eventCount; i++) { m_callbacks.insert(std::pair(boost::python::extract(events[i]), callback)); } m_notificationsEnabled = true; return true; } else { return false; } } bool NotepadPlusWrapper::save() { return static_cast(callNotepad(NPPM_SAVECURRENTFILE)); } void NotepadPlusWrapper::newDocument() { DEBUG_TRACE(L"NotepadPlusWrapper::newDocument\n"); notAllowedInScintillaCallback("new() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using new() in the callback handler"); callNotepad(NPPM_MENUCOMMAND, 0, IDM_FILE_NEW); } bool NotepadPlusWrapper::newDocumentWithFilename(const char *filename) { notAllowedInScintillaCallback("new() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using new() in the callback handler"); callNotepad(NPPM_MENUCOMMAND, 0, IDM_FILE_NEW); return saveAs(filename); } bool NotepadPlusWrapper::saveAs(const char *filename) { return static_cast(callNotepad(NPPM_SAVECURRENTFILEAS, FALSE, reinterpret_cast(WcharMbcsConverter::char2tchar(filename).get()))); } bool NotepadPlusWrapper::saveAsCopy(const char *filename) { return static_cast(callNotepad(NPPM_SAVECURRENTFILEAS, TRUE, reinterpret_cast(WcharMbcsConverter::char2tchar(filename).get()))); } bool NotepadPlusWrapper::open(const char *filename) { notAllowedInScintillaCallback("open() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using open() in the callback handler"); return static_cast(callNotepad(NPPM_DOOPEN, 0, reinterpret_cast(WcharMbcsConverter::char2tchar(filename).get()))); } bool NotepadPlusWrapper::activateFile(const char *filename) { notAllowedInScintillaCallback("activateFile() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using activateFile() in the callback handler"); return static_cast(callNotepad(NPPM_SWITCHTOFILE, 0, reinterpret_cast(WcharMbcsConverter::char2tchar(filename).get()))); } int NotepadPlusWrapper::getCurrentView() { int currentView = 0; callNotepad(NPPM_GETCURRENTSCINTILLA, 0, reinterpret_cast(¤tView)); return currentView; } LangType NotepadPlusWrapper::getCurrentLangType() { int lang = 0; callNotepad(NPPM_GETCURRENTLANGTYPE, 0, reinterpret_cast(&lang)); return static_cast(lang); } void NotepadPlusWrapper::setCurrentLangType(LangType lang) { callNotepad(NPPM_SETCURRENTLANGTYPE, 0, static_cast(lang)); } boost::python::str NotepadPlusWrapper::getPluginVersion() { return boost::python::str(PYSCR_VERSION_STRING); } boost::python::list NotepadPlusWrapper::getFiles() { idx_t count; intptr_t bufferID; boost::python::list files; int view = 0; bool onlyOneView = isSingleView(); if (onlyOneView) { view = getCurrentView(); } int view_end = onlyOneView ? view : 1; for(view; view <= view_end; view++) { count = (idx_t)callNotepad(NPPM_GETNBOPENFILES, 0, view ? SECOND_VIEW : PRIMARY_VIEW); TCHAR **fileNames = (TCHAR **)new TCHAR*[count]; for (idx_t i = 0 ; i < count ; i++) { fileNames[i] = new TCHAR[MAX_PATH]; } if (callNotepad(view ? NPPM_GETOPENFILENAMESSECOND_DEPRECATED : NPPM_GETOPENFILENAMESPRIMARY_DEPRECATED, reinterpret_cast(fileNames), static_cast(count))) { for(idx_t pos = 0; pos < count; pos++) { bufferID = callNotepad(NPPM_GETBUFFERIDFROMPOS, pos, view); if (bufferID) { std::shared_ptr mbFilename = WcharMbcsConverter::tchar2char(fileNames[pos]); files.append(boost::python::make_tuple(const_cast(mbFilename.get()), bufferID, pos, view)); } } } for (idx_t i = 0 ; i < count ; i++) { delete [] fileNames[i]; } delete [] fileNames; } return files; } boost::python::list NotepadPlusWrapper::getSessionFiles(const char *sessionFilename) { boost::python::list result; std::shared_ptr converted = WcharMbcsConverter::char2tchar(sessionFilename); const TCHAR *convertedSessionFilename = converted.get(); idx_t count = (idx_t)callNotepad(NPPM_GETNBSESSIONFILES, 0, reinterpret_cast(convertedSessionFilename)); if (count > 0) { TCHAR **fileNames = (TCHAR **)new TCHAR*[count]; for (idx_t pos = 0 ; pos < count ; pos++) { fileNames[pos] = new TCHAR[MAX_PATH]; } if (callNotepad(NPPM_GETSESSIONFILES, reinterpret_cast(fileNames), reinterpret_cast(convertedSessionFilename))) { for(idx_t pos = 0; pos < count; pos++) { std::shared_ptr mbFilename = WcharMbcsConverter::tchar2char(fileNames[pos]); result.append(const_cast(mbFilename.get())); } } for (idx_t i = 0 ; i < count ; i++) { delete [] fileNames[i]; } delete [] fileNames; } return result; } void NotepadPlusWrapper::saveSession(const char *sessionFilename, boost::python::list files) { sessionInfo si{}; std::shared_ptr tsessionFilename = WcharMbcsConverter::char2tchar(sessionFilename); si.sessionFilePathName = tsessionFilename.get(); size_t filesCount = _len(files); si.files = (TCHAR **)new TCHAR*[filesCount]; // Vector to store temporary list of shared_ptr std::vector< std::shared_ptr > filesList(filesCount); for(idx_t pos = 0; pos < filesCount; pos++) { filesList[pos] = WcharMbcsConverter::char2tchar(static_cast(boost::python::extract(files[pos]))); si.files[pos] = filesList[pos].get(); } si.nbFile = (int)filesCount; callNotepad(NPPM_SAVESESSION, 0, reinterpret_cast(&si)); for(idx_t pos = 0; pos < filesCount; pos++) { filesList[pos].reset(); } } void NotepadPlusWrapper::saveCurrentSession(const char *filename) { callNotepad(NPPM_SAVECURRENTSESSION, 0, reinterpret_cast(WcharMbcsConverter::char2tchar(filename).get())); } boost::shared_ptr NotepadPlusWrapper::createScintilla() { notAllowedInScintillaCallback("createScintilla() is not allowed in a synchronous editor callback. Use an asynchronous callback, or avoid calling createScintilla() in the callback handler."); LRESULT handle = callNotepad(NPPM_CREATESCINTILLAHANDLE, 0, NULL); // return copy return boost::shared_ptr(new ScintillaWrapper((HWND)handle, m_nppHandle)); } void NotepadPlusWrapper::destroyScintilla(boost::shared_ptr buffer) { if (buffer) { notAllowedInScintillaCallback("destroyScintilla() is not allowed in a synchronous editor callback. Use an asynchronous callback, or avoid calling destroyScintilla() in the callback handler."); callNotepad(NPPM_DESTROYSCINTILLAHANDLE_DEPRECATED, 0, reinterpret_cast(buffer->getHandle())); buffer->invalidateHandle(); } } intptr_t NotepadPlusWrapper::getCurrentDocIndex(int view) { return callNotepad(NPPM_GETCURRENTDOCINDEX, 0, static_cast(view)); } void NotepadPlusWrapper::setStatusBar(StatusBarSection section, const char *text) { std::shared_ptr s = WcharMbcsConverter::char2tchar(text); callNotepad(NPPM_SETSTATUSBAR, static_cast(section), reinterpret_cast(s.get())); } intptr_t NotepadPlusWrapper::getPluginMenuHandle() { return callNotepad(NPPM_GETMENUHANDLE, 0, 0); } void NotepadPlusWrapper::activateIndex(int view, int index) { callNotepad(NPPM_ACTIVATEDOC, static_cast(view), static_cast(index)); } void NotepadPlusWrapper::loadSession(boost::python::str filename) { notAllowedInScintillaCallback("loadSession() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using loadSession() in the callback handler"); handleFileNameToLongPath(NPPM_LOADSESSION, filename); } bool NotepadPlusWrapper::activateFileString(boost::python::str filename) { notAllowedInScintillaCallback("activateFile() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using activateFile() in the callback handler"); bool res = handleFileNameToLongPath(NPPM_SWITCHTOFILE, filename); if (!res) { // issue 105 std::shared_ptr tfileName = WcharMbcsConverter::char2tchar(boost::python::extract(filename)); return static_cast(callNotepad(NPPM_SWITCHTOFILE, 0, reinterpret_cast(tfileName.get()))); } return res; } bool NotepadPlusWrapper::reloadFile(boost::python::str filename, bool alert) { return handleFileNameToLongPath(NPPM_RELOADFILE, filename, alert ? 1 : 0); } bool NotepadPlusWrapper::saveAllFiles() { return static_cast(callNotepad(NPPM_SAVEALLFILES)); } boost::python::str NotepadPlusWrapper::getPluginConfigDir() { LRESULT size = callNotepad(NPPM_GETPLUGINSCONFIGDIR, 0, NULL); wchar_t* result(new wchar_t[size+1]); callNotepad(NPPM_GETPLUGINSCONFIGDIR, size+1, reinterpret_cast(result)); return boost::python::str(const_cast(WcharMbcsConverter::tchar2char(result).get())); } boost::python::str NotepadPlusWrapper::getPluginHomePath() { LRESULT size = callNotepad(NPPM_GETPLUGINHOMEPATH, 0, NULL); wchar_t* result(new wchar_t[size+1]); callNotepad(NPPM_GETPLUGINHOMEPATH, size+1, reinterpret_cast(result)); return boost::python::str(const_cast(WcharMbcsConverter::tchar2char(result).get())); } boost::python::str NotepadPlusWrapper::getSettingsOnCloudPath() { LRESULT size = callNotepad(NPPM_GETSETTINGSONCLOUDPATH, 0, NULL); wchar_t* result(new wchar_t[size+1]); callNotepad(NPPM_GETSETTINGSONCLOUDPATH, size+1, reinterpret_cast(result)); return boost::python::str(const_cast(WcharMbcsConverter::tchar2char(result).get())); } boost::python::str NotepadPlusWrapper::getSettingsDirPath() { LRESULT size = callNotepad(NPPM_GETNPPSETTINGSDIRPATH, 0, NULL); wchar_t* result(new wchar_t[size + 1]); callNotepad(NPPM_GETNPPSETTINGSDIRPATH, size + 1, reinterpret_cast(result)); return boost::python::str(const_cast(WcharMbcsConverter::tchar2char(result).get())); } intptr_t NotepadPlusWrapper::getBookMarkID() { return callNotepad(NPPM_GETBOOKMARKID, 0, 0); } void NotepadPlusWrapper::menuCommand(int commandID) { callNotepad(NPPM_MENUCOMMAND, 0, commandID); } boost::python::tuple NotepadPlusWrapper::getVersion() { LPARAM nppVersion = callNotepad(NPPM_GETNPPVERSION); int majorVersion = HIWORD(nppVersion); int minorVersion = LOWORD(nppVersion); std::vector version(4); version[0] = majorVersion; char tmp[10]; _itoa_s(minorVersion, tmp, 10, 10); idx_t i; for(i = 0; tmp[i] && i < 4; i++) { version[i+1] = tmp[i] - 48; } //lint -e864 Expression involving variable 'version' possibly depends on order of evaluation // It's perfectly safe the way it's used here. switch(i) { case 3: return boost::python::make_tuple(version[0], version[1], version[2], version[3]); case 2: return boost::python::make_tuple(version[0], version[1], version[2]); case 1: return boost::python::make_tuple(version[0], version[1]); case 0: default: return boost::python::make_tuple(version[0]); } //lint +e864 } bool NotepadPlusWrapper::hideTabBar() { return static_cast(callNotepad(NPPM_HIDETABBAR, 0, TRUE)); } bool NotepadPlusWrapper::showTabBar() { return static_cast(callNotepad(NPPM_HIDETABBAR, 0, FALSE)); } intptr_t NotepadPlusWrapper::getCurrentBufferID() { return callNotepad(NPPM_GETCURRENTBUFFERID); } void NotepadPlusWrapper::reloadBuffer(intptr_t bufferID, bool withAlert) { if (checkForValidBuffer(bufferID)) { callNotepad(NPPM_RELOADBUFFERID, static_cast(bufferID), static_cast(withAlert)); } else { invalidValueProvided("invalid aka unknown bufferID provided"); } } LangType NotepadPlusWrapper::getLangType() { return getBufferLangType(callNotepad(NPPM_GETCURRENTBUFFERID)); } LangType NotepadPlusWrapper::getBufferLangType(intptr_t bufferID) { if (checkForValidBuffer(bufferID)) { return static_cast(callNotepad(NPPM_GETBUFFERLANGTYPE, bufferID)); } else { invalidValueProvided("invalid aka unknown bufferID provided"); //add some random return value to avoid compiler warning about missing return value return LangType::L_TEXT; } } void NotepadPlusWrapper::setBufferLangType(LangType language, intptr_t bufferID) { callNotepad(NPPM_SETBUFFERLANGTYPE, static_cast(bufferID), static_cast(language)); } BufferEncoding NotepadPlusWrapper::getEncoding() { return getBufferEncoding(callNotepad(NPPM_GETCURRENTBUFFERID)); } BufferEncoding NotepadPlusWrapper::getBufferEncoding(intptr_t bufferID) { if (checkForValidBuffer(bufferID)) { return static_cast(callNotepad(NPPM_GETBUFFERENCODING, static_cast(bufferID))); } else { invalidValueProvided("invalid aka unknown bufferID provided"); //add some random return value to avoid compiler warning about missing return value return BufferEncoding::uniEnd; } } bool NotepadPlusWrapper::setEncoding(BufferEncoding encoding) { return setBufferEncoding(encoding, static_cast(callNotepad(NPPM_GETCURRENTBUFFERID)) ); } bool NotepadPlusWrapper::setBufferEncoding(BufferEncoding encoding, intptr_t bufferID) { return static_cast(callNotepad(NPPM_SETBUFFERENCODING, static_cast(bufferID), static_cast(encoding))); } FormatType NotepadPlusWrapper::getFormatType() { return getBufferFormatType(callNotepad(NPPM_GETCURRENTBUFFERID)); } FormatType NotepadPlusWrapper::getBufferFormatType(intptr_t bufferID) { if (checkForValidBuffer(bufferID)) { return static_cast(callNotepad(NPPM_GETBUFFERFORMAT, static_cast(bufferID))); } else { invalidValueProvided("invalid aka unknown bufferID provided"); //add some random return value to avoid compiler warning about missing return value return FormatType::WIN_FORMAT; } } bool NotepadPlusWrapper::setFormatType(FormatType format) { return setBufferFormatType(format, callNotepad(NPPM_GETCURRENTBUFFERID)); } bool NotepadPlusWrapper::setBufferFormatType(FormatType format, intptr_t bufferID) { return static_cast(callNotepad(NPPM_SETBUFFERFORMAT, static_cast(bufferID), static_cast(format))); } void NotepadPlusWrapper::closeDocument() { notAllowedInScintillaCallback("close() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using close() in the callback handler"); callNotepad(NPPM_MENUCOMMAND, 0, IDM_FILE_CLOSE); } void NotepadPlusWrapper::closeAllDocuments() { notAllowedInScintillaCallback("closeAll() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using closeAll() in the callback handler"); callNotepad(NPPM_MENUCOMMAND, 0, IDM_FILE_CLOSEALL); } void NotepadPlusWrapper::closeAllButCurrentDocument() { notAllowedInScintillaCallback("closeAllButCurrent() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using closeAllButCurrent() in the callback handler"); callNotepad(NPPM_MENUCOMMAND, 0, IDM_FILE_CLOSEALL_BUT_CURRENT); } void NotepadPlusWrapper::reloadCurrentDocument() { callNotepad(NPPM_MENUCOMMAND, 0, IDM_FILE_RELOAD); } int NotepadPlusWrapper::messageBox(const char *message, const char *title, UINT flags) const { if (!message) { message = ""; } if (!title) { title = "Python Script for Notepad++"; } if (!flags) { flags = 0; } int retVal; GILRelease release; retVal = ::MessageBox(m_nppHandle, WcharMbcsConverter::char2tchar(message).get(), WcharMbcsConverter::char2tchar(title).get(), flags); return retVal; } boost::python::object NotepadPlusWrapper::prompt(boost::python::object promptObj, boost::python::object title, boost::python::object initial) { PromptDialog promptDlg(m_hInst, m_nppHandle); const char *cPrompt = NULL; const char *cTitle = NULL; const char *cInitial = NULL; if (!promptObj || !promptObj.is_none()) cPrompt = (const char *)boost::python::extract(promptObj.attr("__str__")()); if (!title || !title.is_none()) cTitle= (const char *)boost::python::extract(title.attr("__str__")()); if (!initial || !initial.is_none()) cInitial = (const char *)boost::python::extract(initial.attr("__str__")()); PromptDialog::PROMPT_RESULT result; GILRelease release; result = promptDlg.showPrompt(cPrompt, cTitle, cInitial); release.reacquire(); if (PromptDialog::RESULT_OK == result) { return boost::python::str(promptDlg.getText()); } else { return boost::python::object(); } } void NotepadPlusWrapper::clearCallbackFunction(boost::python::object callback) { for(callbackT::iterator it = m_callbacks.begin(); it != m_callbacks.end();) { if (callback == it->second) { it = m_callbacks.erase(it); } else { ++it; } } if (m_callbacks.empty()) { m_notificationsEnabled = false; } } void NotepadPlusWrapper::clearCallbackEvents(boost::python::list events) { for(callbackT::iterator it = m_callbacks.begin(); it != m_callbacks.end(); ) { if(boost::python::extract(events.contains(it->first))) { it = m_callbacks.erase(it); } else { ++it; } } if (m_callbacks.empty()) { m_notificationsEnabled = false; } } void NotepadPlusWrapper::clearCallback(boost::python::object callback, boost::python::list events) { for(callbackT::iterator it = m_callbacks.begin(); it != m_callbacks.end(); ) { if(it->second == callback && boost::python::extract(events.contains(it->first))) { it = m_callbacks.erase(it); } else { ++it; } } if (m_callbacks.empty()) { m_notificationsEnabled = false; } } void NotepadPlusWrapper::clearAllCallbacks() { for(callbackT::iterator it = m_callbacks.begin(); it != m_callbacks.end(); ) { it = m_callbacks.erase(it); } if (m_callbacks.empty()) { m_notificationsEnabled = false; } } void NotepadPlusWrapper::activateBufferID(intptr_t bufferID) { notAllowedInScintillaCallback("activateBufferID() cannot be called in a synchronous editor callback. " "Use an asynchronous callback, or avoid using activateBufferID() in the callback handler"); idx_t index = (idx_t)callNotepad(NPPM_GETPOSFROMBUFFERID, static_cast(bufferID), static_cast(getCurrentView())); if (index != -1) { UINT view = (index & 0xC0000000) >> 30; index = index & 0x3FFFFFFF; callNotepad(NPPM_ACTIVATEDOC, view, (LPARAM)index); } } boost::python::str NotepadPlusWrapper::getBufferFilename(intptr_t bufferID) { TCHAR buffer[MAX_PATH]{}; callNotepad(NPPM_GETFULLPATHFROMBUFFERID, static_cast(bufferID), reinterpret_cast(buffer)); std::shared_ptr filename = WcharMbcsConverter::tchar2char(buffer); return boost::python::str(const_cast(filename.get())); } boost::python::str NotepadPlusWrapper::getCurrentFilename() { idx_t bufferID = callNotepad(NPPM_GETCURRENTBUFFERID); return getBufferFilename(bufferID); } bool NotepadPlusWrapper::runPluginCommand(boost::python::str pluginName, boost::python::str menuOption, bool refreshCache) const { bool retVal = false; MenuManager *menuManager = MenuManager::getInstance(); if (!pluginName.is_none() && !menuOption.is_none()) { std::shared_ptr tpluginName = WcharMbcsConverter::char2tchar(boost::python::extract(pluginName)); std::shared_ptr tmenuOption = WcharMbcsConverter::char2tchar(boost::python::extract(menuOption)); GILRelease release; idx_t commandID = menuManager->findPluginCommand(tpluginName.get(), tmenuOption.get(), refreshCache); if (commandID) { ::SendMessage(m_nppHandle, WM_COMMAND, commandID, 0); retVal = true; } } return retVal; } bool NotepadPlusWrapper::runMenuCommand(boost::python::str menuName, boost::python::str menuOption, bool refreshCache) const { bool retVal = false; MenuManager *menuManager = MenuManager::getInstance(); if (!menuName.is_none() && !menuOption.is_none()) { std::shared_ptr tmenuName = WcharMbcsConverter::char2tchar(boost::python::extract(menuName)); std::shared_ptr tmenuOption = WcharMbcsConverter::char2tchar(boost::python::extract(menuOption)); GILRelease release; idx_t commandID = menuManager->findMenuCommand(tmenuName.get(), tmenuOption.get(), refreshCache); if (commandID) { ::SendMessage(m_nppHandle, WM_COMMAND, commandID, 0); retVal = true; } } return retVal; } boost::python::str NotepadPlusWrapper::getNppDir() { TCHAR buffer[MAX_PATH]{}; callNotepad(NPPM_GETNPPDIRECTORY, MAX_PATH, reinterpret_cast(buffer)); return boost::python::str(const_cast(WcharMbcsConverter::tchar2char(buffer).get())); } boost::python::str NotepadPlusWrapper::getCommandLine() { return boost::python::str(const_cast(WcharMbcsConverter::tchar2char(::GetCommandLine()).get())); } bool NotepadPlusWrapper::allocateSupported() { return static_cast(callNotepad(NPPM_ALLOCATESUPPORTED_DEPRECATED)); } boost::python::object NotepadPlusWrapper::allocateCmdID(int quantity) { int startID = 0; bool result = static_cast(callNotepad(NPPM_ALLOCATECMDID, static_cast(quantity), reinterpret_cast(&startID))); if (result) { return boost::python::object(startID); } else { return boost::python::object(); } } boost::python::object NotepadPlusWrapper::allocateMarker(int quantity) { int startID = 0; bool result = static_cast(callNotepad(NPPM_ALLOCATEMARKER, static_cast(quantity), reinterpret_cast(&startID))); if (result) { return boost::python::object(startID); } else { return boost::python::object(); } } boost::python::object NotepadPlusWrapper::allocateIndicator(int quantity) { int startID = 0; bool result = static_cast(callNotepad(NPPM_ALLOCATEINDICATOR, static_cast(quantity), reinterpret_cast(&startID))); if (result) { return boost::python::object(startID); } else { return boost::python::object(); } } intptr_t NotepadPlusWrapper::getMenuHandle(int menu = 0) { return callNotepad(NPPM_GETMENUHANDLE, static_cast(menu), 0); } bool NotepadPlusWrapper::isTabBarHidden() { return static_cast(callNotepad(NPPM_ISTABBARHIDDEN)); } bool NotepadPlusWrapper::hideToolBar(bool hideOrNot) { return static_cast(callNotepad(NPPM_HIDETOOLBAR, 0, hideOrNot)); } bool NotepadPlusWrapper::isToolBarHidden() { return static_cast(callNotepad(NPPM_ISTOOLBARHIDDEN)); } bool NotepadPlusWrapper::hideMenu(bool hideOrNot) { return static_cast(callNotepad(NPPM_HIDEMENU, 0, hideOrNot)); } bool NotepadPlusWrapper::isMenuHidden() { return static_cast(callNotepad(NPPM_ISMENUHIDDEN)); } bool NotepadPlusWrapper::hideStatusBar(bool hideOrNot) { return static_cast(callNotepad(NPPM_HIDESTATUSBAR, 0, hideOrNot)); } bool NotepadPlusWrapper::isStatusBarHidden() { return static_cast(callNotepad(NPPM_ISSTATUSBARHIDDEN)); } bool NotepadPlusWrapper::saveFile(boost::python::str filename) { return handleFileNameToLongPath(NPPM_SAVEFILE, filename); } void NotepadPlusWrapper::showDocSwitcher(bool showOrNot) { callNotepad(NPPM_SHOWDOCLIST, 0, showOrNot); } bool NotepadPlusWrapper::isDocSwitcherShown() { return static_cast(callNotepad(NPPM_ISDOCLISTSHOWN)); } void NotepadPlusWrapper::docSwitcherDisableExtColumn(bool disableOrNot) { callNotepad(NPPM_DOCLISTDISABLEEXTCOLUMN, 0, disableOrNot); } void NotepadPlusWrapper::docSwitcherDisablePathColumn(bool disableOrNot) { callNotepad(NPPM_DOCLISTDISABLEPATHCOLUMN, 0, disableOrNot); } bool NotepadPlusWrapper::isDarkModeEnabled() { return static_cast(callNotepad(NPPM_ISDARKMODEENABLED)); } intptr_t NotepadPlusWrapper::getCurrentNativeLangEncoding() { return callNotepad(NPPM_GETCURRENTNATIVELANGENCODING); } boost::python::str NotepadPlusWrapper::getLanguageName(int langType) { size_t size = callNotepad(NPPM_GETLANGUAGENAME, langType, NULL); wchar_t* result(new wchar_t[size+1]); callNotepad(NPPM_GETLANGUAGENAME, langType, reinterpret_cast(result)); std::shared_ptr languageName = WcharMbcsConverter::tchar2char(result); return boost::python::str(const_cast(languageName.get())); } boost::python::str NotepadPlusWrapper::getLanguageDesc(int langType) { size_t size = callNotepad(NPPM_GETLANGUAGEDESC, langType, NULL); wchar_t* result(new wchar_t[size+1]); callNotepad(NPPM_GETLANGUAGEDESC, langType, reinterpret_cast(result)); std::shared_ptr languageName = WcharMbcsConverter::tchar2char(result); return boost::python::str(const_cast(languageName.get())); } bool NotepadPlusWrapper::getAppdataPluginsAllowed() { return static_cast(callNotepad(NPPM_GETAPPDATAPLUGINSALLOWED)); } boost::python::tuple NotepadPlusWrapper::getEditorDefaultForegroundColor() { int retVal = (int)callNotepad(NPPM_GETEDITORDEFAULTFOREGROUNDCOLOR); return boost::python::make_tuple(COLOUR_RED(retVal), COLOUR_GREEN(retVal), COLOUR_BLUE(retVal)); } boost::python::tuple NotepadPlusWrapper::getEditorDefaultBackgroundColor() { int retVal = (int)callNotepad(NPPM_GETEDITORDEFAULTBACKGROUNDCOLOR); return boost::python::make_tuple(COLOUR_RED(retVal), COLOUR_GREEN(retVal), COLOUR_BLUE(retVal)); } void NotepadPlusWrapper::setSmoothFont(bool setSmoothFontOrNot) { callNotepad(NPPM_SETSMOOTHFONT, 0, setSmoothFontOrNot); } void NotepadPlusWrapper::setEditorBorderEdge(bool withEditorBorderEdgeOrNot) { callNotepad(NPPM_SETEDITORBORDEREDGE, 0, withEditorBorderEdgeOrNot); } intptr_t NotepadPlusWrapper::getNbUserLang() { return callNotepad(NPPM_GETNBUSERLANG); } intptr_t NotepadPlusWrapper::encodeSci(int view) { return callNotepad(NPPM_ENCODESCI, view, 0); } intptr_t NotepadPlusWrapper::decodeSci(int view) { return callNotepad(NPPM_DECODESCI, view, 0); } void NotepadPlusWrapper::launchFindInFilesDlg(std::wstring dir2Search, std::wstring filter) { callNotepad(NPPM_LAUNCHFINDINFILESDLG, reinterpret_cast(dir2Search.c_str()), reinterpret_cast(filter.c_str())); } winVer NotepadPlusWrapper::getWindowsVersion() { return static_cast(callNotepad(NPPM_GETWINDOWSVERSION, 0, 0)); } bool NotepadPlusWrapper::makeCurrentBufferDirty() { return callNotepad(NPPM_MAKECURRENTBUFFERDIRTY); } bool NotepadPlusWrapper::getEnableThemeTextureFunc() { return callNotepad(NPPM_GETENABLETHEMETEXTUREFUNC_DEPRECATED); } void NotepadPlusWrapper::triggerTabbarContextMenu(int view, int index2Activate) { callNotepad(NPPM_TRIGGERTABBARCONTEXTMENU, view, index2Activate); } void NotepadPlusWrapper::disableAutoUpdate() { callNotepad(NPPM_DISABLEAUTOUPDATE); } bool NotepadPlusWrapper::setUntitledName(const char *newName, intptr_t bufferID = 0) { if ( !newName) { return false; } return static_cast(callNotepad(NPPM_SETUNTITLEDNAME, bufferID, reinterpret_cast(WcharMbcsConverter::char2tchar(newName).get()))); } int NotepadPlusWrapper::getTabColorID(int view, int tabIndex) { return static_cast(callNotepad(NPPM_GETTABCOLORID, view, tabIndex)); } int NotepadPlusWrapper::getToolBarIconSetChoice() { return static_cast(callNotepad(NPPM_GETTOOLBARICONSETCHOICE)); } boost::python::str NotepadPlusWrapper::getNativeLangFileName() { size_t size = callNotepad(NPPM_GETNATIVELANGFILENAME) + 1; std::vector buffer(size); callNotepad(NPPM_GETNATIVELANGFILENAME, size, reinterpret_cast(buffer.data())); return boost::python::str(buffer.data(), size - 1); } LineNumWidthMode NotepadPlusWrapper::getLineNumberWidthMode() { return static_cast(callNotepad(NPPM_GETLINENUMBERWIDTHMODE)); } bool NotepadPlusWrapper::setLineNumberWidthMode(LineNumWidthMode widthMode) { return static_cast(callNotepad(NPPM_SETLINENUMBERWIDTHMODE, 0, static_cast(widthMode))); } boost::python::object NotepadPlusWrapper::getExternalLexerAutoIndentMode(const char* externalLexerName) { int indentMode = -1; bool result = static_cast(callNotepad(NPPM_GETEXTERNALLEXERAUTOINDENTMODE, reinterpret_cast(WcharMbcsConverter::char2tchar(externalLexerName).get()), reinterpret_cast(&indentMode))); if (result) { return boost::python::object(static_cast(indentMode)); } else { return boost::python::object(); } } bool NotepadPlusWrapper::setExternalLexerAutoIndentMode(const char* externalLexerName, AutoIndentMode indentMode) { return static_cast(callNotepad(NPPM_SETEXTERNALLEXERAUTOINDENTMODE, reinterpret_cast(WcharMbcsConverter::char2tchar(externalLexerName).get()), static_cast(indentMode))); } bool NotepadPlusWrapper::isAutoIndention() { return static_cast(callNotepad(NPPM_ISAUTOINDENTON)); } bool NotepadPlusWrapper::addModificationFlags(uint32_t flags) { if ((flags & ~PYSCR_SC_MODEVENTMASKALL) != 0) { throw InvalidValueProvidedException("Provided flags value is not a valid MODIFICATION enum value."); } return static_cast(callNotepad(NPPM_ADDSCNMODIFIEDFLAGS, 0, static_cast(flags))); } bool NotepadPlusWrapper::isSingleView() const { HWND splitter_hwnd = FindWindowEx(m_nppHandle, NULL, L"splitterContainer", NULL); return !IsWindowVisible(splitter_hwnd); } void NotepadPlusWrapper::flashWindow(UINT count, DWORD milliseconds) const { FLASHWINFO flashinfo{}; flashinfo.cbSize = sizeof(flashinfo); flashinfo.hwnd = m_nppHandle; flashinfo.dwFlags = FLASHW_ALL; flashinfo.dwTimeout = milliseconds; flashinfo.uCount = count; FlashWindowEx(&flashinfo); } void NotepadPlusWrapper::notAllowedInScintillaCallback(const char *message) { DWORD currentThreadID = ::GetCurrentThreadId(); if (currentThreadID == g_mainThreadID && ScintillaCallbackCounter::isInCallback()) { throw NotAllowedInCallbackException(message); } } bool NotepadPlusWrapper::checkForValidBuffer(intptr_t bufferID) { if(callNotepad(NPPM_GETPOSFROMBUFFERID, static_cast(bufferID)) == -1) { return false; } else { return true; } } void NotepadPlusWrapper::invalidValueProvided(const char *message) { throw InvalidValueProvidedException(message); } bool NotepadPlusWrapper::handleFileNameToLongPath(UINT nppmID, boost::python::str filename, WPARAM wParam) { std::shared_ptr s = WcharMbcsConverter::char2tchar((const char*)boost::python::extract(filename)); const DWORD longPathBufferSize = ::GetLongPathName(s.get(), nullptr, 0); if (longPathBufferSize == 0) { // Handle an error condition. DEBUG_TRACE((L"GetLongPathName get size failed (%d)\n", GetLastError())); return false; } else { std::vector longNameFullpath(longPathBufferSize); if (::GetLongPathName(s.get(), longNameFullpath.data(), static_cast(longNameFullpath.size())) != (longPathBufferSize - 1)) { // Handle an error condition. DEBUG_TRACE((L"GetLongPathName get path failed (%d)\n", GetLastError())); return false; } else { return static_cast(callNotepad(nppmID, wParam, reinterpret_cast(longNameFullpath.data()))); } } } }