Initial commit: ReclassX structured binary editor

This commit is contained in:
sysadmin
2026-02-01 11:37:32 -07:00
commit 0be67c8396
786 changed files with 473499 additions and 0 deletions

View File

@@ -0,0 +1,867 @@
// This module implements the "official" low-level API.
//
// Copyright (c) 2023 Riverbank Computing Limited <info@riverbankcomputing.com>
//
// This file is part of QScintilla.
//
// This file may be used under the terms of the GNU General Public License
// version 3.0 as published by the Free Software Foundation and appearing in
// the file LICENSE included in the packaging of this file. Please review the
// following information to ensure the GNU General Public License version 3.0
// requirements will be met: http://www.gnu.org/copyleft/gpl.html.
//
// If you do not wish to use this file under the terms of the GPL version 3.0
// then you may purchase a commercial license. For more information contact
// info@riverbankcomputing.com.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#include "Qsci/qsciscintillabase.h"
#include <QApplication>
#include <QClipboard>
#include <QColor>
#include <QContextMenuEvent>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QDragLeaveEvent>
#include <QFocusEvent>
#include <QKeyEvent>
#include <QList>
#include <QMimeData>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QScrollBar>
#include <QStyle>
#include "SciAccessibility.h"
#include "ScintillaQt.h"
// The #defines in Scintilla.h and the enums in qsciscintillabase.h conflict
// (because we want to use the same names) so we have to undefine those we use
// in this file.
#undef SCI_SETCARETPERIOD
#undef SCK_DOWN
#undef SCK_UP
#undef SCK_LEFT
#undef SCK_RIGHT
#undef SCK_HOME
#undef SCK_END
#undef SCK_PRIOR
#undef SCK_NEXT
#undef SCK_DELETE
#undef SCK_INSERT
#undef SCK_ESCAPE
#undef SCK_BACK
#undef SCK_TAB
#undef SCK_RETURN
#undef SCK_ADD
#undef SCK_SUBTRACT
#undef SCK_DIVIDE
#undef SCK_WIN
#undef SCK_RWIN
#undef SCK_MENU
#undef SCN_URIDROPPED
// Remember if we have linked the lexers.
static bool lexersLinked = false;
// The list of instances.
static QList<QsciScintillaBase *> poolList;
// Mime support.
static const QLatin1String mimeTextPlain("text/plain");
static const QLatin1String mimeRectangularWin("MSDEVColumnSelect");
static const QLatin1String mimeRectangular("text/x-qscintilla-rectangular");
#if QT_VERSION < 0x060000 && defined(Q_OS_OSX)
extern void initialiseRectangularPasteboardMime();
#endif
// The ctor.
QsciScintillaBase::QsciScintillaBase(QWidget *parent)
: QAbstractScrollArea(parent), preeditPos(-1), preeditNrBytes(0),
clickCausedFocus(false)
{
#if !defined(QT_NO_ACCESSIBILITY)
QsciAccessibleScintillaBase::initialise();
#endif
connectVerticalScrollBar();
connectHorizontalScrollBar();
setAcceptDrops(true);
setFocusPolicy(Qt::WheelFocus);
setAttribute(Qt::WA_KeyCompression);
setAttribute(Qt::WA_InputMethodEnabled);
setInputMethodHints(
Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhMultiLine);
viewport()->setBackgroundRole(QPalette::Base);
viewport()->setMouseTracking(true);
viewport()->setAttribute(Qt::WA_NoSystemBackground);
triple_click.setSingleShot(true);
#if QT_VERSION < 0x060000 && defined(Q_OS_OSX)
initialiseRectangularPasteboardMime();
#endif
sci = new QsciScintillaQt(this);
SendScintilla(SCI_SETCARETPERIOD, QApplication::cursorFlashTime() / 2);
// Make sure the lexers are linked in.
if (!lexersLinked)
{
Scintilla_LinkLexers();
lexersLinked = true;
}
// Add it to the pool.
poolList.append(this);
}
// The dtor.
QsciScintillaBase::~QsciScintillaBase()
{
// The QsciScintillaQt object isn't a child so delete it explicitly.
delete sci;
// Remove it from the pool.
poolList.removeAt(poolList.indexOf(this));
}
// Return an instance from the pool.
QsciScintillaBase *QsciScintillaBase::pool()
{
return poolList.first();
}
// Tell Scintilla to update the scroll bars. Scintilla should be doing this
// itself.
void QsciScintillaBase::setScrollBars()
{
sci->SetScrollBars();
}
// Send a message to the real Scintilla widget using the low level Scintilla
// API.
long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
long lParam) const
{
return sci->WndProc(msg, wParam, lParam);
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
void *lParam) const
{
return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(lParam));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, uintptr_t wParam,
const char *lParam) const
{
return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(lParam));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg,
const char *lParam) const
{
return sci->WndProc(msg, static_cast<uptr_t>(0),
reinterpret_cast<sptr_t>(lParam));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, const char *wParam,
const char *lParam) const
{
return sci->WndProc(msg, reinterpret_cast<uptr_t>(wParam),
reinterpret_cast<sptr_t>(lParam));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, long wParam) const
{
return sci->WndProc(msg, static_cast<uptr_t>(wParam),
static_cast<sptr_t>(0));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, int wParam) const
{
return sci->WndProc(msg, static_cast<uptr_t>(wParam),
static_cast<sptr_t>(0));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, long cpMin, long cpMax,
char *lpstrText) const
{
Sci_TextRange tr;
tr.chrg.cpMin = cpMin;
tr.chrg.cpMax = cpMax;
tr.lpstrText = lpstrText;
return sci->WndProc(msg, static_cast<uptr_t>(0),
reinterpret_cast<sptr_t>(&tr));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
const QColor &col) const
{
sptr_t lParam = (col.blue() << 16) | (col.green() << 8) | col.red();
return sci->WndProc(msg, wParam, lParam);
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, const QColor &col) const
{
uptr_t wParam = (col.blue() << 16) | (col.green() << 8) | col.red();
return sci->WndProc(msg, wParam, static_cast<sptr_t>(0));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
QPainter *hdc, const QRect &rc, long cpMin, long cpMax) const
{
Sci_RangeToFormat rf;
rf.hdc = rf.hdcTarget = reinterpret_cast<Scintilla::SurfaceID>(hdc);
rf.rc.left = rc.left();
rf.rc.top = rc.top();
rf.rc.right = rc.right() + 1;
rf.rc.bottom = rc.bottom() + 1;
rf.chrg.cpMin = cpMin;
rf.chrg.cpMax = cpMax;
return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(&rf));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
const QPixmap &lParam) const
{
return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(&lParam));
}
// Overloaded message send.
long QsciScintillaBase::SendScintilla(unsigned int msg, unsigned long wParam,
const QImage &lParam) const
{
return sci->WndProc(msg, wParam, reinterpret_cast<sptr_t>(&lParam));
}
// Send a message to the real Scintilla widget using the low level Scintilla
// API that returns a pointer result.
void *QsciScintillaBase::SendScintillaPtrResult(unsigned int msg) const
{
return reinterpret_cast<void *>(sci->WndProc(msg, static_cast<uptr_t>(0),
static_cast<sptr_t>(0)));
}
// Re-implemented to handle font changes
void QsciScintillaBase::changeEvent(QEvent *e)
{
if (e->type() == QEvent::FontChange || e->type() == QEvent::ApplicationFontChange)
sci->InvalidateStyleRedraw();
QAbstractScrollArea::changeEvent(e);
}
// Re-implemented to handle the context menu.
void QsciScintillaBase::contextMenuEvent(QContextMenuEvent *e)
{
sci->ContextMenu(Scintilla::Point(e->globalX(), e->globalY()));
}
// Re-implemented to tell the widget it has the focus.
void QsciScintillaBase::focusInEvent(QFocusEvent *e)
{
sci->SetFocusState(true);
clickCausedFocus = (e->reason() == Qt::MouseFocusReason);
QAbstractScrollArea::focusInEvent(e);
}
// Re-implemented to tell the widget it has lost the focus.
void QsciScintillaBase::focusOutEvent(QFocusEvent *e)
{
if (e->reason() == Qt::ActiveWindowFocusReason)
{
// Only tell Scintilla we have lost focus if the new active window
// isn't our auto-completion list. This is probably only an issue on
// Linux and there are still problems because subsequent focus out
// events don't always seem to get generated (at least with Qt5).
QWidget *aw = QApplication::activeWindow();
if (!aw || aw->parent() != this || !aw->inherits("QsciSciListBox"))
sci->SetFocusState(false);
}
else
{
sci->SetFocusState(false);
}
QAbstractScrollArea::focusOutEvent(e);
}
// Re-implemented to make sure tabs are passed to the editor.
bool QsciScintillaBase::focusNextPrevChild(bool next)
{
if (!sci->pdoc->IsReadOnly())
return false;
return QAbstractScrollArea::focusNextPrevChild(next);
}
// Handle key presses.
void QsciScintillaBase::keyPressEvent(QKeyEvent *e)
{
int modifiers = 0;
if (e->modifiers() & Qt::ShiftModifier)
modifiers |= SCMOD_SHIFT;
if (e->modifiers() & Qt::ControlModifier)
modifiers |= SCMOD_CTRL;
if (e->modifiers() & Qt::AltModifier)
modifiers |= SCMOD_ALT;
if (e->modifiers() & Qt::MetaModifier)
modifiers |= SCMOD_META;
int key = commandKey(e->key(), modifiers);
if (key)
{
bool consumed = false;
sci->KeyDownWithModifiers(key, modifiers, &consumed);
if (consumed)
{
e->accept();
return;
}
}
QString text = e->text();
if (!text.isEmpty() && text[0].isPrint())
{
QByteArray bytes = textAsBytes(text);
sci->AddCharUTF(bytes.data(), bytes.length());
e->accept();
}
else
{
QAbstractScrollArea::keyPressEvent(e);
}
}
// Map a Qt key to a valid Scintilla command key, or 0 if none.
int QsciScintillaBase::commandKey(int qt_key, int &modifiers)
{
int key;
switch (qt_key)
{
case Qt::Key_Down:
key = SCK_DOWN;
break;
case Qt::Key_Up:
key = SCK_UP;
break;
case Qt::Key_Left:
key = SCK_LEFT;
break;
case Qt::Key_Right:
key = SCK_RIGHT;
break;
case Qt::Key_Home:
key = SCK_HOME;
break;
case Qt::Key_End:
key = SCK_END;
break;
case Qt::Key_PageUp:
key = SCK_PRIOR;
break;
case Qt::Key_PageDown:
key = SCK_NEXT;
break;
case Qt::Key_Delete:
key = SCK_DELETE;
break;
case Qt::Key_Insert:
key = SCK_INSERT;
break;
case Qt::Key_Escape:
key = SCK_ESCAPE;
break;
case Qt::Key_Backspace:
key = SCK_BACK;
break;
case Qt::Key_Tab:
key = SCK_TAB;
break;
case Qt::Key_Backtab:
// Scintilla assumes a backtab is shift-tab.
key = SCK_TAB;
modifiers |= SCMOD_SHIFT;
break;
case Qt::Key_Return:
case Qt::Key_Enter:
key = SCK_RETURN;
break;
case Qt::Key_Super_L:
key = SCK_WIN;
break;
case Qt::Key_Super_R:
key = SCK_RWIN;
break;
case Qt::Key_Menu:
key = SCK_MENU;
break;
default:
if ((key = qt_key) > 0x7f)
key = 0;
}
return key;
}
// Encode a QString as bytes.
QByteArray QsciScintillaBase::textAsBytes(const QString &text) const
{
if (sci->IsUnicodeMode())
return text.toUtf8();
return text.toLatin1();
}
// Decode bytes as a QString.
QString QsciScintillaBase::bytesAsText(const char *bytes, int size) const
{
if (sci->IsUnicodeMode())
return QString::fromUtf8(bytes, size);
return QString::fromLatin1(bytes, size);
}
// Handle a mouse button double click.
void QsciScintillaBase::mouseDoubleClickEvent(QMouseEvent *e)
{
if (e->button() != Qt::LeftButton)
{
e->ignore();
return;
}
setFocus();
// Make sure Scintilla will interpret this as a double-click.
unsigned clickTime = sci->lastClickTime + Scintilla::Platform::DoubleClickTime() - 1;
sci->ButtonDownWithModifiers(Scintilla::Point(e->x(), e->y()), clickTime,
eventModifiers(e));
// Remember the current position and time in case it turns into a triple
// click.
triple_click_at = e->globalPos();
triple_click.start(QApplication::doubleClickInterval());
}
// Handle a mouse move.
void QsciScintillaBase::mouseMoveEvent(QMouseEvent *e)
{
sci->ButtonMoveWithModifiers(Scintilla::Point(e->x(), e->y()), 0,
eventModifiers(e));
}
// Handle a mouse button press.
void QsciScintillaBase::mousePressEvent(QMouseEvent *e)
{
setFocus();
Scintilla::Point pt(e->x(), e->y());
if (e->button() == Qt::LeftButton || e->button() == Qt::RightButton)
{
unsigned clickTime;
// It is a triple click if the timer is running and the mouse hasn't
// moved too much.
if (triple_click.isActive() && (e->globalPos() - triple_click_at).manhattanLength() < QApplication::startDragDistance())
clickTime = sci->lastClickTime + Scintilla::Platform::DoubleClickTime() - 1;
else
clickTime = sci->lastClickTime + Scintilla::Platform::DoubleClickTime() + 1;
triple_click.stop();
// Scintilla uses the Alt modifier to initiate rectangular selection.
// However the GTK port (under X11, not Windows) uses the Control
// modifier (by default, although it is configurable). It does this
// because most X11 window managers hijack Alt-drag to move the window.
// We do the same, except that (for the moment at least) we don't allow
// the modifier to be configured.
bool shift = e->modifiers() & Qt::ShiftModifier;
bool ctrl = e->modifiers() & Qt::ControlModifier;
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
bool alt = e->modifiers() & Qt::AltModifier;
#else
bool alt = ctrl;
#endif
if (e->button() == Qt::LeftButton)
sci->ButtonDownWithModifiers(pt, clickTime,
QsciScintillaQt::ModifierFlags(shift, ctrl, alt));
else
sci->RightButtonDownWithModifiers(pt, clickTime,
QsciScintillaQt::ModifierFlags(shift, ctrl, alt));
}
else if (e->button() == Qt::MiddleButton)
{
QClipboard *cb = QApplication::clipboard();
if (cb->supportsSelection())
{
int pos = sci->PositionFromLocation(pt);
sci->sel.Clear();
sci->SetSelection(pos, pos);
sci->pasteFromClipboard(QClipboard::Selection);
}
}
}
// Handle a mouse button releases.
void QsciScintillaBase::mouseReleaseEvent(QMouseEvent *e)
{
if (e->button() != Qt::LeftButton)
return;
Scintilla::Point pt(e->x(), e->y());
if (sci->HaveMouseCapture())
{
bool ctrl = e->modifiers() & Qt::ControlModifier;
sci->ButtonUpWithModifiers(pt, 0,
QsciScintillaQt::ModifierFlags(false, ctrl, false));
}
if (!sci->pdoc->IsReadOnly() && !sci->PointInSelMargin(pt) && qApp->autoSipEnabled())
{
QStyle::RequestSoftwareInputPanel rsip = QStyle::RequestSoftwareInputPanel(style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
if (!clickCausedFocus || rsip == QStyle::RSIP_OnMouseClick)
qApp->inputMethod()->show();
}
clickCausedFocus = false;
}
// Handle paint events.
void QsciScintillaBase::paintEvent(QPaintEvent *e)
{
sci->paintEvent(e);
}
// Handle resize events.
void QsciScintillaBase::resizeEvent(QResizeEvent *)
{
sci->ChangeSize();
}
// Re-implemented to suppress the default behaviour as Scintilla works at a
// more fundamental level. Note that this means that replacing the scrollbars
// with custom versions does not work.
void QsciScintillaBase::scrollContentsBy(int, int)
{
}
// Handle the vertical scrollbar.
void QsciScintillaBase::handleVSb(int value)
{
sci->ScrollTo(value);
}
// Handle the horizontal scrollbar.
void QsciScintillaBase::handleHSb(int value)
{
sci->HorizontalScrollTo(value);
}
// Handle drag enters.
void QsciScintillaBase::dragEnterEvent(QDragEnterEvent *e)
{
QsciScintillaBase::dragMoveEvent(e);
}
// Handle drag leaves.
void QsciScintillaBase::dragLeaveEvent(QDragLeaveEvent *)
{
sci->SetDragPosition(Scintilla::SelectionPosition());
}
// Handle drag moves.
void QsciScintillaBase::dragMoveEvent(QDragMoveEvent *e)
{
if (e->mimeData()->hasUrls())
{
e->acceptProposedAction();
}
else
{
sci->SetDragPosition(
sci->SPositionFromLocation(
Scintilla::Point(e->pos().x(), e->pos().y()), false,
false, sci->UserVirtualSpace()));
acceptAction(e);
}
}
// Handle drops.
void QsciScintillaBase::dropEvent(QDropEvent *e)
{
if (e->mimeData()->hasUrls())
{
e->acceptProposedAction();
foreach (const QUrl &url, e->mimeData()->urls())
emit SCN_URIDROPPED(url);
return;
}
acceptAction(e);
if (!e->isAccepted())
return;
bool moving;
int len;
const char *s;
bool rectangular;
moving = (e->dropAction() == Qt::MoveAction);
QByteArray text = fromMimeData(e->mimeData(), rectangular);
len = text.length();
s = text.data();
std::string dest = Scintilla::Document::TransformLineEnds(s, len,
sci->pdoc->eolMode);
sci->DropAt(sci->posDrop, dest.c_str(), dest.length(), moving,
rectangular);
sci->Redraw();
}
void QsciScintillaBase::acceptAction(QDropEvent *e)
{
if (sci->pdoc->IsReadOnly() || !canInsertFromMimeData(e->mimeData()))
e->ignore();
else
e->acceptProposedAction();
}
// See if a MIME data object can be decoded.
bool QsciScintillaBase::canInsertFromMimeData(const QMimeData *source) const
{
return source->hasFormat(mimeTextPlain);
}
// Create text from a MIME data object.
QByteArray QsciScintillaBase::fromMimeData(const QMimeData *source, bool &rectangular) const
{
// See if it is rectangular. We try all of the different formats that
// Scintilla supports in case we are working across different platforms.
if (source->hasFormat(mimeRectangularWin))
rectangular = true;
else if (source->hasFormat(mimeRectangular))
rectangular = true;
else
rectangular = false;
// Note that we don't support Scintilla's hack of adding a '\0' as Qt
// strips it off under the covers when pasting from another process.
QString utf8 = source->text();
QByteArray text;
if (sci->IsUnicodeMode())
text = utf8.toUtf8();
else
text = utf8.toLatin1();
return text;
}
// Create a MIME data object for some text.
QMimeData *QsciScintillaBase::toMimeData(const QByteArray &text, bool rectangular) const
{
QMimeData *mime = new QMimeData;
QString utf8;
if (sci->IsUnicodeMode())
utf8 = QString::fromUtf8(text.constData(), text.size());
else
utf8 = QString::fromLatin1(text.constData(), text.size());
mime->setText(utf8);
if (rectangular)
{
// Use the platform specific "standard" for specifying a rectangular
// selection.
#if defined(Q_OS_WIN)
mime->setData(mimeRectangularWin, QByteArray());
#else
mime->setData(mimeRectangular, QByteArray());
#endif
}
return mime;
}
// Connect up the vertical scroll bar.
void QsciScintillaBase::connectVerticalScrollBar()
{
connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
SLOT(handleVSb(int)));
}
// Connect up the horizontal scroll bar.
void QsciScintillaBase::connectHorizontalScrollBar()
{
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
SLOT(handleHSb(int)));
}
//! Replace the vertical scroll bar.
void QsciScintillaBase::replaceVerticalScrollBar(QScrollBar *scrollBar)
{
setVerticalScrollBar(scrollBar);
connectVerticalScrollBar();
}
// Replace the horizontal scroll bar.
void QsciScintillaBase::replaceHorizontalScrollBar(QScrollBar *scrollBar)
{
setHorizontalScrollBar(scrollBar);
connectHorizontalScrollBar();
}
// Return true if a context menu should be displayed. This is provided as a
// helper to QsciScintilla::contextMenuEvent(). A proper design would break
// backwards compatibility.
bool QsciScintillaBase::contextMenuNeeded(int x, int y) const
{
Scintilla::Point pt(x, y);
// Clear any selection if the mouse is outside.
if (!sci->PointInSelection(pt))
sci->SetEmptySelection(sci->PositionFromLocation(pt));
// Respect SC_POPUP_*.
return sci->ShouldDisplayPopup(pt);
}
// Return the Scintilla keyboard modifiers set for a mouse event.
int QsciScintillaBase::eventModifiers(QMouseEvent *e)
{
bool shift = e->modifiers() & Qt::ShiftModifier;
bool ctrl = e->modifiers() & Qt::ControlModifier;
bool alt = e->modifiers() & Qt::AltModifier;
return QsciScintillaQt::ModifierFlags(shift, ctrl, alt);
}