mirror of
https://github.com/notepad-plus-plus/notepad-plus-plus.git
synced 2025-07-21 21:04:54 +02:00
Add new feature of using first line of untitled document for its tab name
Fix #3994, fix #16584, close #16585
This commit is contained in:
parent
6459905816
commit
abc23714db
@ -1108,6 +1108,7 @@ Translation note:
|
||||
<Item id="6419" name="New Document"/>
|
||||
<Item id="6420" name="Apply to opened ANSI files"/>
|
||||
<Item id="6432" name="Always open a new document in addition at startup"/>
|
||||
<Item id="6433" name="Use the first line of document as untitled tab name"/>
|
||||
</NewDoc>
|
||||
|
||||
<DefaultDir title="Default Directory">
|
||||
|
@ -1108,6 +1108,7 @@ Translation note:
|
||||
<Item id="6419" name="New Document"/>
|
||||
<Item id="6420" name="Apply to opened ANSI files"/>
|
||||
<Item id="6432" name="Always open a new document in addition at startup"/>
|
||||
<Item id="6433" name="Use the first line of document as untitled tab name"/>
|
||||
</NewDoc>
|
||||
|
||||
<DefaultDir title="Default Directory">
|
||||
|
@ -1085,6 +1085,7 @@ Translation note:
|
||||
<Item id="6419" name="Tài liệu mới"/>
|
||||
<Item id="6420" name="Áp dụng cho các tệp ANSI đã mở"/>
|
||||
<Item id="6432" name="Luôn mở thêm một tài liệu mới mỗi khi khởi động"/>
|
||||
<Item id="6433" name="Dùng dòng đầu tiên làm tên cho các tài liệu chưa có tiêu đề"/>
|
||||
</NewDoc>
|
||||
|
||||
<DefaultDir title="Thư mục mặc định">
|
||||
|
@ -6360,7 +6360,7 @@ void Notepad_plus::getCurrentOpenedFiles(Session & session, bool includeUntitled
|
||||
}
|
||||
|
||||
const wchar_t* langName = languageName.c_str();
|
||||
sessionFileInfo sfi(buf->getFullPathName(), langName, buf->getEncoding(), buf->getUserReadOnly(), buf->isPinned(), buf->getPosition(editView), buf->getBackupFileName().c_str(), buf->getLastModifiedTimestamp(), buf->getMapPosition());
|
||||
sessionFileInfo sfi(buf->getFullPathName(), langName, buf->getEncoding(), buf->getUserReadOnly(), buf->isPinned(), buf->isUntitledTabRenamed(), buf->getPosition(editView), buf->getBackupFileName().c_str(), buf->getLastModifiedTimestamp(), buf->getMapPosition());
|
||||
|
||||
sfi._isMonitoring = buf->isMonitoringOn();
|
||||
sfi._individualTabColour = docTab[k]->getIndividualTabColourId(static_cast<int>(i));
|
||||
|
@ -2118,6 +2118,7 @@ bool Notepad_plus::fileRename(BufferID id)
|
||||
_pluginsManager.notify(&scnN);
|
||||
|
||||
success = true;
|
||||
buf->setUntitledTabRenamedStatus(true);
|
||||
|
||||
bool isSnapshotMode = NppParameters::getInstance().getNppGUI().isSnapshotMode();
|
||||
if (isSnapshotMode)
|
||||
@ -2194,6 +2195,8 @@ bool Notepad_plus::fileRenameUntitledPluginAPI(BufferID id, const wchar_t* tabNe
|
||||
scnN.nmhdr.code = NPPN_FILERENAMED;
|
||||
_pluginsManager.notify(&scnN);
|
||||
|
||||
buf->setUntitledTabRenamedStatus(true);
|
||||
|
||||
bool isSnapshotMode = NppParameters::getInstance().getNppGUI().isSnapshotMode();
|
||||
if (isSnapshotMode)
|
||||
{
|
||||
@ -2489,6 +2492,8 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch
|
||||
buf->setUserReadOnly(session._mainViewFiles[i]._isUserReadOnly);
|
||||
buf->setPinned(session._mainViewFiles[i]._isPinned);
|
||||
|
||||
buf->setUntitledTabRenamedStatus(session._mainViewFiles[i]._isUntitledTabRenamed);
|
||||
|
||||
if (isSnapshotMode && !session._mainViewFiles[i]._backupFilePath.empty() && doesFileExist(session._mainViewFiles[i]._backupFilePath.c_str()))
|
||||
buf->setDirty(true);
|
||||
|
||||
@ -2624,6 +2629,8 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch
|
||||
buf->setUserReadOnly(session._subViewFiles[k]._isUserReadOnly);
|
||||
buf->setPinned(session._subViewFiles[k]._isPinned);
|
||||
|
||||
buf->setUntitledTabRenamedStatus(session._subViewFiles[k]._isUntitledTabRenamed);
|
||||
|
||||
if (isSnapshotMode && !session._subViewFiles[k]._backupFilePath.empty() && doesFileExist(session._subViewFiles[k]._backupFilePath.c_str()))
|
||||
buf->setDirty(true);
|
||||
|
||||
|
@ -59,6 +59,59 @@ BOOL Notepad_plus::notify(SCNotification *notification)
|
||||
{
|
||||
// for the backup system
|
||||
_pEditView->getCurrentBuffer()->setModifiedStatus(true);
|
||||
|
||||
// auto make temporary name for untitled documents
|
||||
Buffer* buffer = notifyView->getCurrentBuffer();
|
||||
const NppGUI& nppGui = NppParameters::getInstance().getNppGUI();
|
||||
const NewDocDefaultSettings& ndds = nppGui.getNewDocDefaultSettings();
|
||||
intptr_t curLineIndex = _pEditView->execute(SCI_LINEFROMPOSITION, notification->position);
|
||||
if (curLineIndex == 0 && ndds._useContentAsTabName && buffer->isUntitled() && !buffer->isUntitledTabRenamed())
|
||||
{
|
||||
// make a temporary tab name from first line of document
|
||||
wstring content1stLineTabName = _pEditView->getLine(0);
|
||||
buffer->normalizeTabName(content1stLineTabName);
|
||||
|
||||
// check whether there is any buffer with the same name
|
||||
BufferID sameNamedBufferId = _pDocTab->findBufferByName(content1stLineTabName.c_str());
|
||||
if (sameNamedBufferId == BUFFER_INVALID)
|
||||
sameNamedBufferId = _pNonDocTab->findBufferByName(content1stLineTabName.c_str());
|
||||
|
||||
if (!content1stLineTabName.empty() && content1stLineTabName != buffer->getFileName() && sameNamedBufferId == BUFFER_INVALID)
|
||||
{
|
||||
// notify tab name changing
|
||||
SCNotification scnNotif{};
|
||||
scnNotif.nmhdr.code = NPPN_FILEBEFORERENAME;
|
||||
scnNotif.nmhdr.hwndFrom = _pPublicInterface->getHSelf();
|
||||
scnNotif.nmhdr.idFrom = (uptr_t)buffer->getID();
|
||||
_pluginsManager.notify(&scnNotif);
|
||||
|
||||
// backup old file path
|
||||
wstring oldFileNamePath = buffer->getFullPathName();
|
||||
|
||||
// set tab name
|
||||
buffer->setFileName(content1stLineTabName.c_str());
|
||||
|
||||
// notify tab renamed
|
||||
scnNotif.nmhdr.code = NPPN_FILERENAMED;
|
||||
_pluginsManager.notify(&scnNotif);
|
||||
|
||||
// for the backup system
|
||||
wstring oldBackUpFileName = buffer->getBackupFileName();
|
||||
bool isSnapshotMode = nppGui.isSnapshotMode();
|
||||
if (isSnapshotMode && !oldBackUpFileName.empty())
|
||||
{
|
||||
wstring newBackUpFileName = oldBackUpFileName;
|
||||
newBackUpFileName.replace(newBackUpFileName.rfind(oldFileNamePath), oldFileNamePath.length(), content1stLineTabName);
|
||||
|
||||
if (doesFileExist(newBackUpFileName.c_str()))
|
||||
::ReplaceFile(newBackUpFileName.c_str(), oldBackUpFileName.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS | REPLACEFILE_IGNORE_ACL_ERRORS, 0, 0);
|
||||
else
|
||||
::MoveFileEx(oldBackUpFileName.c_str(), newBackUpFileName.c_str(), MOVEFILE_REPLACE_EXISTING);
|
||||
|
||||
buffer->setBackupFileName(newBackUpFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notification->modificationType & SC_MOD_CHANGEINDICATOR)
|
||||
|
@ -2449,7 +2449,12 @@ bool NppParameters::getSessionFromXmlTree(TiXmlDocument *pSessionDoc, Session& s
|
||||
if (boolStrPinned)
|
||||
isPinned = _wcsicmp(L"yes", boolStrPinned) == 0;
|
||||
|
||||
sessionFileInfo sfi(fileName, langName, encStr ? encoding : -1, isUserReadOnly, isPinned, position, pBackupFilePath, fileModifiedTimestamp, mapPosition);
|
||||
bool isUntitleTabRenamed = false;
|
||||
const wchar_t* boolStrTabRenamed = (childNode->ToElement())->Attribute(L"untitleTabRenamed");
|
||||
if (boolStrTabRenamed)
|
||||
isUntitleTabRenamed = _wcsicmp(L"yes", boolStrTabRenamed) == 0;
|
||||
|
||||
sessionFileInfo sfi(fileName, langName, encStr ? encoding : -1, isUserReadOnly, isPinned, isUntitleTabRenamed, position, pBackupFilePath, fileModifiedTimestamp, mapPosition);
|
||||
|
||||
const wchar_t* intStrTabColour = (childNode->ToElement())->Attribute(L"tabColourId");
|
||||
if (intStrTabColour)
|
||||
@ -3674,6 +3679,9 @@ void NppParameters::writeSession(const Session & session, const wchar_t *fileNam
|
||||
(fileNameNode->ToElement())->SetAttribute(L"tabColourId", static_cast<int32_t>(viewSessionFiles[i]._individualTabColour));
|
||||
(fileNameNode->ToElement())->SetAttribute(L"RTL", viewSessionFiles[i]._isRTL ? L"yes" : L"no");
|
||||
(fileNameNode->ToElement())->SetAttribute(L"tabPinned", viewSessionFiles[i]._isPinned ? L"yes" : L"no");
|
||||
// Save this info only when it's an untitled entry
|
||||
if (viewSessionFiles[i]._isUntitledTabRenamed)
|
||||
(fileNameNode->ToElement())->SetAttribute(L"untitleTabRenamed", L"yes");
|
||||
|
||||
// docMap
|
||||
(fileNameNode->ToElement())->SetAttribute(L"mapFirstVisibleDisplayLine", _i64tot(static_cast<LONGLONG>(viewSessionFiles[i]._mapPos._firstVisibleDisplayLine), szInt64, 10));
|
||||
@ -5547,6 +5555,10 @@ void NppParameters::feedGUIParameters(TiXmlNode *node)
|
||||
val = element->Attribute(L"addNewDocumentOnStartup");
|
||||
if (val)
|
||||
_nppGUI._newDocDefaultSettings._addNewDocumentOnStartup = (lstrcmp(val, L"yes") == 0);
|
||||
|
||||
val = element->Attribute(L"useContentAsTabName");
|
||||
if (val)
|
||||
_nppGUI._newDocDefaultSettings._useContentAsTabName = (lstrcmp(val, L"yes") == 0);
|
||||
}
|
||||
|
||||
else if (!lstrcmp(nm, L"langsExcluded"))
|
||||
@ -7517,7 +7529,7 @@ void NppParameters::createXmlTreeFromGUIParams()
|
||||
insertGUIConfigBoolNode(newGUIRoot, L"SaveAllConfirm", _nppGUI._saveAllConfirm);
|
||||
}
|
||||
|
||||
// <GUIConfig name = "NewDocDefaultSettings" format = "0" encoding = "0" lang = "3" codepage = "-1" openAnsiAsUTF8 = "no" / >
|
||||
// <GUIConfig name = "NewDocDefaultSettings" format = "0" encoding = "0" lang = "3" codepage = "-1" openAnsiAsUTF8 = "no" useContentAsTabName = "no" / >
|
||||
{
|
||||
TiXmlElement *GUIConfigElement = (newGUIRoot->InsertEndChild(TiXmlElement(L"GUIConfig")))->ToElement();
|
||||
GUIConfigElement->SetAttribute(L"name", L"NewDocDefaultSettings");
|
||||
@ -7527,6 +7539,7 @@ void NppParameters::createXmlTreeFromGUIParams()
|
||||
GUIConfigElement->SetAttribute(L"codepage", _nppGUI._newDocDefaultSettings._codepage);
|
||||
GUIConfigElement->SetAttribute(L"openAnsiAsUTF8", _nppGUI._newDocDefaultSettings._openAnsiAsUtf8 ? L"yes" : L"no");
|
||||
GUIConfigElement->SetAttribute(L"addNewDocumentOnStartup", _nppGUI._newDocDefaultSettings._addNewDocumentOnStartup ? L"yes" : L"no");
|
||||
GUIConfigElement->SetAttribute(L"useContentAsTabName", _nppGUI._newDocDefaultSettings._useContentAsTabName ? L"yes" : L"no");
|
||||
}
|
||||
|
||||
// <GUIConfig name = "langsExcluded" gr0 = "0" gr1 = "0" gr2 = "0" gr3 = "0" gr4 = "0" gr5 = "0" gr6 = "0" gr7 = "0" langMenuCompact = "yes" / >
|
||||
|
@ -219,8 +219,8 @@ public:
|
||||
|
||||
struct sessionFileInfo : public Position
|
||||
{
|
||||
sessionFileInfo(const wchar_t* fn, const wchar_t *ln, int encoding, bool userReadOnly,bool isPinned, const Position& pos, const wchar_t *backupFilePath, FILETIME originalFileLastModifTimestamp, const MapPosition & mapPos) :
|
||||
Position(pos), _encoding(encoding), _isUserReadOnly(userReadOnly), _isPinned(isPinned), _originalFileLastModifTimestamp(originalFileLastModifTimestamp), _mapPos(mapPos)
|
||||
sessionFileInfo(const wchar_t* fn, const wchar_t *ln, int encoding, bool userReadOnly,bool isPinned, bool isUntitleTabRenamed, const Position& pos, const wchar_t *backupFilePath, FILETIME originalFileLastModifTimestamp, const MapPosition & mapPos) :
|
||||
Position(pos), _encoding(encoding), _isUserReadOnly(userReadOnly), _isPinned(isPinned), _isUntitledTabRenamed(isUntitleTabRenamed), _originalFileLastModifTimestamp(originalFileLastModifTimestamp), _mapPos(mapPos)
|
||||
{
|
||||
if (fn) _fileName = fn;
|
||||
if (ln) _langName = ln;
|
||||
@ -239,6 +239,7 @@ struct sessionFileInfo : public Position
|
||||
int _individualTabColour = -1;
|
||||
bool _isRTL = false;
|
||||
bool _isPinned = false;
|
||||
bool _isUntitledTabRenamed = false;
|
||||
std::wstring _backupFilePath;
|
||||
FILETIME _originalFileLastModifTimestamp {};
|
||||
|
||||
@ -609,6 +610,7 @@ struct NewDocDefaultSettings final
|
||||
LangType _lang = L_TEXT;
|
||||
int _codepage = -1; // -1 when not using
|
||||
bool _addNewDocumentOnStartup = false;
|
||||
bool _useContentAsTabName = false;
|
||||
};
|
||||
|
||||
|
||||
|
@ -37,6 +37,10 @@ static const int LF = 0x0A;
|
||||
|
||||
long Buffer::_recentTagCtr = 0;
|
||||
|
||||
// Invalid characters for a file name
|
||||
// Refer: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file
|
||||
// Including 'tab' and 'return/new line' characters
|
||||
const wchar_t* fileNameInvalidChars = L"\\/:*?\"<>|\t\r\n";
|
||||
|
||||
namespace // anonymous
|
||||
{
|
||||
@ -304,6 +308,44 @@ void Buffer::setFileName(const wchar_t *fn)
|
||||
doNotify(BufferChangeFilename | BufferChangeTimestamp | lang2Change);
|
||||
}
|
||||
|
||||
void Buffer::normalizeTabName(wstring& tabName)
|
||||
{
|
||||
if (!tabName.empty())
|
||||
{
|
||||
// remove leading/trailing spaces
|
||||
trim(tabName);
|
||||
|
||||
// remove invalid characters
|
||||
wstring tempStr;
|
||||
for (wchar_t ch : tabName)
|
||||
{
|
||||
bool isInvalid = false;
|
||||
for (const wchar_t* p = fileNameInvalidChars; *p != L'\0'; ++p)
|
||||
{
|
||||
if (ch == *p)
|
||||
{
|
||||
isInvalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isInvalid)
|
||||
tempStr += ch;
|
||||
}
|
||||
tabName = tempStr;
|
||||
|
||||
// restrict length
|
||||
if (tabName.length() >= langNameLenMax - 1)
|
||||
{
|
||||
tempStr = tabName.substr(0, langNameLenMax - 1);
|
||||
tabName = tempStr;
|
||||
}
|
||||
|
||||
// remove leading/trailing spaces again
|
||||
trim(tabName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Buffer::checkFileState() // returns true if the status has been changed (it can change into DOC_REGULAR too). false otherwise
|
||||
{
|
||||
|
@ -170,6 +170,8 @@ public:
|
||||
|
||||
const wchar_t * getFileName() const { return _fileName; }
|
||||
|
||||
void normalizeTabName(std::wstring& tabName);
|
||||
|
||||
BufferID getID() const { return _id; }
|
||||
|
||||
void increaseRecentTag() {
|
||||
@ -269,6 +271,9 @@ public:
|
||||
doNotify(BufferChangeLexing);
|
||||
}
|
||||
|
||||
bool isUntitledTabRenamed() const { return _isUntitledTabRenamed; }
|
||||
void setUntitledTabRenamedStatus(bool isRenamed) { _isUntitledTabRenamed = isRenamed; }
|
||||
|
||||
//these two return reference count after operation
|
||||
int addReference(ScintillaEditView * identifier); //if ID not registered, creates a new Position for that ID and new foldstate
|
||||
int removeReference(const ScintillaEditView * identifier); //reduces reference. If zero, Document is purged
|
||||
@ -399,6 +404,8 @@ private:
|
||||
bool _needLexer = false; // new buffers do not need lexing, Scintilla takes care of that
|
||||
//these properties have to be duplicated because of multiple references
|
||||
|
||||
bool _isUntitledTabRenamed = false;
|
||||
|
||||
//All the vectors must have the same size at all times
|
||||
std::vector<ScintillaEditView *> _referees; // Instances of ScintillaEditView which contain this buffer
|
||||
std::vector<Position> _positions;
|
||||
|
@ -248,7 +248,7 @@ IDD_PREFERENCE_SUB_NEWDOCUMENT DIALOGEX 115, 10, 460, 205
|
||||
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
|
||||
FONT 8, "MS Shell Dlg", 0, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "New Document",IDC_NEWDOCUMENT_GR_STATIC,15,8,424,161,BS_CENTER
|
||||
GROUPBOX "New Document",IDC_NEWDOCUMENT_GR_STATIC,15,8,424,174,BS_CENTER
|
||||
GROUPBOX "Format (Line ending)",IDC_FORMAT_GB_STATIC,58,29,129,79,BS_CENTER
|
||||
CONTROL "Windows (CR LF)",IDC_RADIO_F_WIN,"Button",BS_AUTORADIOBUTTON | WS_GROUP,65,48,70,10
|
||||
CONTROL "Unix (LF)",IDC_RADIO_F_UNIX,"Button",BS_AUTORADIOBUTTON,65,64,70,10
|
||||
@ -265,6 +265,7 @@ BEGIN
|
||||
RTEXT "Default language:",IDC_DEFAULTLANG_STATIC,16,125,77,8
|
||||
COMBOBOX IDC_COMBO_DEFAULTLANG,98,123,100,140,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
|
||||
CONTROL "Always open a new document in addition at startup",IDC_CHECK_ADDNEWDOCONSTARTUP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,23,152,250,10
|
||||
CONTROL "Use the first line of document as untitled tab name",IDC_CHECK_USECONTENTASTABNAME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,23,165,250,10
|
||||
END
|
||||
|
||||
|
||||
|
@ -3419,6 +3419,8 @@ intptr_t CALLBACK NewDocumentSubDlg::run_dlgProc(UINT message, WPARAM wParam, LP
|
||||
|
||||
::SendDlgItemMessage(_hSelf, IDC_CHECK_ADDNEWDOCONSTARTUP, BM_SETCHECK, ndds._addNewDocumentOnStartup, 0);
|
||||
|
||||
::SendDlgItemMessage(_hSelf, IDC_CHECK_USECONTENTASTABNAME, BM_SETCHECK, ndds._useContentAsTabName, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -3516,6 +3518,12 @@ intptr_t CALLBACK NewDocumentSubDlg::run_dlgProc(UINT message, WPARAM wParam, LP
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case IDC_CHECK_USECONTENTASTABNAME:
|
||||
{
|
||||
ndds._useContentAsTabName = isCheckedOrNot(IDC_CHECK_USECONTENTASTABNAME);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if (HIWORD(wParam) == CBN_SELCHANGE)
|
||||
|
@ -318,6 +318,7 @@
|
||||
#define IDC_DISPLAY_STATIC 6429
|
||||
#define IDC_OPENSAVEDIR_CHECK_DROPFOLDEROPENFILES 6431
|
||||
#define IDC_CHECK_ADDNEWDOCONSTARTUP 6432
|
||||
#define IDC_CHECK_USECONTENTASTABNAME 6433
|
||||
|
||||
#define IDD_PREFERENCE_SUB_DEFAULTDIRECTORY 6450
|
||||
#define IDD_PREFERENCE_SUB_RECENTFILESHISTORY 6460
|
||||
|
Loading…
x
Reference in New Issue
Block a user