// // Copyright (c) 1990-2011, Scientific Toolworks, Inc. // // The License.txt file describes the conditions under which this software may be distributed. // // Author: Jason Haslam // // Additions Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware // ScintillaQt.cpp - Qt specific subclass of ScintillaBase #include "PlatQt.h" #include "ScintillaQt.h" #ifdef SCI_LEXER #include "LexerModule.h" #include "ExternalLexer.h" #endif #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #endif #include #include #include #include #include #ifdef SCI_NAMESPACE using namespace Scintilla; #endif ScintillaQt::ScintillaQt(QAbstractScrollArea *parent) : QObject(parent), scrollArea(parent), vMax(0), hMax(0), vPage(0), hPage(0), haveMouseCapture(false), dragWasDropped(false) { wMain = scrollArea->viewport(); // On OS X drawing text into a pixmap moves it around 1 pixel to // the right compared to drawing it directly onto a window. // Buffered drawing turned off by default to avoid this. WndProc(SCI_SETBUFFEREDDRAW, false, 0); Initialise(); for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { timers[tr] = 0; } } ScintillaQt::~ScintillaQt() { for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { FineTickerCancel(tr); } SetIdle(false); } void ScintillaQt::execCommand(QAction *action) { int command = action->data().toInt(); Command(command); } #if defined(Q_OS_WIN) static const QString sMSDEVColumnSelect("MSDEVColumnSelect"); static const QString sWrappedMSDEVColumnSelect("application/x-qt-windows-mime;value=\"MSDEVColumnSelect\""); #elif defined(Q_OS_MAC) static const QString sScintillaRecPboardType("com.scintilla.utf16-plain-text.rectangular"); static const QString sScintillaRecMimeType("text/x-scintilla.utf16-plain-text.rectangular"); #else // Linux static const QString sMimeRectangularMarker("text/x-rectangular-marker"); #endif #if defined(Q_OS_MAC) && QT_VERSION < QT_VERSION_CHECK(5, 0, 0) class ScintillaRectangularMime : public QMacPasteboardMime { public: ScintillaRectangularMime() : QMacPasteboardMime(MIME_ALL) { } QString convertorName() { return QString("ScintillaRectangularMime"); } bool canConvert(const QString &mime, QString flav) { return mimeFor(flav) == mime; } QString mimeFor(QString flav) { if (flav == sScintillaRecPboardType) return sScintillaRecMimeType; return QString(); } QString flavorFor(const QString &mime) { if (mime == sScintillaRecMimeType) return sScintillaRecPboardType; return QString(); } QVariant convertToMime(const QString & /* mime */, QList data, QString /* flav */) { QByteArray all; foreach (QByteArray i, data) { all += i; } return QVariant(all); } QList convertFromMime(const QString & /* mime */, QVariant data, QString /* flav */) { QByteArray a = data.toByteArray(); QList l; l.append(a); return l; } }; // The Mime object registers itself but only want one for all Scintilla instances. // Should delete at exit to help memory leak detection but that would be extra work // and, since the clipboard exists after last Scintilla instance, may be complex. static ScintillaRectangularMime *singletonMime = 0; #endif void ScintillaQt::Initialise() { #if defined(Q_OS_WIN) || defined(Q_OS_MAC) rectangularSelectionModifier = SCMOD_ALT; #else rectangularSelectionModifier = SCMOD_CTRL; #endif #if defined(Q_OS_MAC) && QT_VERSION < QT_VERSION_CHECK(5, 0, 0) if (!singletonMime) { singletonMime = new ScintillaRectangularMime(); QStringList slTypes(sScintillaRecPboardType); qRegisterDraggedTypes(slTypes); } #endif connect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, SLOT(SelectionChanged())); } void ScintillaQt::Finalise() { for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { FineTickerCancel(tr); } ScintillaBase::Finalise(); } void ScintillaQt::SelectionChanged() { bool nowPrimary = QApplication::clipboard()->ownsSelection(); if (nowPrimary != primarySelection) { primarySelection = nowPrimary; Redraw(); } } bool ScintillaQt::DragThreshold(Point ptStart, Point ptNow) { int xMove = abs(ptStart.x - ptNow.x); int yMove = abs(ptStart.y - ptNow.y); return (xMove > QApplication::startDragDistance()) || (yMove > QApplication::startDragDistance()); } static QString StringFromSelectedText(const SelectionText &selectedText) { if (selectedText.codePage == SC_CP_UTF8) { return QString::fromUtf8(selectedText.Data(), static_cast(selectedText.Length())); } else { QTextCodec *codec = QTextCodec::codecForName( CharacterSetID(selectedText.characterSet)); return codec->toUnicode(selectedText.Data(), static_cast(selectedText.Length())); } } static void AddRectangularToMime(QMimeData *mimeData, QString su) { #if defined(Q_OS_WIN) // Add an empty marker mimeData->setData(sMSDEVColumnSelect, QByteArray()); #elif defined(Q_OS_MAC) // OS X gets marker + data to work with other implementations. // Don't understand how this works but it does - the // clipboard format is supposed to be UTF-16, not UTF-8. mimeData->setData(sScintillaRecMimeType, su.toUtf8()); #else Q_UNUSED(su); // Linux // Add an empty marker mimeData->setData(sMimeRectangularMarker, QByteArray()); #endif } static bool IsRectangularInMime(const QMimeData *mimeData) { QStringList formats = mimeData->formats(); for (int i = 0; i < formats.size(); ++i) { #if defined(Q_OS_WIN) // Windows rectangular markers // If rectangular copies made by this application, see base name. if (formats[i] == sMSDEVColumnSelect) return true; // Otherwise see wrapped name. if (formats[i] == sWrappedMSDEVColumnSelect) return true; #elif defined(Q_OS_MAC) if (formats[i] == sScintillaRecMimeType) return true; #else // Linux if (formats[i] == sMimeRectangularMarker) return true; #endif } return false; } bool ScintillaQt::ValidCodePage(int codePage) const { return codePage == 0 || codePage == SC_CP_UTF8 || codePage == 932 || codePage == 936 || codePage == 949 || codePage == 950 || codePage == 1361; } void ScintillaQt::ScrollText(int linesToMove) { int dy = vs.lineHeight * (linesToMove); scrollArea->viewport()->scroll(0, dy); } void ScintillaQt::SetVerticalScrollPos() { scrollArea->verticalScrollBar()->setValue(topLine); emit verticalScrolled(topLine); } void ScintillaQt::SetHorizontalScrollPos() { scrollArea->horizontalScrollBar()->setValue(xOffset); emit horizontalScrolled(xOffset); } bool ScintillaQt::ModifyScrollBars(int nMax, int nPage) { bool modified = false; int vNewPage = nPage; int vNewMax = nMax - vNewPage + 1; if (vMax != vNewMax || vPage != vNewPage) { vMax = vNewMax; vPage = vNewPage; modified = true; scrollArea->verticalScrollBar()->setMaximum(vMax); scrollArea->verticalScrollBar()->setPageStep(vPage); emit verticalRangeChanged(vMax, vPage); } int hNewPage = GetTextRectangle().Width(); int hNewMax = (scrollWidth > hNewPage) ? scrollWidth - hNewPage : 0; int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth; if (hMax != hNewMax || hPage != hNewPage || scrollArea->horizontalScrollBar()->singleStep() != charWidth) { hMax = hNewMax; hPage = hNewPage; modified = true; scrollArea->horizontalScrollBar()->setMaximum(hMax); scrollArea->horizontalScrollBar()->setPageStep(hPage); scrollArea->horizontalScrollBar()->setSingleStep(charWidth); emit horizontalRangeChanged(hMax, hPage); } return modified; } void ScintillaQt::ReconfigureScrollBars() { if (verticalScrollBarVisible) { scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } else { scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } if (horizontalScrollBarVisible && !Wrapping()) { scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } else { scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } } void ScintillaQt::CopyToModeClipboard(const SelectionText &selectedTeHTTP/1.1 200 OK Connection: keep-alive Connection: keep-alive Content-Disposition: inline; filename="ScintillaQt.cpp" Content-Disposition: inline; filename="ScintillaQt.cpp" Content-Length: 19506 Content-Length: 19506 Content-Security-Policy: default-src 'none' Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Type: text/plain; charset=UTF-8 Date: Mon, 24 Nov 2025 07:08:20 UTC ETag: "c2b250a3e77b9ae15008db2f4bd874a724d16293" ETag: "c2b250a3e77b9ae15008db2f4bd874a724d16293" Expires: Thu, 22 Nov 2035 07:08:20 GMT Expires: Thu, 22 Nov 2035 07:08:21 GMT Last-Modified: Mon, 24 Nov 2025 07:08:20 GMT Last-Modified: Mon, 24 Nov 2025 07:08:21 GMT Server: OpenBSD httpd Server: OpenBSD httpd X-Content-Type-Options: nosniff X-Content-Type-Options: nosniff // // Copyright (c) 1990-2011, Scientific Toolworks, Inc. // // The License.txt file describes the conditions under which this software may be distributed. // // Author: Jason Haslam // // Additions Copyright (c) 2011 Archaeopteryx Software, Inc. d/b/a Wingware // ScintillaQt.cpp - Qt specific subclass of ScintillaBase #include "PlatQt.h" #include "ScintillaQt.h" #ifdef SCI_LEXER #include "LexerModule.h" #include "ExternalLexer.h" #endif #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #endif #include #include #include #include #include #ifdef SCI_NAMESPACE using namespace Scintilla; #endif ScintillaQt::ScintillaQt(QAbstractScrollArea *parent) : QObject(parent), scrollArea(parent), vMax(0), hMax(0), vPage(0), hPage(0), haveMouseCapture(false), dragWasDropped(false) { wMain = scrollArea->viewport(); // On OS X drawing text into a pixmap moves it around 1 pixel to // the right compared to drawing it directly onto a window. // Buffered drawing turned off by default to avoid this. WndProc(SCI_SETBUFFEREDDRAW, false, 0); Initialise(); for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { timers[tr] = 0; } } ScintillaQt::~ScintillaQt() { for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { FineTickerCancel(tr); } SetIdle(false); } void ScintillaQt::execCommand(QAction *action) { int command = action->data().toInt(); Command(command); } #if defined(Q_OS_WIN) static const QString sMSDEVColumnSelect("MSDEVColumnSelect"); static const QString sWrappedMSDEVColumnSelect("application/x-qt-windows-mime;value=\"MSDEVColumnSelect\""); #elif defined(Q_OS_MAC) static const QString sScintillaRecPboardType("com.scintilla.utf16-plain-text.rectangular"); static const QString sScintillaRecMimeType("text/x-scintilla.utf16-plain-text.rectangular"); #else // Linux static const QString sMimeRectangularMarker("text/x-rectangular-marker"); #endif #if defined(Q_OS_MAC) && QT_VERSION < QT_VERSION_CHECK(5, 0, 0) class ScintillaRectangularMime : public QMacPasteboardMime { public: ScintillaRectangularMime() : QMacPasteboardMime(MIME_ALL) { } QString convertorName() { return QString("ScintillaRectangularMime"); } bool canConvert(const QString &mime, QString flav) { return mimeFor(flav) == mime; } QString mimeFor(QString flav) { if (flav == sScintillaRecPboardType) return sScintillaRecMimeType; return QString(); } QString flavorFor(const QString &mime) { if (mime == sScintillaRecMimeType) return sScintillaRecPboardType; return QString(); } QVariant convertToMime(const QString & /* mime */, QList data, QString /* flav */) { QByteArray all; foreach (QByteArray i, data) { all += i; } return QVariant(all); } QList convertFromMime(const QString & /* mime */, QVariant data, QString /* flav */) { QByteArray a = data.toByteArray(); QList l; l.append(a); return l; } }; // The Mime object registers itself but only want one for all Scintilla instances. // Should delete at exit to help memory leak detection but that would be extra work // and, since the clipboard exists after last Scintilla instance, may be complex. static ScintillaRectangularMime *singletonMime = 0; #endif void ScintillaQt::Initialise() { #if defined(Q_OS_WIN) || defined(Q_OS_MAC) rectangularSelectionModifier = SCMOD_ALT; #else rectangularSelectionModifier = SCMOD_CTRL; #endif #if defined(Q_OS_MAC) && QT_VERSION < QT_VERSION_CHECK(5, 0, 0) if (!singletonMime) { singletonMime = new ScintillaRectangularMime(); QStringList slTypes(sScintillaRecPboardType); qRegisterDraggedTypes(slTypes); } #endif connect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, SLOT(SelectionChanged())); } void ScintillaQt::Finalise() { for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast(tr + 1)) { FineTickerCancel(tr); } ScintillaBase::Finalise(); } void ScintillaQt::SelectionChanged() { bool nowPrimary = QApplication::clipboard()->ownsSelection(); if (nowPrimary != primarySelection) { primarySelection = nowPrimary; Redraw(); } } bool ScintillaQt::DragThreshold(Point ptStart, Point ptNow) { int xMove = abs(ptStart.x - ptNow.x); int yMove = abs(ptStart.y - ptNow.y); return (xMove > QApplication::startDragDistance()) || (yMove > QApplication::startDragDistance()); } static QString StringFromSelectedText(const SelectionText &selectedText) { if (selectedText.codePage == SC_CP_UTF8) { return QString::fromUtf8(selectedText.Data(), static_cast(selectedText.Length())); } else { QTextCodec *codec = QTextCodec::codecForName( CharacterSetID(selectedText.characterSet)); return codec->toUnicode(selectedText.Data(), static_cast(selectedText.Length())); } } static void AddRectangularToMime(QMimeData *mimeData, QString su) { #if defined(Q_OS_WIN) // Add an empty marker mimeData->setData(sMSDEVColumnSelect, QByteArray()); #elif defined(Q_OS_MAC) // OS X gets marker + data to work with other implementations. // Don't understand how this works but it does - the // clipboard format is supposed to be UTF-16, not UTF-8. mimeData->setData(sScintillaRecMimeType, su.toUtf8()); #else Q_UNUSED(su); // Linux // Add an empty marker mimeData->setData(sMimeRectangularMarker, QByteArray()); #endif } static bool IsRectangularInMime(const QMimeData *mimeData) { QStringList formats = mimeData->formats(); for (int i = 0; i < formats.size(); ++i) { #if defined(Q_OS_WIN) // Windows rectangular markers // If rectangular copies made by this application, see base name. if (formats[i] == sMSDEVColumnSelect) return true; // Otherwise see wrapped name. if (formats[i] == sWrappedMSDEVColumnSelect) return true; #elif defined(Q_OS_MAC) if (formats[i] == sScintillaRecMimeType) return true; #else // Linux if (formats[i] == sMimeRectangularMarker) return true; #endif } return false; } bool ScintillaQt::ValidCodePage(int codePage) const { return codePage == 0 || codePage == SC_CP_UTF8 || codePage == 932 || codePage == 936 || codePage == 949 || codePage == 950 || codePage == 1361; } void ScintillaQt::ScrollText(int linesToMove) { int dy = vs.lineHeight * (linesToMove); scrollArea->viewport()->scroll(0, dy); } void ScintillaQt::SetVerticalScrollPos() { scrollArea->verticalScrollBar()->setValue(topLine); emit verticalScrolled(topLine); } void ScintillaQt::SetHorizontalScrollPos() { scrollArea->horizontalScrollBar()->setValue(xOffset); emit horizontalScrolled(xOffset); } bool ScintillaQt::ModifyScrollBars(int nMax, int nPage) { bool modified = false; int vNewPage = nPage; int vNewMax = nMax - vNewPage + 1; if (vMax != vNewMax || vPage != vNewPage) { vMax = vNewMax; vPage = vNewPage; modified = true; scrollArea->verticalScrollBar()->setMaximum(vMax); scrollArea->verticalScrollBar()->setPageStep(vPage); emit verticalRangeChanged(vMax, vPage); } int hNewPage = GetTextRectangle().Width(); int hNewMax = (scrollWidth > hNewPage) ? scrollWidth - hNewPage : 0; int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth; if (hMax != hNewMax || hPage != hNewPage || scrollArea->horizontalScrollBar()->singleStep() != charWidth) { hMax = hNewMax; hPage = hNewPage; modified = true; scrollArea->horizontalScrollBar()->setMaximum(hMax); scrollArea->horizontalScrollBar()->setPageStep(hPage); scrollArea->horizontalScrollBar()->setSingleStep(charWidth); emit horizontalRangeChanged(hMax, hPage); } return modified; } void ScintillaQt::ReconfigureScrollBars() { if (verticalScrollBarVisible) { scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } else { scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } if (horizontalScrollBarVisible && !Wrapping()) { scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } else { scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } } void ScintillaQt::CopyToModeClipboard(const SelectionText &selectedText, QClipboard::Mode clipboardMode_) { QClipboard *clipboard = QApplication::clipboard(); clipboard->clear(clipboardMode_); QString su = StringFromSelectedText(selectedText); QMimeData *mimeData = new QMimeData(); mimeData->setText(su); if (selectedText.rectangular) { AddRectangularToMime(mimeData, su); } // Allow client code to add additional data (e.g rich text). emit aboutToCopy(mimeData); clipboard->setMimeData(mimeData, clipboardMode_); } void ScintillaQt::Copy() { if (!sel.Empty()) { SelectionText st; CopySelectionRange(&st); CopyToClipboard(st); } } void ScintillaQt::CopyToClipboard(const SelectionText &selectedText) { CopyToModeClipboard(selectedText, QClipboard::Clipboard); } void ScintillaQt::PasteFromMode(QClipboard::Mode clipboardMode_) { QClipboard *clipboard = QApplication::clipboard(); const QMimeData *mimeData = clipboard->mimeData(clipboardMode_); bool isRectangular = IsRectangularInMime(mimeData); QString text = clipboard->text(clipboardMode_); QByteArray utext = BytesForDocument(text); std::string dest(utext.constData(), utext.length()); SelectionText selText; selText.Copy(dest, pdoc->dbcsCodePage, CharacterSetOfDocument(), isRectangular, false); UndoGroup ug(pdoc); ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH); InsertPasteShape(selText.Data(), static_cast(selText.Length()), selText.rectangular ? pasteRectangular : pasteStream); EnsureCaretVisible(); } void ScintillaQt::Paste() { PasteFromMode(QClipboard::Clipboard); } void ScintillaQt::ClaimSelection() { if (QApplication::clipboard()->supportsSelection()) { // X Windows has a 'primary selection' as well as the clipboard. // Whenever the user selects some text, we become the primary selection if (!sel.Empty()) { primarySelection = true; SelectionText st; CopySelectionRange(&st); CopyToModeClipboard(st, QClipboard::Selection); } else { primarySelection = false; } } } void ScintillaQt::NotifyChange() { emit notifyChange(); emit command( Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE), reinterpret_cast(wMain.GetID())); } void ScintillaQt::NotifyFocus(bool focus) { emit command( Platform::LongFromTwoShorts (GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS), reinterpret_cast