diff --git a/src/ColumnsPlusPlus.cpp b/src/ColumnsPlusPlus.cpp index aa5c331..8c42b22 100644 --- a/src/ColumnsPlusPlus.cpp +++ b/src/ColumnsPlusPlus.cpp @@ -246,6 +246,15 @@ bool ElasticProgressInfo::setTabstops(bool stepless) { void ColumnsPlusPlusData::analyzeTabstops(DocumentData& dd) { + dd.elasticAnalysisRequired = false; + dd.deleteWithoutLayoutChange = false; + dd.width24b = sci.TextWidth(STYLE_DEFAULT, " "); + dd.width24d = sci.TextWidth(STYLE_DEFAULT, "123456789012345678901234"); + dd.width24w = sci.TextWidth(STYLE_DEFAULT, "WWWWWWWWWWWWWWWWWWWWWWWW"); + dd.assumeMonospace = dd.settings.monospace == ElasticTabsProfile::MonospaceBest ? guessMonospaced() + : dd.settings.monospace == ElasticTabsProfile::MonospaceAlways; + int ccsym = settings.monospaceNoMnemonics && dd.assumeMonospace ? '!' : 0; + if (sci.ControlCharSymbol() != ccsym) sci.SetControlCharSymbol(ccsym); ElasticProgressInfo epi(*this, dd); epi.isAnalyze = true; epi.firstNeeded = 0; @@ -272,8 +281,6 @@ void ColumnsPlusPlusData::analyzeTabstops(DocumentData& dd) { bool ElasticProgressInfo::analyzeTabstops() { auto& sci = data.sci; - dd.elasticAnalysisRequired = false; - dd.deleteWithoutLayoutChange = false; const Scintilla::Line firstToProcess = step * stepSize; const Scintilla::Line lastToProcess = std::min(lastNeeded, firstToProcess + stepSize - 1); if (!step) dd.tabLayouts.clear(); @@ -414,7 +421,7 @@ void ColumnsPlusPlusData::scnModified(const Scintilla::NotificationData* scnp) { int width; if (findTabLayoutBlock(ctd, scnp->position, scnp->length, tlb, width)) { width += sci.TextWidth(STYLE_DEFAULT, std::string(ctd.settings.minimumSpaceBetweenColumns, ' ').data()); - if (!tlb || width < tlb->width) { + if (!tlb || width < tlb->width - 1) /* a one-pixel error is possible with DirectWrite and monospace font optimization */ { ctd.deleteWithoutLayoutChange = true; ctd.deleteWithoutLayoutChangePosition = scnp->position; ctd.deleteWithoutLayoutChangeLength = scnp->length; @@ -441,11 +448,7 @@ void ColumnsPlusPlusData::scnUpdateUI(const Scintilla::NotificationData* scnp) { if (Scintilla::FlagSet(scnp->updated, Scintilla::Update::Selection)) syncFindButton(); if (!ddp->settings.elasticEnabled) return; ddp->deleteWithoutLayoutChange = false; - if (fontSpacingChange(*ddp)) { - setSpacing(*ddp); - analyzeTabstops(*ddp); - } - else if (ddp->elasticAnalysisRequired) analyzeTabstops(*ddp); + if (ddp->elasticAnalysisRequired || fontSpacingChange(*ddp)) analyzeTabstops(*ddp); if (Scintilla::FlagSet(scnp->updated, Scintilla::Update::Selection)) reselectRectangularSelectionAndControlCharSymbol(*ddp, false); setTabstops(*ddp); } @@ -455,7 +458,6 @@ void ColumnsPlusPlusData::scnZoom(const Scintilla::NotificationData* scnp) { DocumentData* ddp = getDocument(scnp); if (!ddp || !ddp->settings.elasticEnabled) return; ddp->deleteWithoutLayoutChange = false; - setSpacing(*ddp); analyzeTabstops(*ddp); setTabstops(*ddp); } @@ -496,7 +498,6 @@ void ColumnsPlusPlusData::bufferActivated() { } if (settings.overrideTabSize) sci.SetTabWidth(settings.minimumOrLeadingTabSize); if (isNewDocument) { - setSpacing(dd); analyzeTabstops(dd); setTabstops(dd); } @@ -526,17 +527,11 @@ void ColumnsPlusPlusData::modifyAll(const NMHDR* nmhdr) { position &= 0x3FFFFFFF; intptr_t index = SendMessage(nppData._nppHandle, NPPM_GETCURRENTDOCINDEX, 0, secondary); if (index < 0) return; - if (position == index) /* buffer is visible (notification happens, or at least can happen, after scnUpdateUI due to changes) */ { - activeScintilla = secondary ? nppData._scintillaSecondHandle : nppData._scintillaMainHandle; - pointerScintilla = SendMessage(activeScintilla, static_cast(Scintilla::Message::GetDirectPointer), 0, 0); - sci.SetFnPtr(directStatusScintilla, pointerScintilla); - sci.SetStatus(Scintilla::Status::Ok); - void* docptr = sci.DocPointer(); - if (!documents.contains(docptr)) return; - DocumentData& dd = documents[docptr]; - if (fontSpacingChange(dd)) setSpacing(dd); - analyzeTabstops(dd); - setTabstops(dd); + if (position == index) /* buffer is visible */ { + DocumentData* ddp = getDocument(secondary ? nppData._scintillaSecondHandle : nppData._scintillaMainHandle); + if (!ddp) return; + analyzeTabstops(*ddp); + setTabstops(*ddp); return; } for (auto i = documents.begin(); i != documents.end(); ++i) if (i->second.buffer == bufferID) { @@ -557,7 +552,6 @@ void ColumnsPlusPlusData::toggleElasticEnabled() { sci.SetTabWidth(settings.minimumOrLeadingTabSize); } sci.SetTabIndents(0); - setSpacing(*ddp); analyzeTabstops(*ddp); setTabstops(*ddp); } else { diff --git a/src/ColumnsPlusPlus.h b/src/ColumnsPlusPlus.h index ed2433f..d6a5cc4 100644 --- a/src/ColumnsPlusPlus.h +++ b/src/ColumnsPlusPlus.h @@ -303,7 +303,9 @@ class DocumentData { DocumentDataSettings settings; std::vector tabLayouts; UINT_PTR buffer; // identifier used by Notepad++ messages and notifications - int blankWidth = 0; // the blank width at which tabLayouts were calculated; if this changes, full analysis is required + int width24b = 0; // the widths of 24 blanks, 24 digits and 24 capital W letters + int width24d = 0; // at which tabLayouts were calculated; + int width24w = 0; // if these values change, full analysis is required int tabOriginal; // set when buffer activated, used for restore when elasticTabsEnabled or overrideTabSize turned off bool elasticAnalysisRequired = false; bool deleteWithoutLayoutChange = false; @@ -398,8 +400,8 @@ class ColumnsPlusPlusData { return ⅆ } - DocumentData* getDocument(const Scintilla::NotificationData* scnp) { - activeScintilla = reinterpret_cast(scnp->nmhdr.hwndFrom); + DocumentData* getDocument(HWND scintillaHandle) { + activeScintilla = scintillaHandle; pointerScintilla = SendMessage(activeScintilla, static_cast(Scintilla::Message::GetDirectPointer), 0, 0); sci.SetFnPtr(directStatusScintilla, pointerScintilla); sci.SetStatus(Scintilla::Status::Ok); // C-interface code can ignore an error status, which would cause the C++ interface to raise an exception @@ -408,33 +410,30 @@ class ColumnsPlusPlusData { return &documents[docptr]; } + DocumentData* getDocument(const Scintilla::NotificationData* scnp) { return getDocument(reinterpret_cast(scnp->nmhdr.hwndFrom)); } + RectangularSelection getRectangularSelection(); bool fontSpacingChange(DocumentData& dd) { - int width = sci.TextWidth(STYLE_DEFAULT, " "); - if (width != dd.blankWidth) return true; - if (dd.settings.monospace != ElasticTabsProfile::MonospaceBest) return false; - return dd.assumeMonospace != guessMonospaced(width); + if (dd.width24b != sci.TextWidth(STYLE_DEFAULT, " ")) return true; + if (dd.width24d != sci.TextWidth(STYLE_DEFAULT, "123456789012345678901234")) return true; + if (dd.width24w != sci.TextWidth(STYLE_DEFAULT, "WWWWWWWWWWWWWWWWWWWWWWWW")) return true; + return false; } - bool guessMonospaced(int width = 0) { - if (!width) width = sci.TextWidth(STYLE_DEFAULT, " "); - if (sci.TextWidth(STYLE_DEFAULT, "W") != width) return false; + bool guessMonospaced() { + int width24b = sci.TextWidth(STYLE_DEFAULT, " "); + int width24w = sci.TextWidth(STYLE_DEFAULT, "WWWWWWWWWWWWWWWWWWWWWWWW"); + if (width24b != width24w) return false; for (int style = 0; style < STYLE_DEFAULT; ++style) - if (sci.TextWidth(style, "W") != width || sci.TextWidth(style, " ") != width) return false; + if ( sci.TextWidth(style, "WWWWWWWWWWWWWWWWWWWWWWWW") != width24b + || sci.TextWidth(style, " ") != width24b) return false; for (int style = STYLE_LASTPREDEFINED + 1; style < 256; ++style) - if (sci.TextWidth(style, "W") != width || sci.TextWidth(style, " ") != width) return false; + if ( sci.TextWidth(style, "WWWWWWWWWWWWWWWWWWWWWWWW") != width24b + || sci.TextWidth(style, " ") != width24b) return false; return true; } - void setSpacing(DocumentData& dd) { - dd.blankWidth = sci.TextWidth(STYLE_DEFAULT, " "); - dd.assumeMonospace = dd.settings.monospace == ElasticTabsProfile::MonospaceBest ? guessMonospaced(dd.blankWidth) - : dd.settings.monospace == ElasticTabsProfile::MonospaceAlways; - int ccsym = settings.monospaceNoMnemonics && dd.assumeMonospace ? '!' : 0; - if (sci.ControlCharSymbol() != ccsym) sci.SetControlCharSymbol(ccsym); - } - void reselectRectangularSelectionAndControlCharSymbol(DocumentData& dd, bool setControlCharSymbol) { if (!dd.settings.elasticEnabled) return; Scintilla::SelectionMode selectionMode = sci.SelectionMode(); diff --git a/src/Profiles.cpp b/src/Profiles.cpp index 2ae5356..97100d6 100644 --- a/src/Profiles.cpp +++ b/src/Profiles.cpp @@ -43,7 +43,6 @@ void ColumnsPlusPlusData::showElasticProfile() { ddp->settings = settings; if (settings.elasticEnabled) { sci.SetTabWidth(settings.overrideTabSize ? settings.minimumOrLeadingTabSize : ddp->tabOriginal); - setSpacing(*ddp); analyzeTabstops(*ddp); setTabstops(*ddp); }