Compare commits
30 Commits
5a5e727b99
...
c0be5c4b23
Author | SHA1 | Date |
---|---|---|
|
c0be5c4b23 | |
|
f0a3ede44f | |
|
735298d850 | |
|
e47b96b563 | |
|
893f3112c7 | |
|
1452874ac5 | |
|
862a6717ec | |
|
8e76d29707 | |
|
7e33b3b87a | |
|
2a897e0099 | |
|
85cad2aa0e | |
|
3e3ca37642 | |
|
11e207f686 | |
|
db90ece792 | |
|
d5a4095779 | |
|
48cf02cfa9 | |
|
912dfaf227 | |
|
c08c2a859e | |
|
96fa55d930 | |
|
5686edde5b | |
|
4f4db8b5f8 | |
|
63b4583ccd | |
|
a16a8033b5 | |
|
b4881d7e3c | |
|
84d9596ada | |
|
ed118376d5 | |
|
31c896c9dc | |
|
8c866d090f | |
|
b2d03972b8 | |
|
204b34447b |
|
@ -7,7 +7,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run clang-format style check for C/C++/Protobuf programs.
|
||||
uses: jidicula/clang-format-action@v4.13.0
|
||||
uses: jidicula/clang-format-action@v4.14.0
|
||||
with:
|
||||
clang-format-version: '13'
|
||||
clang-format-version: '12'
|
||||
fallback-style: 'LLVM' # optional
|
||||
|
|
|
@ -9,6 +9,6 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: cmake-format lint
|
||||
uses: neg-c/cmake-format-action@v0.1.1
|
||||
uses: neg-c/cmake-format-action@v0.1.3
|
||||
with:
|
||||
inplace: true
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
name: CMake build release for linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- release
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
token: ${{ secrets.CONTRIBUTORS_TOKEN }}
|
||||
- name: Install Qt
|
||||
# Installs the Qt SDK
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: 6.8.1
|
||||
host: 'linux'
|
||||
target: 'desktop'
|
||||
arch: 'linux_gcc_64'
|
||||
cache: true
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/build/package
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target install
|
||||
|
||||
- name: Install Packing Tools
|
||||
run: sudo apt install fakeroot patchelf -y
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Python packages
|
||||
run: pip install -r ${{github.workspace}}/mkinstaller/linuxdeploy/requirements.txt
|
||||
|
||||
- name: Deploy WingHexExplorer2
|
||||
run: python ${{github.workspace}}/mkinstaller/linuxdeploy/deploy.py ${{github.workspace}}/build
|
||||
|
||||
- name: +x for ld-linux if it has
|
||||
run: sudo ${{github.workspace}}/mkinstaller/linuxdeploy/add-ld-x.sh ${{github.workspace}}/build/package
|
||||
|
||||
- name: Create installer
|
||||
run: bash ${{github.workspace}}/mkinstaller/linuxdeploy/build.sh ${{github.workspace}}/build/package
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: WingHexExplorer2-linux-release-build-cache
|
||||
path: ${{github.workspace}}/mkinstaller/linuxdeploy/build
|
|
@ -0,0 +1,66 @@
|
|||
name: CMake build release for win
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- release
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build-release:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
token: ${{ secrets.CONTRIBUTORS_TOKEN }}
|
||||
- name: Install Qt
|
||||
# Installs the Qt SDK
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: 6.6.2
|
||||
host: 'windows'
|
||||
target: 'desktop'
|
||||
arch: 'win64_msvc2019_64'
|
||||
cache: true
|
||||
- name: Add Chinese support file for InnoSetup
|
||||
run: |
|
||||
$sourcePath = "${{github.workspace}}/mkinstaller/innoSetup/ChineseSimplified.isl"
|
||||
$destinationPath = "C:\Program Files (x86)\Inno Setup 6\Languages\ChineseSimplified.isl"
|
||||
Start-Process powershell -ArgumentList "Copy-Item -Path '$sourcePath' -Destination '$destinationPath' -Force" -Verb runAs
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Python packages
|
||||
run: pip install -r ${{github.workspace}}/mkinstaller/innoSetup/requirements.txt
|
||||
|
||||
- name: Deploy WingHexExplorer2
|
||||
run: python ${{github.workspace}}/mkinstaller/innoSetup/mkinnopak.py --no-build "${{github.workspace}}/build"
|
||||
|
||||
- name: Compile .ISS to .EXE Installer
|
||||
uses: Minionguyjpro/Inno-Setup-Action@v1.2.2
|
||||
with:
|
||||
path: build/package/WingHexExplorer2/mkiss.iss
|
||||
options: /O${{github.workspace}}/mkinstaller/innoSetup
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: WingHexExplorer2-win-release-build-cache
|
||||
path: ${{github.workspace}}/mkinstaller/innoSetup/*.exe
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
name: CMake build check
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
|
|
@ -10,3 +10,6 @@
|
|||
[submodule "3rdparty/SingleApplication"]
|
||||
path = 3rdparty/SingleApplication
|
||||
url = git@github.com:itay-grudev/SingleApplication.git
|
||||
[submodule "3rdparty/json"]
|
||||
path = 3rdparty/json
|
||||
url = git@github.com:nlohmann/json.git
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f2034769ce887367e97a5fbaced5b14aa8039fd3
|
||||
Subproject commit 57ef77e4793ca14f448f485f52392ab0eefe968b
|
|
@ -1,3 +0,0 @@
|
|||
*.pro.user*
|
||||
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(
|
||||
QConsoleWidget
|
||||
VERSION 2.14.1
|
||||
LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
|
||||
|
||||
if(MSVC)
|
||||
string(APPEND CMAKE_CXX_FLAGS " /utf-8")
|
||||
string(APPEND CMAKE_C_FLAGS " /utf-8")
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
add_library(
|
||||
QConsoleWidget STATIC src/QConsoleIODevice.cpp src/QConsoleIODevice.h
|
||||
src/QConsoleWidget.cpp src/QConsoleWidget.h)
|
||||
|
||||
target_link_libraries(QConsoleWidget PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
|
|
@ -9,7 +9,7 @@
|
|||
QConsoleIODevice::QConsoleIODevice(QConsoleWidget *w, QObject *parent)
|
||||
: QIODevice(parent), widget_(w), readpos_(0), writtenSinceLastEmit_(0),
|
||||
readSinceLastEmit_(0) {
|
||||
setCurrentWriteChannel(QConsoleWidget::StandardOutput);
|
||||
setCurrentWriteChannel(STDOUT_FILENO);
|
||||
|
||||
open(QIODevice::ReadWrite | QIODevice::Unbuffered);
|
||||
}
|
||||
|
@ -53,10 +53,10 @@ qint64 QConsoleIODevice::readData(char *data, qint64 len) {
|
|||
qint64 QConsoleIODevice::writeData(const char *data, qint64 len) {
|
||||
QByteArray ba(data, (int)len);
|
||||
int ch = currentWriteChannel();
|
||||
if (ch == QConsoleWidget::StandardError)
|
||||
widget_->writeStdErr(ba);
|
||||
else
|
||||
if (ch == STDOUT_FILENO)
|
||||
widget_->writeStdOut(ba);
|
||||
else
|
||||
widget_->writeStdErr(ba);
|
||||
|
||||
writtenSinceLastEmit_ += len;
|
||||
if (!signalsBlocked()) {
|
|
@ -6,8 +6,15 @@
|
|||
|
||||
class QConsoleWidget;
|
||||
|
||||
class QConsoleIODevice : public QIODevice {
|
||||
#ifndef STDOUT_FILENO
|
||||
#define STDOUT_FILENO 1
|
||||
#endif
|
||||
|
||||
#ifndef STDERR_FILENO
|
||||
#define STDERR_FILENO 2
|
||||
#endif
|
||||
|
||||
class QConsoleIODevice : public QIODevice {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
|
@ -0,0 +1,451 @@
|
|||
#include "QConsoleWidget.h"
|
||||
|
||||
#include "class/ascompletion.h"
|
||||
#include "control/qcodecompletionwidget.h"
|
||||
#include "qdocumentline.h"
|
||||
#include "qformatscheme.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include "QConsoleIODevice.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
|
||||
QConsoleWidget::QConsoleWidget(QWidget *parent)
|
||||
: QEditor(false, parent), mode_(Output) {
|
||||
iodevice_ = new QConsoleIODevice(this, this);
|
||||
m_doc->setProperty("console", QVariant::fromValue(inpos_));
|
||||
setFlag(QEditor::AutoCloseChars, true);
|
||||
setAcceptDrops(false);
|
||||
setUndoRedoEnabled(false);
|
||||
setCursorMirrorEnabled(false);
|
||||
createSimpleBasicContextMenu(false, false);
|
||||
}
|
||||
|
||||
QConsoleWidget::~QConsoleWidget() {}
|
||||
|
||||
void QConsoleWidget::setMode(ConsoleMode m) {
|
||||
if (m == mode_)
|
||||
return;
|
||||
|
||||
if (m == Input) {
|
||||
auto cursor = this->cursor();
|
||||
cursor.movePosition(QDocumentCursor::End);
|
||||
setCursor(cursor);
|
||||
inpos_ = cursor;
|
||||
m_doc->setProperty("console", QVariant::fromValue(inpos_));
|
||||
mode_ = Input;
|
||||
}
|
||||
|
||||
if (m == Output) {
|
||||
mode_ = Output;
|
||||
}
|
||||
}
|
||||
|
||||
QString QConsoleWidget::getCommandLine() {
|
||||
if (mode_ == Output)
|
||||
return QString();
|
||||
auto textCursor = this->cursor();
|
||||
auto ltxt = textCursor.line().text();
|
||||
QString code = ltxt.mid(inpos_.columnNumber());
|
||||
return code.replace(QChar::ParagraphSeparator, QChar::LineFeed);
|
||||
}
|
||||
|
||||
void QConsoleWidget::paste() {
|
||||
auto text = qApp->clipboard()->text();
|
||||
if (canPaste()) {
|
||||
m_cursor.insertText(text.replace('\n', ' '));
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::handleReturnKey() {
|
||||
QString code = getCommandLine();
|
||||
|
||||
// start new block
|
||||
auto textCursor = this->cursor();
|
||||
auto line = textCursor.line();
|
||||
textCursor.moveTo(line.lineNumber(), line.length());
|
||||
textCursor.insertLine();
|
||||
|
||||
setMode(Output);
|
||||
setCursor(textCursor);
|
||||
|
||||
// Update the history
|
||||
if (!code.isEmpty())
|
||||
history_.add(code);
|
||||
|
||||
// send signal / update iodevice
|
||||
if (iodevice_->isOpen())
|
||||
iodevice_->consoleWidgetInput(code);
|
||||
|
||||
emit consoleCommand(code);
|
||||
}
|
||||
|
||||
void QConsoleWidget::keyPressEvent(QKeyEvent *e) {
|
||||
if (mode() == Input) {
|
||||
auto ascom = dynamic_cast<AsCompletion *>(completionEngine());
|
||||
if (ascom) {
|
||||
auto cw = ascom->codeCompletionWidget();
|
||||
if (cw && cw->isVisible()) {
|
||||
// The following keys are forwarded by the completer to the
|
||||
// widget
|
||||
switch (e->key()) {
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Escape:
|
||||
case Qt::Key_Backtab:
|
||||
e->ignore();
|
||||
return; // let the completer do default behavior
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto textCursor = this->cursor();
|
||||
bool selectionInEditZone = isSelectionInEditZone();
|
||||
|
||||
// check for user abort request
|
||||
if (e->modifiers() & Qt::ControlModifier) {
|
||||
if (e->key() == Qt::Key_Q) // Ctrl-Q aborts
|
||||
{
|
||||
emit abortEvaluation();
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow copying anywhere in the console ...
|
||||
if (e->key() == Qt::Key_C && e->modifiers() == Qt::ControlModifier) {
|
||||
if (textCursor.hasSelection())
|
||||
copy();
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// the rest of key events are ignored during output mode
|
||||
if (mode() != Input) {
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow cut only if the selection is limited to the interactive area ...
|
||||
if (e->key() == Qt::Key_X && e->modifiers() == Qt::ControlModifier) {
|
||||
cut();
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow paste only if the selection is in the interactive area ...
|
||||
if (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier) {
|
||||
if (selectionInEditZone || isCursorInEditZone()) {
|
||||
const QMimeData *const clipboard =
|
||||
QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isNull()) {
|
||||
textCursor.insertText(text);
|
||||
}
|
||||
}
|
||||
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
int key = e->key();
|
||||
// int shiftMod = e->modifiers() == Qt::ShiftModifier;
|
||||
|
||||
if (history_.isActive() && key != Qt::Key_Up && key != Qt::Key_Down)
|
||||
history_.deactivate();
|
||||
|
||||
// Force the cursor back to the interactive area
|
||||
// for all keys except modifiers
|
||||
if (!isCursorInEditZone() && key != Qt::Key_Control &&
|
||||
key != Qt::Key_Shift && key != Qt::Key_Alt) {
|
||||
auto line = textCursor.line();
|
||||
textCursor.moveTo(line.lineNumber(), line.length());
|
||||
setCursor(textCursor);
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Qt::Key_Up:
|
||||
// Activate the history and move to the 1st matching history item
|
||||
if (!history_.isActive())
|
||||
history_.activate(getCommandLine());
|
||||
if (history_.move(true))
|
||||
replaceCommandLine(history_.currentValue());
|
||||
else
|
||||
QApplication::beep();
|
||||
e->accept();
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
if (history_.move(false))
|
||||
replaceCommandLine(history_.currentValue());
|
||||
else
|
||||
QApplication::beep();
|
||||
e->accept();
|
||||
|
||||
case Qt::Key_Left:
|
||||
if (textCursor > inpos_)
|
||||
QEditor::keyPressEvent(e);
|
||||
else {
|
||||
QApplication::beep();
|
||||
e->accept();
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Delete:
|
||||
e->accept();
|
||||
if (selectionInEditZone)
|
||||
cut();
|
||||
else {
|
||||
// cursor must be in edit zone
|
||||
if (textCursor < inpos_)
|
||||
QApplication::beep();
|
||||
else
|
||||
QEditor::keyPressEvent(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Backspace:
|
||||
e->accept();
|
||||
if (selectionInEditZone)
|
||||
cut();
|
||||
else {
|
||||
// cursor must be in edit zone
|
||||
if (textCursor <= inpos_)
|
||||
QApplication::beep();
|
||||
else
|
||||
QEditor::keyPressEvent(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Home:
|
||||
e->accept();
|
||||
setCursor(inpos_);
|
||||
break;
|
||||
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
e->accept();
|
||||
handleReturnKey();
|
||||
break;
|
||||
|
||||
case Qt::Key_Escape:
|
||||
e->accept();
|
||||
replaceCommandLine(QString());
|
||||
break;
|
||||
|
||||
default:
|
||||
// setCurrentCharFormat(chanFormat_[StandardInput]);
|
||||
if (isCursorInEditZone()) {
|
||||
QEditor::keyPressEvent(e);
|
||||
} else {
|
||||
e->ignore();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool QConsoleWidget::isSelectionInEditZone() const {
|
||||
auto textCursor = this->cursor();
|
||||
if (!textCursor.hasSelection())
|
||||
return false;
|
||||
|
||||
auto selectionStart = textCursor.selectionStart();
|
||||
auto selectionEnd = textCursor.selectionEnd();
|
||||
|
||||
return selectionStart > inpos_ && selectionEnd >= inpos_;
|
||||
}
|
||||
|
||||
bool QConsoleWidget::isCursorInEditZone() const { return cursor() >= inpos_; }
|
||||
|
||||
bool QConsoleWidget::canPaste() const {
|
||||
auto textCursor = this->cursor();
|
||||
if (textCursor < inpos_)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void QConsoleWidget::replaceCommandLine(const QString &str) {
|
||||
// Select the text after the last command prompt ...
|
||||
auto textCursor = this->cursor();
|
||||
auto line = textCursor.line();
|
||||
textCursor.moveTo(line.lineNumber(), line.length());
|
||||
|
||||
auto bcursor = inpos_;
|
||||
bcursor.setSelectionBoundary(textCursor);
|
||||
bcursor.replaceSelectedText(str);
|
||||
bcursor.movePosition(str.length());
|
||||
|
||||
setCursor(bcursor);
|
||||
}
|
||||
|
||||
QString QConsoleWidget::getHistoryPath() {
|
||||
QDir dir(Utilities::getAppDataPath());
|
||||
return dir.absoluteFilePath(QStringLiteral(".command_history.lst"));
|
||||
}
|
||||
|
||||
void QConsoleWidget::write(const QString &message, const QString &sfmtID) {
|
||||
auto tc = cursor();
|
||||
auto ascom = dynamic_cast<AsCompletion *>(completionEngine());
|
||||
|
||||
QCodeCompletionWidget *cw = nullptr;
|
||||
if (ascom) {
|
||||
cw = ascom->codeCompletionWidget();
|
||||
}
|
||||
|
||||
if (ascom == nullptr || mode() == Output || (cw && cw->isCompleting())) {
|
||||
// in output mode or completion messages are appended
|
||||
auto tc1 = tc;
|
||||
auto line = tc1.line();
|
||||
tc1.moveTo(line, line.length());
|
||||
|
||||
// check is cursor was not at the end
|
||||
// (e.g. had been moved per mouse action)
|
||||
bool needsRestore = tc1.position() != tc.position();
|
||||
|
||||
// insert text
|
||||
setCursor(tc1);
|
||||
tc.insertText(message, false, sfmtID);
|
||||
ensureCursorVisible();
|
||||
|
||||
// restore cursor if needed
|
||||
if (needsRestore)
|
||||
setCursor(tc);
|
||||
} else {
|
||||
// in Input mode output messages are inserted
|
||||
// before the edit block
|
||||
|
||||
// get offset of current pos from the end
|
||||
auto editpos = tc;
|
||||
auto line = editpos.line();
|
||||
tc.moveTo(line, 0);
|
||||
tc.insertLine();
|
||||
tc.insertText(message, false, sfmtID);
|
||||
setCursor(editpos);
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::writeStdOut(const QString &s) {
|
||||
write(s, QStringLiteral("stdout"));
|
||||
}
|
||||
|
||||
void QConsoleWidget::writeStdErr(const QString &s) {
|
||||
write(s, QStringLiteral("stderr"));
|
||||
}
|
||||
|
||||
/////////////////// QConsoleWidget::History /////////////////////
|
||||
|
||||
QConsoleWidget::History QConsoleWidget::history_;
|
||||
|
||||
QConsoleWidget::History::History(void)
|
||||
: pos_(0), active_(false), maxsize_(10000) {
|
||||
QFile f(QConsoleWidget::getHistoryPath());
|
||||
if (f.open(QFile::ReadOnly)) {
|
||||
QTextStream is(&f);
|
||||
while (!is.atEnd())
|
||||
add(is.readLine());
|
||||
}
|
||||
}
|
||||
QConsoleWidget::History::~History(void) {
|
||||
QFile f(QConsoleWidget::getHistoryPath());
|
||||
if (f.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
QTextStream os(&f);
|
||||
int n = strings_.size();
|
||||
while (n > 0)
|
||||
os << strings_.at(--n) << Qt::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::History::add(const QString &str) {
|
||||
active_ = false;
|
||||
// if (strings_.contains(str)) return;
|
||||
if (strings_.size() == maxsize_)
|
||||
strings_.pop_back();
|
||||
strings_.push_front(str);
|
||||
}
|
||||
|
||||
void QConsoleWidget::History::activate(const QString &tk) {
|
||||
active_ = true;
|
||||
token_ = tk;
|
||||
pos_ = -1;
|
||||
}
|
||||
|
||||
bool QConsoleWidget::History::move(bool dir) {
|
||||
if (active_) {
|
||||
int next = indexOf(dir, pos_);
|
||||
if (pos_ != next) {
|
||||
pos_ = next;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
int QConsoleWidget::History::indexOf(bool dir, int from) const {
|
||||
int i = from, to = from;
|
||||
if (dir) {
|
||||
while (i < strings_.size() - 1) {
|
||||
const QString &si = strings_.at(++i);
|
||||
if (si.startsWith(token_)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (i > 0) {
|
||||
const QString &si = strings_.at(--i);
|
||||
if (si.startsWith(token_)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
void QConsoleWidget::cut() {
|
||||
if (isSelectionInEditZone()) {
|
||||
QEditor::cut();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////// Stream manipulators /////////////////////
|
||||
|
||||
QTextStream &waitForInput(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d)
|
||||
d->waitForReadyRead(-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
QTextStream &inputMode(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d && d->widget())
|
||||
d->widget()->setMode(QConsoleWidget::Input);
|
||||
return s;
|
||||
}
|
||||
QTextStream &outChannel(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d)
|
||||
d->setCurrentWriteChannel(STDOUT_FILENO);
|
||||
return s;
|
||||
}
|
||||
|
||||
QTextStream &errChannel(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d)
|
||||
d->setCurrentWriteChannel(STDERR_FILENO);
|
||||
return s;
|
||||
}
|
|
@ -1,55 +1,40 @@
|
|||
#ifndef _QCONSOLEWIDGET_H_
|
||||
#define _QCONSOLEWIDGET_H_
|
||||
|
||||
#include <QCompleter>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QIODevice>
|
||||
#include <QTextCharFormat>
|
||||
#include <QTextStream>
|
||||
|
||||
class QConsoleIODevice;
|
||||
class QConsoleWidgetCompleter;
|
||||
#include "qeditor.h"
|
||||
|
||||
class QConsoleWidget : public QPlainTextEdit {
|
||||
class QConsoleIODevice;
|
||||
|
||||
class QConsoleWidget : public QEditor {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ConsoleMode { Input, Output };
|
||||
Q_ENUM(ConsoleMode)
|
||||
|
||||
enum ConsoleChannel {
|
||||
StandardInput,
|
||||
StandardOutput,
|
||||
StandardError,
|
||||
nConsoleChannels
|
||||
};
|
||||
Q_ENUM(ConsoleChannel)
|
||||
|
||||
QConsoleWidget(QWidget *parent = 0);
|
||||
~QConsoleWidget();
|
||||
explicit QConsoleWidget(QWidget *parent = nullptr);
|
||||
virtual ~QConsoleWidget();
|
||||
|
||||
ConsoleMode mode() const { return mode_; }
|
||||
void setMode(ConsoleMode m);
|
||||
QIODevice *device() const { return (QIODevice *)iodevice_; }
|
||||
QTextCharFormat channelCharFormat(ConsoleChannel ch) const {
|
||||
return chanFormat_[ch];
|
||||
}
|
||||
void setChannelCharFormat(ConsoleChannel ch, const QTextCharFormat &fmt) {
|
||||
chanFormat_[ch] = fmt;
|
||||
}
|
||||
const QStringList &completionTriggers() const {
|
||||
return completion_triggers_;
|
||||
}
|
||||
void setCompletionTriggers(const QStringList &l) {
|
||||
completion_triggers_ = l;
|
||||
}
|
||||
|
||||
virtual QSize sizeHint() const override { return QSize(600, 400); }
|
||||
|
||||
// write a formatted message to the console
|
||||
void write(const QString &message, const QTextCharFormat &fmt);
|
||||
void write(const QString &message, const QString &sfmtID = {}) override;
|
||||
|
||||
static const QStringList &history() { return history_.strings_; }
|
||||
void setCompleter(QConsoleWidgetCompleter *c);
|
||||
|
||||
// get the current command line
|
||||
QString getCommandLine();
|
||||
|
||||
public slots:
|
||||
virtual void paste() override;
|
||||
|
||||
// write to StandardOutput
|
||||
void writeStdOut(const QString &s);
|
||||
|
@ -67,12 +52,9 @@ protected:
|
|||
bool canPaste() const;
|
||||
bool canCut() const { return isSelectionInEditZone(); }
|
||||
void handleReturnKey();
|
||||
void handleTabKey();
|
||||
void updateCompleter();
|
||||
void checkCompletionTriggers(const QString &txt);
|
||||
// reimp QPlainTextEdit functions
|
||||
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
|
||||
// Returns true if there is a selection in edit zone
|
||||
bool isSelectionInEditZone() const;
|
||||
// Returns true if cursor is in edit zone
|
||||
|
@ -82,10 +64,9 @@ protected:
|
|||
|
||||
static QString getHistoryPath();
|
||||
|
||||
protected slots:
|
||||
|
||||
// insert the completion from completer
|
||||
void insertCompletion(const QString &completion);
|
||||
// QEditor interface
|
||||
public slots:
|
||||
virtual void cut() override;
|
||||
|
||||
private:
|
||||
struct History {
|
||||
|
@ -109,12 +90,9 @@ private:
|
|||
|
||||
static History history_;
|
||||
ConsoleMode mode_;
|
||||
int inpos_, completion_pos_;
|
||||
QStringList completion_triggers_;
|
||||
QDocumentCursor inpos_;
|
||||
QString currentMultiLineCode_;
|
||||
QConsoleIODevice *iodevice_;
|
||||
QTextCharFormat chanFormat_[nConsoleChannels];
|
||||
QConsoleWidgetCompleter *completer_;
|
||||
};
|
||||
|
||||
QTextStream &waitForInput(QTextStream &s);
|
||||
|
@ -122,22 +100,4 @@ QTextStream &inputMode(QTextStream &s);
|
|||
QTextStream &outChannel(QTextStream &s);
|
||||
QTextStream &errChannel(QTextStream &s);
|
||||
|
||||
class QConsoleWidgetCompleter : public QCompleter {
|
||||
public:
|
||||
/*
|
||||
* Update the completion model given a string. The given string
|
||||
* is the current console text between the cursor and the start of
|
||||
* the line.
|
||||
*
|
||||
* Return the completion count
|
||||
*/
|
||||
virtual int updateCompletionModel(const QString &str) = 0;
|
||||
|
||||
/*
|
||||
* Return the position in the command line where the completion
|
||||
* should be inserted
|
||||
*/
|
||||
virtual int insertPos() = 0;
|
||||
};
|
||||
|
||||
#endif
|
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
|
@ -1,577 +0,0 @@
|
|||
#include "QConsoleWidget.h"
|
||||
#include "QConsoleIODevice.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringListModel>
|
||||
#include <QTextBlock>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocumentFragment>
|
||||
|
||||
QConsoleWidget::QConsoleWidget(QWidget *parent)
|
||||
: QPlainTextEdit(parent), mode_(Output), completer_(0) {
|
||||
iodevice_ = new QConsoleIODevice(this, this);
|
||||
|
||||
QTextCharFormat fmt = currentCharFormat();
|
||||
for (int i = 0; i < nConsoleChannels; i++)
|
||||
chanFormat_[i] = fmt;
|
||||
|
||||
chanFormat_[StandardOutput].setForeground(Qt::darkBlue);
|
||||
chanFormat_[StandardError].setForeground(Qt::red);
|
||||
|
||||
setTextInteractionFlags(Qt::TextEditorInteraction);
|
||||
setUndoRedoEnabled(false);
|
||||
}
|
||||
|
||||
QConsoleWidget::~QConsoleWidget() {}
|
||||
|
||||
void QConsoleWidget::setMode(ConsoleMode m) {
|
||||
if (m == mode_)
|
||||
return;
|
||||
|
||||
if (m == Input) {
|
||||
QTextCursor cursor = textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
setTextCursor(cursor);
|
||||
setCurrentCharFormat(chanFormat_[StandardInput]);
|
||||
inpos_ = cursor.position();
|
||||
mode_ = Input;
|
||||
}
|
||||
|
||||
if (m == Output) {
|
||||
mode_ = Output;
|
||||
}
|
||||
}
|
||||
|
||||
QString QConsoleWidget::getCommandLine() {
|
||||
if (mode_ == Output)
|
||||
return QString();
|
||||
// select text in edit zone (from the input pos to the end)
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
textCursor.movePosition(QTextCursor::End);
|
||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||
QString code = textCursor.selectedText();
|
||||
code.replace(QChar::ParagraphSeparator, QChar::LineFeed);
|
||||
return code;
|
||||
}
|
||||
|
||||
void QConsoleWidget::handleReturnKey() {
|
||||
QString code = getCommandLine();
|
||||
|
||||
// start new block
|
||||
appendPlainText(QString());
|
||||
setMode(Output);
|
||||
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
textCursor.movePosition(QTextCursor::End);
|
||||
setTextCursor(textCursor);
|
||||
|
||||
// Update the history
|
||||
if (!code.isEmpty())
|
||||
history_.add(code);
|
||||
|
||||
// send signal / update iodevice
|
||||
if (iodevice_->isOpen())
|
||||
iodevice_->consoleWidgetInput(code);
|
||||
|
||||
emit consoleCommand(code);
|
||||
}
|
||||
|
||||
void QConsoleWidget::handleTabKey() {
|
||||
QTextCursor tc = this->textCursor();
|
||||
int anchor = tc.anchor();
|
||||
int position = tc.position();
|
||||
tc.setPosition(inpos_);
|
||||
tc.setPosition(position, QTextCursor::KeepAnchor);
|
||||
QString text = tc.selectedText().trimmed();
|
||||
tc.setPosition(anchor, QTextCursor::MoveAnchor);
|
||||
tc.setPosition(position, QTextCursor::KeepAnchor);
|
||||
if (text.isEmpty()) {
|
||||
tc.insertText(" ");
|
||||
} else {
|
||||
updateCompleter();
|
||||
if (completer_ && completer_->completionCount() == 1) {
|
||||
insertCompletion(completer_->currentCompletion());
|
||||
completer_->popup()->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::updateCompleter() {
|
||||
if (!completer_)
|
||||
return;
|
||||
|
||||
// if the completer is first shown, mark
|
||||
// the text position
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
if (!completer_->popup()->isVisible()) {
|
||||
completion_pos_ = textCursor.position();
|
||||
// qDebug() << "show completer, pos " << completion_pos_;
|
||||
}
|
||||
|
||||
// Get the text between the current cursor position
|
||||
// and the start of the input
|
||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||
QString commandText = textCursor.selectedText();
|
||||
// qDebug() << "code to complete: " << commandText;
|
||||
|
||||
// Call the completer to update the completion model
|
||||
// Place and show the completer if there are available completions
|
||||
int count;
|
||||
if ((count = completer_->updateCompletionModel(commandText))) {
|
||||
// qDebug() << "found " << count << " completions";
|
||||
|
||||
// Get a QRect for the cursor at the start of the
|
||||
// current word and then translate it down 8 pixels.
|
||||
textCursor = this->textCursor();
|
||||
textCursor.movePosition(QTextCursor::StartOfWord);
|
||||
QRect cr = this->cursorRect(textCursor);
|
||||
cr.translate(0, 8);
|
||||
cr.setWidth(
|
||||
completer_->popup()->sizeHintForColumn(0) +
|
||||
completer_->popup()->verticalScrollBar()->sizeHint().width());
|
||||
completer_->complete(cr);
|
||||
} else {
|
||||
// qDebug() << "no completions - hiding";
|
||||
completer_->popup()->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::checkCompletionTriggers(const QString &txt) {
|
||||
if (!completer_ || completion_triggers_.isEmpty() || txt.isEmpty())
|
||||
return;
|
||||
|
||||
foreach (const QString &tr, completion_triggers_) {
|
||||
if (tr.endsWith(txt)) {
|
||||
QTextCursor tc = this->textCursor();
|
||||
tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
|
||||
tr.length());
|
||||
if (tc.selectedText() == tr) {
|
||||
updateCompleter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::insertCompletion(const QString &completion) {
|
||||
QTextCursor tc = textCursor();
|
||||
tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
|
||||
tc.position() - inpos_ - completer_->insertPos());
|
||||
tc.insertText(completion, chanFormat_[StandardInput]);
|
||||
setTextCursor(tc);
|
||||
}
|
||||
|
||||
void QConsoleWidget::setCompleter(QConsoleWidgetCompleter *c) {
|
||||
if (completer_) {
|
||||
completer_->setWidget(0);
|
||||
QObject::disconnect(completer_, SIGNAL(activated(const QString &)),
|
||||
this, SLOT(insertCompletion(const QString &)));
|
||||
}
|
||||
completer_ = c;
|
||||
if (completer_) {
|
||||
completer_->setWidget(this);
|
||||
QObject::connect(completer_, SIGNAL(activated(const QString &)), this,
|
||||
SLOT(insertCompletion(const QString &)));
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::keyPressEvent(QKeyEvent *e) {
|
||||
if (completer_ && completer_->popup()->isVisible()) {
|
||||
// The following keys are forwarded by the completer to the widget
|
||||
switch (e->key()) {
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Escape:
|
||||
case Qt::Key_Backtab:
|
||||
e->ignore();
|
||||
return; // let the completer do default behavior
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
bool selectionInEditZone = isSelectionInEditZone();
|
||||
|
||||
// check for user abort request
|
||||
if (e->modifiers() & Qt::ControlModifier) {
|
||||
if (e->key() == Qt::Key_Q) // Ctrl-Q aborts
|
||||
{
|
||||
emit abortEvaluation();
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow copying anywhere in the console ...
|
||||
if (e->key() == Qt::Key_C && e->modifiers() == Qt::ControlModifier) {
|
||||
if (textCursor.hasSelection())
|
||||
copy();
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// the rest of key events are ignored during output mode
|
||||
if (mode() != Input) {
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow cut only if the selection is limited to the interactive area ...
|
||||
if (e->key() == Qt::Key_X && e->modifiers() == Qt::ControlModifier) {
|
||||
if (selectionInEditZone)
|
||||
cut();
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow paste only if the selection is in the interactive area ...
|
||||
if (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier) {
|
||||
if (selectionInEditZone || isCursorInEditZone()) {
|
||||
const QMimeData *const clipboard =
|
||||
QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isNull()) {
|
||||
textCursor.insertText(text, channelCharFormat(StandardInput));
|
||||
}
|
||||
}
|
||||
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
int key = e->key();
|
||||
int shiftMod = e->modifiers() == Qt::ShiftModifier;
|
||||
|
||||
if (history_.isActive() && key != Qt::Key_Up && key != Qt::Key_Down)
|
||||
history_.deactivate();
|
||||
|
||||
// Force the cursor back to the interactive area
|
||||
// for all keys except modifiers
|
||||
if (!isCursorInEditZone() && key != Qt::Key_Control &&
|
||||
key != Qt::Key_Shift && key != Qt::Key_Alt) {
|
||||
textCursor.movePosition(QTextCursor::End);
|
||||
setTextCursor(textCursor);
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Qt::Key_Up:
|
||||
// Activate the history and move to the 1st matching history item
|
||||
if (!history_.isActive())
|
||||
history_.activate(getCommandLine());
|
||||
if (history_.move(true))
|
||||
replaceCommandLine(history_.currentValue());
|
||||
else
|
||||
QApplication::beep();
|
||||
e->accept();
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
if (history_.move(false))
|
||||
replaceCommandLine(history_.currentValue());
|
||||
else
|
||||
QApplication::beep();
|
||||
e->accept();
|
||||
|
||||
case Qt::Key_Left:
|
||||
if (textCursor.position() > inpos_)
|
||||
QPlainTextEdit::keyPressEvent(e);
|
||||
else {
|
||||
QApplication::beep();
|
||||
e->accept();
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Delete:
|
||||
e->accept();
|
||||
if (selectionInEditZone)
|
||||
cut();
|
||||
else {
|
||||
// cursor must be in edit zone
|
||||
if (textCursor.position() < inpos_)
|
||||
QApplication::beep();
|
||||
else
|
||||
QPlainTextEdit::keyPressEvent(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Backspace:
|
||||
e->accept();
|
||||
if (selectionInEditZone)
|
||||
cut();
|
||||
else {
|
||||
// cursor must be in edit zone
|
||||
if (textCursor.position() <= inpos_)
|
||||
QApplication::beep();
|
||||
else
|
||||
QPlainTextEdit::keyPressEvent(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Tab:
|
||||
e->accept();
|
||||
handleTabKey();
|
||||
return;
|
||||
|
||||
case Qt::Key_Home:
|
||||
e->accept();
|
||||
textCursor.setPosition(inpos_, shiftMod ? QTextCursor::KeepAnchor
|
||||
: QTextCursor::MoveAnchor);
|
||||
setTextCursor(textCursor);
|
||||
break;
|
||||
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
e->accept();
|
||||
handleReturnKey();
|
||||
break;
|
||||
|
||||
case Qt::Key_Escape:
|
||||
e->accept();
|
||||
replaceCommandLine(QString());
|
||||
break;
|
||||
|
||||
default:
|
||||
e->accept();
|
||||
setCurrentCharFormat(chanFormat_[StandardInput]);
|
||||
QPlainTextEdit::keyPressEvent(e);
|
||||
// check if the last key triggers a completion
|
||||
checkCompletionTriggers(e->text());
|
||||
break;
|
||||
}
|
||||
|
||||
if (completer_ && completer_->popup()->isVisible()) {
|
||||
// if the completer is visible check if it should be updated
|
||||
if (this->textCursor().position() < completion_pos_)
|
||||
completer_->popup()->hide();
|
||||
else
|
||||
updateCompleter();
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::contextMenuEvent(QContextMenuEvent *event) {
|
||||
QMenu *menu = createStandardContextMenu();
|
||||
|
||||
QAction *a;
|
||||
if ((a = menu->findChild<QAction *>("edit-cut")))
|
||||
a->setEnabled(canCut());
|
||||
if ((a = menu->findChild<QAction *>("edit-delete")))
|
||||
a->setEnabled(canCut());
|
||||
if ((a = menu->findChild<QAction *>("edit-paste")))
|
||||
a->setEnabled(canPaste());
|
||||
|
||||
menu->exec(event->globalPos());
|
||||
delete menu;
|
||||
}
|
||||
|
||||
bool QConsoleWidget::isSelectionInEditZone() const {
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
if (!textCursor.hasSelection())
|
||||
return false;
|
||||
|
||||
int selectionStart = textCursor.selectionStart();
|
||||
int selectionEnd = textCursor.selectionEnd();
|
||||
|
||||
return selectionStart >= inpos_ && selectionEnd >= inpos_;
|
||||
}
|
||||
|
||||
bool QConsoleWidget::isCursorInEditZone() const {
|
||||
return textCursor().position() >= inpos_;
|
||||
}
|
||||
|
||||
bool QConsoleWidget::canPaste() const {
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
if (textCursor.position() < inpos_)
|
||||
return false;
|
||||
if (textCursor.anchor() < inpos_)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void QConsoleWidget::replaceCommandLine(const QString &str) {
|
||||
|
||||
// Select the text after the last command prompt ...
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
textCursor.movePosition(QTextCursor::End);
|
||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||
|
||||
// ... and replace it with new string.
|
||||
textCursor.insertText(str, chanFormat_[StandardInput]);
|
||||
|
||||
// move to the end of the document
|
||||
textCursor.movePosition(QTextCursor::End);
|
||||
setTextCursor(textCursor);
|
||||
}
|
||||
|
||||
QString QConsoleWidget::getHistoryPath() {
|
||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
|
||||
QDir::separator() + APP_NAME);
|
||||
return dir.absoluteFilePath(QStringLiteral(".command_history.lst"));
|
||||
}
|
||||
|
||||
void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
|
||||
QTextCharFormat currfmt = currentCharFormat();
|
||||
QTextCursor tc = textCursor();
|
||||
|
||||
if (mode() == Input) {
|
||||
// in Input mode output messages are inserted
|
||||
// before the edit block
|
||||
|
||||
// get offset of current pos from the end
|
||||
int editpos = tc.position();
|
||||
tc.movePosition(QTextCursor::End);
|
||||
editpos = tc.position() - editpos;
|
||||
|
||||
// convert the input pos as relative from the end
|
||||
inpos_ = tc.position() - inpos_;
|
||||
|
||||
// insert block
|
||||
tc.movePosition(QTextCursor::StartOfBlock);
|
||||
tc.insertBlock();
|
||||
tc.movePosition(QTextCursor::PreviousBlock);
|
||||
|
||||
tc.insertText(message, fmt);
|
||||
tc.movePosition(QTextCursor::End);
|
||||
// restore input pos
|
||||
inpos_ = tc.position() - inpos_;
|
||||
// restore the edit pos
|
||||
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, editpos);
|
||||
setTextCursor(tc);
|
||||
setCurrentCharFormat(currfmt);
|
||||
} else {
|
||||
// in output mode messages are appended
|
||||
QTextCursor tc1 = tc;
|
||||
tc1.movePosition(QTextCursor::End);
|
||||
|
||||
// check is cursor was not at the end
|
||||
// (e.g. had been moved per mouse action)
|
||||
bool needsRestore = tc1.position() != tc.position();
|
||||
|
||||
// insert text
|
||||
setTextCursor(tc1);
|
||||
textCursor().insertText(message, fmt);
|
||||
ensureCursorVisible();
|
||||
|
||||
// restore cursor if needed
|
||||
if (needsRestore)
|
||||
setTextCursor(tc);
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::writeStdOut(const QString &s) {
|
||||
write(s, chanFormat_[StandardOutput]);
|
||||
}
|
||||
|
||||
void QConsoleWidget::writeStdErr(const QString &s) {
|
||||
write(s, chanFormat_[StandardError]);
|
||||
}
|
||||
|
||||
/////////////////// QConsoleWidget::History /////////////////////
|
||||
|
||||
QConsoleWidget::History QConsoleWidget::history_;
|
||||
|
||||
QConsoleWidget::History::History(void)
|
||||
: pos_(0), active_(false), maxsize_(10000) {
|
||||
QFile f(QConsoleWidget::getHistoryPath());
|
||||
if (f.open(QFile::ReadOnly)) {
|
||||
QTextStream is(&f);
|
||||
while (!is.atEnd())
|
||||
add(is.readLine());
|
||||
}
|
||||
}
|
||||
QConsoleWidget::History::~History(void) {
|
||||
QFile f(QConsoleWidget::getHistoryPath());
|
||||
if (f.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
QTextStream os(&f);
|
||||
int n = strings_.size();
|
||||
while (n > 0)
|
||||
os << strings_.at(--n) << Qt::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::History::add(const QString &str) {
|
||||
active_ = false;
|
||||
// if (strings_.contains(str)) return;
|
||||
if (strings_.size() == maxsize_)
|
||||
strings_.pop_back();
|
||||
strings_.push_front(str);
|
||||
}
|
||||
|
||||
void QConsoleWidget::History::activate(const QString &tk) {
|
||||
active_ = true;
|
||||
token_ = tk;
|
||||
pos_ = -1;
|
||||
}
|
||||
|
||||
bool QConsoleWidget::History::move(bool dir) {
|
||||
if (active_) {
|
||||
int next = indexOf(dir, pos_);
|
||||
if (pos_ != next) {
|
||||
pos_ = next;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
int QConsoleWidget::History::indexOf(bool dir, int from) const {
|
||||
int i = from, to = from;
|
||||
if (dir) {
|
||||
while (i < strings_.size() - 1) {
|
||||
const QString &si = strings_.at(++i);
|
||||
if (si.startsWith(token_)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (i > 0) {
|
||||
const QString &si = strings_.at(--i);
|
||||
if (si.startsWith(token_)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
/////////////////// Stream manipulators /////////////////////
|
||||
|
||||
QTextStream &waitForInput(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d)
|
||||
d->waitForReadyRead(-1);
|
||||
return s;
|
||||
}
|
||||
|
||||
QTextStream &inputMode(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d && d->widget())
|
||||
d->widget()->setMode(QConsoleWidget::Input);
|
||||
return s;
|
||||
}
|
||||
QTextStream &outChannel(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d)
|
||||
d->setCurrentWriteChannel(QConsoleWidget::StandardOutput);
|
||||
return s;
|
||||
}
|
||||
QTextStream &errChannel(QTextStream &s) {
|
||||
QConsoleIODevice *d = qobject_cast<QConsoleIODevice *>(s.device());
|
||||
if (d)
|
||||
d->setCurrentWriteChannel(QConsoleWidget::StandardError);
|
||||
return s;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
INCLUDEPATH += $$PWD
|
||||
DEPENDPATH += $$PWD
|
||||
|
||||
|
||||
SOURCES += $$PWD/QConsoleWidget.cpp \
|
||||
$$PWD/QConsoleIODevice.cpp
|
||||
|
||||
HEADERS += $$PWD/QConsoleWidget.h \
|
||||
$$PWD/QConsoleIODevice.h
|
|
@ -4,7 +4,7 @@ project(QHexView LANGUAGES CXX)
|
|||
|
||||
find_package(
|
||||
Qt${QT_VERSION_MAJOR}
|
||||
COMPONENTS Widgets Gui
|
||||
COMPONENTS Widgets Gui Concurrent
|
||||
REQUIRED)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)
|
||||
|
@ -57,8 +57,6 @@ add_library(
|
|||
document/commands/baseaddrcommand.h
|
||||
document/commands/encodingchangecommand.cpp
|
||||
document/commands/encodingchangecommand.h
|
||||
document/buffer/qwindriverbuffer.h
|
||||
document/buffer/qwindriverbuffer.cpp
|
||||
document/qstoragedevice.h
|
||||
document/qstoragedevice.cpp
|
||||
document/qhexcursor.cpp
|
||||
|
@ -71,16 +69,15 @@ add_library(
|
|||
document/qhexrenderer.h
|
||||
QHexEdit2/chunks.cpp
|
||||
QHexEdit2/chunks.h
|
||||
QHexEdit2/chunks_win.cpp
|
||||
QHexEdit2/chunks_win.h
|
||||
qhexview.h
|
||||
qhexview.cpp)
|
||||
|
||||
set_target_properties(
|
||||
QHexView
|
||||
PROPERTIES AUTOMOC ON
|
||||
CXX_STANDARD 11
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
target_link_libraries(QHexView PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::Gui)
|
||||
target_link_libraries(
|
||||
QHexView PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent)
|
||||
|
|
|
@ -12,25 +12,17 @@ Chunks::Chunks(QObject *parent) : QObject(parent) {}
|
|||
|
||||
Chunks::Chunks(QIODevice *ioDevice, QObject *parent) : QObject(parent) {
|
||||
setIODevice(ioDevice);
|
||||
if (ioDevice)
|
||||
ioDevice->setParent(this);
|
||||
}
|
||||
|
||||
Chunks::~Chunks() {}
|
||||
|
||||
bool Chunks::setIODevice(QIODevice *ioDevice) {
|
||||
_ioDevice = ioDevice;
|
||||
if (_ioDevice &&
|
||||
(_ioDevice->isOpen() ||
|
||||
_ioDevice->open(QIODevice::ReadOnly))) // Try to open IODevice
|
||||
{
|
||||
_size = _ioDevice->size();
|
||||
_ioDevice->close();
|
||||
} else // Fallback is an empty buffer
|
||||
{
|
||||
QBuffer *buf = new QBuffer(this);
|
||||
_ioDevice = buf;
|
||||
_size = 0;
|
||||
if (ioDevice && ioDevice->isOpen()) {
|
||||
ioDevice->setParent(this);
|
||||
_size = ioDevice->size();
|
||||
_ioDevice = ioDevice;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_chunks.clear();
|
||||
_pos = 0;
|
||||
|
@ -56,8 +48,6 @@ QByteArray Chunks::data(qsizetype pos, qsizetype maxSize) {
|
|||
else if ((pos + maxSize) > _size)
|
||||
maxSize = _size - pos;
|
||||
|
||||
_ioDevice->open(QIODevice::ReadOnly);
|
||||
|
||||
while (maxSize > 0) {
|
||||
chunk.absPos = std::numeric_limits<qsizetype>::max();
|
||||
bool chunksLoopOngoing = true;
|
||||
|
@ -105,7 +95,7 @@ QByteArray Chunks::data(qsizetype pos, qsizetype maxSize) {
|
|||
pos += quint64(readBuffer.size());
|
||||
}
|
||||
}
|
||||
_ioDevice->close();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
@ -113,15 +103,12 @@ bool Chunks::write(QIODevice *iODevice, qsizetype pos, qsizetype count) {
|
|||
if (count == -1)
|
||||
count = _size;
|
||||
|
||||
// fix the bug
|
||||
bool ok = (iODevice->isOpen() && iODevice->isWritable()) ||
|
||||
iODevice->open(QIODevice::WriteOnly);
|
||||
bool ok = iODevice->isOpen() && iODevice->isWritable();
|
||||
if (ok) {
|
||||
for (auto idx = pos; idx < qsizetype(count); idx += BUFFER_SIZE) {
|
||||
QByteArray ba = data(idx, BUFFER_SIZE);
|
||||
iODevice->write(ba);
|
||||
}
|
||||
iODevice->close();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@ -253,10 +240,8 @@ qsizetype Chunks::getChunkIndex(qsizetype absPos) {
|
|||
Chunk newChunk;
|
||||
qsizetype readAbsPos = absPos - ioDelta;
|
||||
qsizetype readPos = (readAbsPos & READ_CHUNK_MASK);
|
||||
_ioDevice->open(QIODevice::ReadOnly);
|
||||
_ioDevice->seek(qint64(readPos));
|
||||
newChunk.data = _ioDevice->read(CHUNK_SIZE);
|
||||
_ioDevice->close();
|
||||
newChunk.absPos = absPos - (readAbsPos - readPos);
|
||||
newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
|
||||
_chunks.insert(insertIdx, newChunk);
|
||||
|
|
|
@ -1,310 +0,0 @@
|
|||
#include "chunks_win.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffffd00)
|
||||
|
||||
/*this file is modified by wingsummer in order to fit the QHexView*/
|
||||
|
||||
// ***************************************** Constructors and file settings
|
||||
|
||||
Chunks_win::Chunks_win(QObject *parent) : QObject(parent) {}
|
||||
|
||||
Chunks_win::Chunks_win(const QStorageInfo &ioDevice, QObject *parent)
|
||||
: QObject(parent) {
|
||||
setIODevice(ioDevice);
|
||||
}
|
||||
|
||||
Chunks_win::~Chunks_win() {
|
||||
if (_handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool Chunks_win::setIODevice(const QStorageInfo &ioDevice) {
|
||||
if (ioDevice.isValid()) {
|
||||
auto device = ioDevice.device();
|
||||
auto devicePrefix = QStringLiteral("\\\\.\\");
|
||||
QString dd = devicePrefix +
|
||||
device.mid(devicePrefix.length(),
|
||||
device.length() - devicePrefix.length() - 1);
|
||||
|
||||
_handle = CreateFileA(dd.toLatin1(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, 0, NULL);
|
||||
if (_handle == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DISK_GEOMETRY diskGeometry;
|
||||
DWORD bytesReturned;
|
||||
if (!DeviceIoControl(_handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
|
||||
&diskGeometry, sizeof(diskGeometry),
|
||||
&bytesReturned, NULL)) {
|
||||
|
||||
CloseHandle(_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
// dont use ioDevice.bytesTotal(),
|
||||
// because it's use GetDiskFreeSpaceEx API to get
|
||||
this->CHUNK_SIZE = diskGeometry.BytesPerSector;
|
||||
_size = diskGeometry.Cylinders.QuadPart *
|
||||
diskGeometry.TracksPerCylinder * diskGeometry.SectorsPerTrack *
|
||||
diskGeometry.BytesPerSector;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_chunks.clear();
|
||||
_pos = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ***************************************** Getting data out of Chunks_win
|
||||
|
||||
QByteArray Chunks_win::data(qsizetype pos, qsizetype maxSize) {
|
||||
qsizetype ioDelta = 0;
|
||||
qsizetype chunkIdx = 0;
|
||||
|
||||
Chunk_win chunk;
|
||||
QByteArray buffer;
|
||||
|
||||
// Do some checks and some arrangements
|
||||
|
||||
if (pos >= _size)
|
||||
return buffer;
|
||||
|
||||
if (maxSize < 0)
|
||||
maxSize = _size;
|
||||
else if ((pos + maxSize) > _size)
|
||||
maxSize = _size - pos;
|
||||
|
||||
while (maxSize > 0) {
|
||||
chunk.absPos = std::numeric_limits<qsizetype>::max();
|
||||
bool chunksLoopOngoing = true;
|
||||
while ((chunkIdx < _chunks.count()) && chunksLoopOngoing) {
|
||||
// In this section, we track changes before our required data and
|
||||
// we take the editdet data, if availible. ioDelta is a difference
|
||||
// counter to justify the read pointer to the original data, if
|
||||
// data in between was deleted or inserted.
|
||||
|
||||
chunk = _chunks[chunkIdx];
|
||||
if (chunk.absPos > pos)
|
||||
chunksLoopOngoing = false;
|
||||
else {
|
||||
chunkIdx += 1;
|
||||
qsizetype count;
|
||||
qsizetype chunkOfs = pos - chunk.absPos;
|
||||
if (qsizetype(maxSize) > chunk.data.size() - chunkOfs) {
|
||||
count = chunk.data.size() - chunkOfs;
|
||||
ioDelta += CHUNK_SIZE - chunk.data.size();
|
||||
} else
|
||||
count = maxSize;
|
||||
if (count > 0) {
|
||||
buffer += chunk.data.mid(chunkOfs, count);
|
||||
maxSize -= count;
|
||||
pos += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((maxSize > 0) && (pos < chunk.absPos)) {
|
||||
// In this section, we read data from the original source. This only
|
||||
// will happen, whe no copied data is available
|
||||
|
||||
qint64 byteCount;
|
||||
QByteArray readBuffer;
|
||||
if (chunk.absPos - pos > qsizetype(maxSize))
|
||||
byteCount = maxSize;
|
||||
else
|
||||
byteCount = chunk.absPos - pos;
|
||||
|
||||
maxSize -= byteCount;
|
||||
|
||||
// Set the pointer to the start of the sector you want to read
|
||||
LARGE_INTEGER sectorOffset;
|
||||
auto sorquot = (pos + ioDelta) / CHUNK_SIZE;
|
||||
auto sorrem = (pos + ioDelta) % CHUNK_SIZE;
|
||||
|
||||
sectorOffset.QuadPart = sorquot * CHUNK_SIZE;
|
||||
Q_ASSERT(SetFilePointerEx(_handle, sectorOffset, NULL, FILE_BEGIN));
|
||||
|
||||
readBuffer.fill(0, CHUNK_SIZE);
|
||||
DWORD bytesRead;
|
||||
Q_ASSERT(ReadFile(_handle, readBuffer.data(), CHUNK_SIZE,
|
||||
&bytesRead, NULL));
|
||||
readBuffer = readBuffer.mid(sorrem, byteCount);
|
||||
buffer += readBuffer;
|
||||
pos += readBuffer.size();
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool Chunks_win::write(QIODevice *iODevice, qsizetype pos, qsizetype count) {
|
||||
if (count == -1)
|
||||
count = _size;
|
||||
|
||||
// fix the bug
|
||||
bool ok = (iODevice->isOpen() && iODevice->isWritable()) ||
|
||||
iODevice->open(QIODevice::WriteOnly);
|
||||
if (ok) {
|
||||
for (auto idx = pos; idx < qsizetype(count); idx += CHUNK_SIZE) {
|
||||
QByteArray ba = data(idx, CHUNK_SIZE);
|
||||
iODevice->write(ba);
|
||||
}
|
||||
iODevice->close();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// ***************************************** Search API
|
||||
|
||||
qsizetype Chunks_win::indexOf(const QByteArray &ba, qsizetype from) {
|
||||
qsizetype result = -1;
|
||||
QByteArray buffer;
|
||||
|
||||
for (auto pos = from; (pos < _size) && (result < 0); pos += CHUNK_SIZE) {
|
||||
buffer = data(pos, CHUNK_SIZE + ba.size() - 1);
|
||||
int findPos = buffer.indexOf(ba);
|
||||
if (findPos >= 0)
|
||||
result = pos + findPos;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
qsizetype Chunks_win::lastIndexOf(const QByteArray &ba, qsizetype from) {
|
||||
qint64 result = -1;
|
||||
QByteArray buffer;
|
||||
|
||||
for (auto pos = from; (pos > 0) && (result < 0); pos -= CHUNK_SIZE) {
|
||||
auto sPos = pos - CHUNK_SIZE - ba.size() + 1;
|
||||
/*if (sPos < 0)
|
||||
sPos = 0;*/
|
||||
buffer = data(sPos, pos - sPos);
|
||||
int findPos = buffer.lastIndexOf(ba);
|
||||
if (findPos >= 0)
|
||||
result = sPos + findPos;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ***************************************** Char manipulations
|
||||
|
||||
bool Chunks_win::insert(qsizetype pos, char b) {
|
||||
if (pos > _size)
|
||||
return false;
|
||||
qsizetype chunkIdx;
|
||||
if (pos == _size) {
|
||||
chunkIdx = getChunkIndex(pos - 1);
|
||||
} else
|
||||
chunkIdx = getChunkIndex(pos);
|
||||
auto posInBa = pos - _chunks[chunkIdx].absPos;
|
||||
_chunks[chunkIdx].data.insert(int(posInBa), b);
|
||||
_chunks[chunkIdx].dataChanged.insert(int(posInBa), char(1));
|
||||
for (int idx = chunkIdx + 1; idx < _chunks.size(); idx++)
|
||||
_chunks[idx].absPos += 1;
|
||||
_size += 1;
|
||||
_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Chunks_win::overwrite(qsizetype pos, char b) {
|
||||
if (pos >= _size)
|
||||
return false;
|
||||
auto chunkIdx = getChunkIndex(pos);
|
||||
auto posInBa = pos - _chunks[chunkIdx].absPos;
|
||||
_chunks[chunkIdx].data[int(posInBa)] = b;
|
||||
_chunks[chunkIdx].dataChanged[int(posInBa)] = char(1);
|
||||
_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Chunks_win::removeAt(qsizetype pos) {
|
||||
if (pos >= _size)
|
||||
return false;
|
||||
auto chunkIdx = getChunkIndex(pos);
|
||||
auto posInBa = pos - _chunks[chunkIdx].absPos;
|
||||
_chunks[chunkIdx].data.remove(int(posInBa), 1);
|
||||
_chunks[chunkIdx].dataChanged.remove(int(posInBa), 1);
|
||||
for (int idx = chunkIdx + 1; idx < _chunks.size(); idx++)
|
||||
_chunks[idx].absPos -= 1;
|
||||
_size -= 1;
|
||||
_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ***************************************** Utility functions
|
||||
|
||||
char Chunks_win::operator[](qsizetype pos) {
|
||||
auto d = data(pos, 1);
|
||||
if (d.isEmpty())
|
||||
return '0';
|
||||
return d.at(0);
|
||||
}
|
||||
|
||||
qsizetype Chunks_win::pos() { return _pos; }
|
||||
|
||||
qsizetype Chunks_win::size() { return _size; }
|
||||
|
||||
qsizetype Chunks_win::getChunkIndex(qsizetype absPos) {
|
||||
// This routine checks, if there is already a copied chunk available. If so,
|
||||
// it returns a reference to it. If there is no copied chunk available,
|
||||
// original data will be copied into a new chunk.
|
||||
|
||||
qsizetype foundIdx = -1;
|
||||
qsizetype insertIdx = 0;
|
||||
qsizetype ioDelta = 0;
|
||||
|
||||
// fix the bug by wingsummer
|
||||
if (absPos < 0) {
|
||||
Chunk_win newChunk;
|
||||
newChunk.data = QByteArray(CHUNK_SIZE, 0);
|
||||
newChunk.absPos = 0;
|
||||
newChunk.dataChanged = nullptr;
|
||||
_chunks.insert(insertIdx, newChunk);
|
||||
return insertIdx;
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < _chunks.size(); idx++) {
|
||||
Chunk_win chunk = _chunks[idx];
|
||||
if ((absPos >= chunk.absPos) &&
|
||||
(absPos < (chunk.absPos + chunk.data.size()))) {
|
||||
foundIdx = idx;
|
||||
break;
|
||||
}
|
||||
if (absPos < chunk.absPos) {
|
||||
insertIdx = idx;
|
||||
break;
|
||||
}
|
||||
ioDelta += chunk.data.size() - CHUNK_SIZE;
|
||||
insertIdx = idx + 1;
|
||||
}
|
||||
|
||||
if (foundIdx == -1) {
|
||||
Chunk_win newChunk;
|
||||
auto readAbsPos = absPos - ioDelta;
|
||||
auto readPos = (readAbsPos & READ_CHUNK_MASK);
|
||||
|
||||
// Set the pointer to the start of the sector you want to read
|
||||
LARGE_INTEGER sectorOffset;
|
||||
sectorOffset.QuadPart = readPos;
|
||||
Q_ASSERT(SetFilePointerEx(_handle, sectorOffset, NULL, FILE_BEGIN));
|
||||
|
||||
newChunk.data.fill(0, CHUNK_SIZE);
|
||||
DWORD bytesRead = 0;
|
||||
Q_ASSERT(ReadFile(_handle, newChunk.data.data(), CHUNK_SIZE, &bytesRead,
|
||||
NULL));
|
||||
newChunk.data.resize(bytesRead);
|
||||
|
||||
newChunk.absPos = absPos - (readAbsPos - readPos);
|
||||
newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
|
||||
_chunks.insert(insertIdx, newChunk);
|
||||
foundIdx = insertIdx;
|
||||
}
|
||||
return foundIdx;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,78 +0,0 @@
|
|||
#ifndef Chunks_winWIN_H
|
||||
#define Chunks_winWIN_H
|
||||
|
||||
/** \cond docNever */
|
||||
|
||||
/*! The Chunks_win class is the storage backend for QHexEdit.
|
||||
*
|
||||
* When QHexEdit loads data, Chunks_win access them using a QIODevice interface.
|
||||
* When the app uses a QByteArray interface, QBuffer is used to provide again a
|
||||
* QIODevice like interface. No data will be changed, therefore Chunks_win opens
|
||||
* the QIODevice in QIODevice::ReadOnly mode. After every access Chunks_win
|
||||
* closes the QIODevice, that's why external applications can overwrite files
|
||||
* while QHexEdit shows them.
|
||||
*
|
||||
* When the the user starts to edit the data, Chunks_win creates a local copy of
|
||||
* a chunk of data (4 kilobytes) and notes all changes there. Parallel to that
|
||||
* chunk, there is a second chunk, which keep track of which bytes are changed
|
||||
* and which not.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
// note: this class is only for storage device reading
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <QStorageInfo>
|
||||
#include <Windows.h>
|
||||
|
||||
struct Chunk_win {
|
||||
QByteArray data;
|
||||
QByteArray dataChanged;
|
||||
qsizetype absPos;
|
||||
};
|
||||
|
||||
class Chunks_win : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Constructors and file settings
|
||||
Chunks_win(QObject *parent = nullptr);
|
||||
Chunks_win(const QStorageInfo &ioDevice, QObject *parent);
|
||||
|
||||
~Chunks_win();
|
||||
|
||||
bool setIODevice(const QStorageInfo &ioDevice);
|
||||
|
||||
// Getting data out of Chunks_win
|
||||
QByteArray data(qsizetype pos = 0, qsizetype maxSize = -1);
|
||||
bool write(QIODevice *iODevice, qsizetype pos = 0, qsizetype count = -1);
|
||||
|
||||
// Search API
|
||||
qsizetype indexOf(const QByteArray &ba, qsizetype from);
|
||||
qsizetype lastIndexOf(const QByteArray &ba, qsizetype from);
|
||||
|
||||
// Char manipulations
|
||||
bool insert(qsizetype pos, char b);
|
||||
bool overwrite(qsizetype pos, char b);
|
||||
bool removeAt(qsizetype pos);
|
||||
|
||||
// Utility functions
|
||||
char operator[](qsizetype pos);
|
||||
qsizetype pos();
|
||||
qsizetype size();
|
||||
|
||||
private:
|
||||
qsizetype getChunkIndex(qsizetype absPos);
|
||||
QIODevice *_ioDevice;
|
||||
qsizetype _pos;
|
||||
qsizetype _size;
|
||||
qsizetype CHUNK_SIZE;
|
||||
HANDLE _handle = INVALID_HANDLE_VALUE;
|
||||
QList<Chunk_win> _chunks;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // Chunks_win_H
|
|
@ -13,7 +13,10 @@ QFileBuffer::QFileBuffer(QObject *parent) : QHexBuffer(parent) {
|
|||
|
||||
QFileBuffer::~QFileBuffer() {}
|
||||
|
||||
uchar QFileBuffer::at(qsizetype idx) { return uchar(_chunks->data(idx, 1)[0]); }
|
||||
uchar QFileBuffer::at(qsizetype idx) {
|
||||
auto data = _chunks->data(idx, 1);
|
||||
return uchar(data[0]);
|
||||
}
|
||||
|
||||
qsizetype QFileBuffer::length() const { return _chunks->size(); }
|
||||
|
||||
|
@ -34,8 +37,7 @@ QByteArray QFileBuffer::read(qsizetype offset, qsizetype length) {
|
|||
}
|
||||
|
||||
bool QFileBuffer::read(QIODevice *device) {
|
||||
_chunks->setIODevice(device);
|
||||
return true;
|
||||
return _chunks->setIODevice(device);
|
||||
}
|
||||
|
||||
void QFileBuffer::write(QIODevice *device) { _chunks->write(device); }
|
||||
|
|
|
@ -24,8 +24,8 @@ public:
|
|||
qsizetype lastIndexOf(const QByteArray &ba, qsizetype from) override;
|
||||
|
||||
private:
|
||||
Chunks *_chunks;
|
||||
uchar *m_memory;
|
||||
Chunks *_chunks = nullptr;
|
||||
uchar *m_memory = nullptr;
|
||||
};
|
||||
|
||||
#endif // QFILEBUFFER_H
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
#include "qwindriverbuffer.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include "document/qstoragedevice.h"
|
||||
|
||||
/*
|
||||
* this file is implemented by wingsummer,
|
||||
* 使用 QHexEdit2 的代码来实现轻松访问上 GB 的文件,
|
||||
* 该类作者并未实现
|
||||
*/
|
||||
|
||||
QWinDriverBuffer::QWinDriverBuffer(QObject *parent) : QHexBuffer(parent) {
|
||||
_chunks = new Chunks_win(parent);
|
||||
}
|
||||
|
||||
QWinDriverBuffer::~QWinDriverBuffer() {}
|
||||
|
||||
uchar QWinDriverBuffer::at(qsizetype idx) {
|
||||
return uchar(_chunks->data(idx, 1)[0]);
|
||||
}
|
||||
|
||||
qsizetype QWinDriverBuffer::length() const { return _chunks->size(); }
|
||||
|
||||
void QWinDriverBuffer::insert(qsizetype offset, const QByteArray &data) {
|
||||
for (int i = 0; i < data.length(); i++) {
|
||||
_chunks->insert(offset + i, data.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
void QWinDriverBuffer::remove(qsizetype offset, qsizetype length) {
|
||||
for (uint i = 0; i < uint(length); i++) {
|
||||
_chunks->removeAt(offset + i);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray QWinDriverBuffer::read(qsizetype offset, qsizetype length) {
|
||||
return _chunks->data(offset, length);
|
||||
}
|
||||
|
||||
bool QWinDriverBuffer::read(QIODevice *device) {
|
||||
if (device == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto driver = qobject_cast<QStorageDevice *>(device);
|
||||
if (driver == nullptr) {
|
||||
delete device;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto storage = driver->storage(); // only use name
|
||||
delete device;
|
||||
|
||||
_chunks->setIODevice(storage);
|
||||
return true;
|
||||
}
|
||||
|
||||
void QWinDriverBuffer::write(QIODevice *device) { _chunks->write(device); }
|
||||
|
||||
qsizetype QWinDriverBuffer::indexOf(const QByteArray &ba, qsizetype from) {
|
||||
return _chunks->indexOf(ba, from);
|
||||
}
|
||||
|
||||
qsizetype QWinDriverBuffer::lastIndexOf(const QByteArray &ba, qsizetype from) {
|
||||
return _chunks->lastIndexOf(ba, from);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,35 +0,0 @@
|
|||
#ifndef QWINDRIVERBUFFER_H
|
||||
#define QWINDRIVERBUFFER_H
|
||||
|
||||
#include <qnamespace.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include "QHexEdit2/chunks_win.h"
|
||||
#include "qhexbuffer.h"
|
||||
#include <QFile>
|
||||
|
||||
class QWinDriverBuffer : public QHexBuffer {
|
||||
|
||||
public:
|
||||
explicit QWinDriverBuffer(QObject *parent = nullptr);
|
||||
~QWinDriverBuffer() override;
|
||||
uchar at(qsizetype idx) override;
|
||||
qsizetype length() const override;
|
||||
void insert(qsizetype offset, const QByteArray &data) override;
|
||||
void remove(qsizetype offset, qsizetype length) override;
|
||||
QByteArray read(qsizetype offset, qsizetype length) override;
|
||||
bool read(QIODevice *device) override;
|
||||
void write(QIODevice *device) override;
|
||||
|
||||
qsizetype indexOf(const QByteArray &ba, qsizetype from) override;
|
||||
qsizetype lastIndexOf(const QByteArray &ba, qsizetype from) override;
|
||||
|
||||
private:
|
||||
Chunks_win *_chunks;
|
||||
uchar *m_memory;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // QWINDRIVERBUFFER_H
|
|
@ -1,8 +1,8 @@
|
|||
#include "bookmarkclearcommand.h"
|
||||
|
||||
BookMarkClearCommand::BookMarkClearCommand(QHexDocument *doc,
|
||||
QList<BookMarkStruct> bookmarks,
|
||||
QUndoCommand *parent)
|
||||
BookMarkClearCommand::BookMarkClearCommand(
|
||||
QHexDocument *doc, const QMap<qsizetype, QString> &bookmarks,
|
||||
QUndoCommand *parent)
|
||||
: QUndoCommand(parent), m_doc(doc), m_bookmarks(bookmarks) {}
|
||||
|
||||
void BookMarkClearCommand::redo() { m_doc->clearBookMark(); }
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
#define BOOKMARKCLEARCOMMAND_H
|
||||
|
||||
#include "document/qhexdocument.h"
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QUndoCommand>
|
||||
|
||||
class BookMarkClearCommand : public QUndoCommand {
|
||||
public:
|
||||
BookMarkClearCommand(QHexDocument *doc, QList<BookMarkStruct> bookmarks,
|
||||
BookMarkClearCommand(QHexDocument *doc,
|
||||
const QMap<qsizetype, QString> &bookmarks,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
void undo() override;
|
||||
|
@ -15,7 +17,7 @@ public:
|
|||
|
||||
protected:
|
||||
QHexDocument *m_doc;
|
||||
QList<BookMarkStruct> m_bookmarks;
|
||||
QMap<qsizetype, QString> m_bookmarks;
|
||||
};
|
||||
|
||||
#endif // BOOKMARKCLEARCOMMAND_H
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "hexcommand.h"
|
||||
|
||||
HexCommand::HexCommand(QHexBuffer *buffer, QHexCursor *cursor, int nibbleindex,
|
||||
HexCommand::HexCommand(QHexDocument *doc, QHexCursor *cursor, int nibbleindex,
|
||||
QUndoCommand *parent)
|
||||
: QUndoCommand(parent), m_buffer(buffer), m_offset(0), m_length(0),
|
||||
m_cursor(cursor), m_nibbleindex(nibbleindex) {}
|
||||
: QUndoCommand(parent), m_doc(doc), m_cursor(cursor), m_offset(0),
|
||||
m_length(0), m_nibbleindex(nibbleindex) {}
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
#ifndef HEXCOMMAND_H
|
||||
#define HEXCOMMAND_H
|
||||
|
||||
#include "document/buffer/qhexbuffer.h"
|
||||
#include "document/qhexcursor.h"
|
||||
#include "document/qhexdocument.h"
|
||||
|
||||
#include <QUndoCommand>
|
||||
|
||||
class HexCommand : public QUndoCommand {
|
||||
public:
|
||||
HexCommand(QHexBuffer *buffer, QHexCursor *cursor, int nibbleindex,
|
||||
HexCommand(QHexDocument *doc, QHexCursor *cursor, int nibbleindex,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
protected:
|
||||
QHexBuffer *m_buffer;
|
||||
QHexDocument *m_doc;
|
||||
QHexCursor *m_cursor;
|
||||
|
||||
qsizetype m_offset;
|
||||
qsizetype m_length;
|
||||
QByteArray m_data;
|
||||
|
||||
QHexCursor *m_cursor;
|
||||
int m_nibbleindex;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
#include "insertcommand.h"
|
||||
|
||||
InsertCommand::InsertCommand(QHexBuffer *buffer, qsizetype offset,
|
||||
const QByteArray &data, QHexCursor *cursor,
|
||||
InsertCommand::InsertCommand(QHexDocument *doc, QHexCursor *cursor,
|
||||
qsizetype offset, const QByteArray &data,
|
||||
int nibbleindex, QUndoCommand *parent)
|
||||
: HexCommand(buffer, cursor, nibbleindex, parent) {
|
||||
: HexCommand(doc, cursor, nibbleindex, parent) {
|
||||
m_offset = offset;
|
||||
m_data = data;
|
||||
m_length = data.length();
|
||||
}
|
||||
|
||||
void InsertCommand::undo() {
|
||||
m_buffer->remove(m_offset, m_data.length());
|
||||
m_doc->_remove(m_offset, m_data.length());
|
||||
m_cursor->setPos(m_offset, m_nibbleindex);
|
||||
}
|
||||
void InsertCommand::redo() {
|
||||
m_buffer->insert(m_offset, m_data);
|
||||
m_doc->_insert(m_offset, m_data);
|
||||
if (m_data.length() == 1 && m_nibbleindex) {
|
||||
m_cursor->setPos(m_offset, 0);
|
||||
} else {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
class InsertCommand : public HexCommand {
|
||||
public:
|
||||
InsertCommand(QHexBuffer *buffer, qsizetype offset, const QByteArray &data,
|
||||
QHexCursor *cursor, int nibbleindex,
|
||||
InsertCommand(QHexDocument *doc, QHexCursor *cursor, qsizetype offset,
|
||||
const QByteArray &data, int nibbleindex,
|
||||
QUndoCommand *parent = nullptr);
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#include "removecommand.h"
|
||||
|
||||
RemoveCommand::RemoveCommand(QHexBuffer *buffer, qsizetype offset,
|
||||
RemoveCommand::RemoveCommand(QHexDocument *doc, qsizetype offset,
|
||||
qsizetype length, QHexCursor *cursor,
|
||||
int nibbleindex, QUndoCommand *parent)
|
||||
: HexCommand(buffer, cursor, nibbleindex, parent) {
|
||||
: HexCommand(doc, cursor, nibbleindex, parent) {
|
||||
m_offset = offset;
|
||||
m_length = length;
|
||||
m_data = m_buffer->read(m_offset, m_length);
|
||||
m_data = doc->read(m_offset, m_length);
|
||||
}
|
||||
|
||||
void RemoveCommand::undo() {
|
||||
m_buffer->insert(m_offset, m_data);
|
||||
m_doc->_insert(m_offset, m_data);
|
||||
if (m_length > 1) {
|
||||
m_cursor->setPos(m_offset + m_length - 1, 1);
|
||||
} else {
|
||||
|
@ -24,5 +24,5 @@ void RemoveCommand::undo() {
|
|||
|
||||
void RemoveCommand::redo() {
|
||||
m_cursor->setPos(m_offset, m_nibbleindex);
|
||||
m_buffer->remove(m_offset, m_length);
|
||||
m_doc->_remove(m_offset, m_length);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
class RemoveCommand : public HexCommand {
|
||||
public:
|
||||
RemoveCommand(QHexBuffer *buffer, qsizetype offset, qsizetype length,
|
||||
RemoveCommand(QHexDocument *doc, qsizetype offset, qsizetype length,
|
||||
QHexCursor *cursor, int nibbleindex,
|
||||
QUndoCommand *parent = nullptr);
|
||||
void undo() override;
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
#include "replacecommand.h"
|
||||
#include "document/qhexdocument.h"
|
||||
|
||||
ReplaceCommand::ReplaceCommand(QHexBuffer *buffer, qsizetype offset,
|
||||
ReplaceCommand::ReplaceCommand(QHexDocument *doc, qsizetype offset,
|
||||
const QByteArray &data, QHexCursor *cursor,
|
||||
int nibbleindex, QUndoCommand *parent)
|
||||
: HexCommand(buffer, cursor, nibbleindex, parent) {
|
||||
: HexCommand(doc, cursor, nibbleindex, parent) {
|
||||
|
||||
m_offset = offset;
|
||||
m_data = data;
|
||||
m_length = data.length();
|
||||
m_olddata = m_buffer->read(m_offset, m_length);
|
||||
m_olddata = doc->read(m_offset, m_length);
|
||||
}
|
||||
|
||||
void ReplaceCommand::undo() {
|
||||
m_buffer->replace(m_offset, m_olddata);
|
||||
m_doc->_replace(m_offset, m_olddata);
|
||||
m_cursor->setPos(m_offset, m_nibbleindex);
|
||||
}
|
||||
|
||||
void ReplaceCommand::redo() {
|
||||
m_buffer->replace(m_offset, m_data);
|
||||
m_doc->_replace(m_offset, m_data);
|
||||
if (m_data.length() == 1 && m_nibbleindex) {
|
||||
m_cursor->setPos(m_offset, 0);
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
class ReplaceCommand : public HexCommand {
|
||||
public:
|
||||
ReplaceCommand(QHexBuffer *buffer, qsizetype offset, const QByteArray &data,
|
||||
ReplaceCommand(QHexDocument *doc, qsizetype offset, const QByteArray &data,
|
||||
QHexCursor *cursor, int nibbleindex,
|
||||
QUndoCommand *parent = nullptr);
|
||||
void undo() override;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "metaaddcommand.h"
|
||||
|
||||
MetaAddCommand::MetaAddCommand(QHexMetadata *hexmeta,
|
||||
QHexMetadataAbsoluteItem &meta,
|
||||
const QHexMetadataItem &meta,
|
||||
QUndoCommand *parent)
|
||||
: MetaCommand(hexmeta, meta, parent) {}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
class MetaAddCommand : public MetaCommand {
|
||||
public:
|
||||
MetaAddCommand(QHexMetadata *hexmeta, QHexMetadataAbsoluteItem &meta,
|
||||
MetaAddCommand(QHexMetadata *hexmeta, const QHexMetadataItem &meta,
|
||||
QUndoCommand *parent = nullptr);
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "metaclearcommand.h"
|
||||
|
||||
MetaClearCommand::MetaClearCommand(QHexMetadata *hexmeta,
|
||||
QList<QHexMetadataAbsoluteItem> metas,
|
||||
const QVector<QHexMetadataItem> &metas,
|
||||
QUndoCommand *parent)
|
||||
: QUndoCommand(parent), m_hexmeta(hexmeta), m_metas(metas) {}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
class MetaClearCommand : public QUndoCommand {
|
||||
public:
|
||||
MetaClearCommand(QHexMetadata *hexmeta,
|
||||
QList<QHexMetadataAbsoluteItem> metas,
|
||||
const QVector<QHexMetadataItem> &metas,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
void undo() override;
|
||||
|
@ -20,7 +20,7 @@ public:
|
|||
|
||||
protected:
|
||||
QHexMetadata *m_hexmeta;
|
||||
QList<QHexMetadataAbsoluteItem> m_metas;
|
||||
QVector<QHexMetadataItem> m_metas;
|
||||
};
|
||||
|
||||
#endif // METACLEARCOMMAND_H
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "metacommand.h"
|
||||
|
||||
MetaCommand::MetaCommand(QHexMetadata *hexmeta, QHexMetadataAbsoluteItem &meta,
|
||||
MetaCommand::MetaCommand(QHexMetadata *hexmeta, const QHexMetadataItem &meta,
|
||||
QUndoCommand *parent)
|
||||
: QUndoCommand(parent), m_hexmeta(hexmeta), m_meta(meta) {}
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
|
||||
class MetaCommand : public QUndoCommand {
|
||||
public:
|
||||
MetaCommand(QHexMetadata *hexmeta, QHexMetadataAbsoluteItem &meta,
|
||||
MetaCommand(QHexMetadata *hexmeta, const QHexMetadataItem &meta,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
protected:
|
||||
QHexMetadata *m_hexmeta;
|
||||
QHexMetadataAbsoluteItem m_meta;
|
||||
QHexMetadataItem m_meta;
|
||||
};
|
||||
|
||||
#endif // METACOMMAND_H
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "metaremovecommand.h"
|
||||
|
||||
MetaRemoveCommand::MetaRemoveCommand(QHexMetadata *hexmeta,
|
||||
QHexMetadataAbsoluteItem &meta,
|
||||
const QHexMetadataItem &meta,
|
||||
QUndoCommand *parent)
|
||||
: MetaCommand(hexmeta, meta, parent) {}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
class MetaRemoveCommand : public MetaCommand {
|
||||
public:
|
||||
MetaRemoveCommand(QHexMetadata *hexmeta, QHexMetadataAbsoluteItem &meta,
|
||||
MetaRemoveCommand(QHexMetadata *hexmeta, const QHexMetadataItem &meta,
|
||||
QUndoCommand *parent = nullptr);
|
||||
|
||||
void undo() override;
|
||||
|
|
|
@ -9,7 +9,7 @@ MetaRemovePosCommand::MetaRemovePosCommand(QHexMetadata *hexmeta, qsizetype pos,
|
|||
void MetaRemovePosCommand::redo() { m_hexmeta->removeMetadata(m_pos); }
|
||||
|
||||
void MetaRemovePosCommand::undo() {
|
||||
for (auto item : olditems)
|
||||
for (auto &item : olditems)
|
||||
m_hexmeta->metadata(item.begin, item.end, item.foreground,
|
||||
item.background, item.comment);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
protected:
|
||||
QHexMetadata *m_hexmeta;
|
||||
qsizetype m_pos;
|
||||
QList<QHexMetadataAbsoluteItem> olditems;
|
||||
QVector<QHexMetadataItem> olditems;
|
||||
};
|
||||
|
||||
#endif // METAREMOVEPOSCOMMAND_H
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "metareplacecommand.h"
|
||||
|
||||
MetaReplaceCommand::MetaReplaceCommand(QHexMetadata *hexmeta,
|
||||
QHexMetadataAbsoluteItem &meta,
|
||||
QHexMetadataAbsoluteItem &oldmeta,
|
||||
const QHexMetadataItem &meta,
|
||||
const QHexMetadataItem &oldmeta,
|
||||
QUndoCommand *parent)
|
||||
: MetaCommand(hexmeta, meta, parent), m_old(oldmeta) {}
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
class MetaReplaceCommand : public MetaCommand {
|
||||
public:
|
||||
MetaReplaceCommand(QHexMetadata *hexmeta, QHexMetadataAbsoluteItem &meta,
|
||||
QHexMetadataAbsoluteItem &oldmeta,
|
||||
MetaReplaceCommand(QHexMetadata *hexmeta, const QHexMetadataItem &meta,
|
||||
const QHexMetadataItem &oldmeta,
|
||||
QUndoCommand *parent = nullptr);
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
private:
|
||||
QHexMetadataAbsoluteItem m_old;
|
||||
QHexMetadataItem m_old;
|
||||
};
|
||||
|
||||
#endif // METAREPLACECOMMAND_H
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
#include "qhexcursor.h"
|
||||
#include <QWidget>
|
||||
|
||||
void QHexCursor::setSelection(qsizetype offset, qsizetype length) {
|
||||
auto lld = lldiv(offset, m_position.lineWidth);
|
||||
m_position.line = lld.quot;
|
||||
m_position.column = int(lld.rem);
|
||||
moveTo(m_position);
|
||||
lld = lldiv(offset + length - 1, m_position.lineWidth);
|
||||
m_selection.line = lld.quot;
|
||||
m_selection.column = int(lld.rem);
|
||||
}
|
||||
#include <QtConcurrent/QtConcurrentMap>
|
||||
|
||||
QHexCursor::QHexCursor(QObject *parent)
|
||||
: QObject(parent), m_insertionmode(QHexCursor::OverwriteMode) {
|
||||
|
@ -20,104 +12,168 @@ QHexCursor::QHexCursor(QObject *parent)
|
|||
m_selection.line = m_selection.column = 0;
|
||||
|
||||
m_position.nibbleindex = m_selection.nibbleindex = 1;
|
||||
|
||||
setLineWidth(DEFAULT_HEX_LINE_LENGTH);
|
||||
}
|
||||
|
||||
const QHexPosition &QHexCursor::selectionStart() const {
|
||||
if (m_position.line < m_selection.line)
|
||||
return m_position;
|
||||
|
||||
if (m_position.line == m_selection.line) {
|
||||
if (m_position.column < m_selection.column)
|
||||
return m_position;
|
||||
}
|
||||
|
||||
return m_selection;
|
||||
const QHexPosition &QHexCursor::selectionStart(qsizetype index) const {
|
||||
return m_sels.at(index).start;
|
||||
}
|
||||
|
||||
const QHexPosition &QHexCursor::selectionEnd() const {
|
||||
if (m_position.line > m_selection.line)
|
||||
return m_position;
|
||||
|
||||
if (m_position.line == m_selection.line) {
|
||||
if (m_position.column > m_selection.column)
|
||||
return m_position;
|
||||
}
|
||||
|
||||
return m_selection;
|
||||
const QHexPosition &QHexCursor::selectionEnd(qsizetype index) const {
|
||||
return m_sels.at(index).end;
|
||||
}
|
||||
|
||||
const QHexPosition &QHexCursor::position() const { return m_position; }
|
||||
|
||||
qsizetype QHexCursor::selectionCount() const { return m_sels.size(); }
|
||||
|
||||
const QHexSelection &QHexCursor::selection(qsizetype index) const {
|
||||
return m_sels.at(index);
|
||||
}
|
||||
|
||||
QHexCursor::InsertionMode QHexCursor::insertionMode() const {
|
||||
return m_insertionmode;
|
||||
}
|
||||
qsizetype QHexCursor::selectionLength() const {
|
||||
return this->selectionEnd() - this->selectionStart() + 1;
|
||||
|
||||
qsizetype QHexCursor::selectionLength(qsizetype index) const {
|
||||
return m_sels.at(index).length();
|
||||
}
|
||||
qsizetype QHexCursor::currentLine() const { return m_position.line; }
|
||||
int QHexCursor::currentColumn() const { return m_position.column; }
|
||||
int QHexCursor::currentNibble() const { return m_position.nibbleindex; }
|
||||
qsizetype QHexCursor::selectionLine() const { return m_selection.line; }
|
||||
qsizetype QHexCursor::selectionColumn() const { return m_selection.column; }
|
||||
int QHexCursor::selectionNibble() const { return m_selection.nibbleindex; }
|
||||
|
||||
qsizetype QHexCursor::currentSelectionLength() const {
|
||||
if (hasPreviewSelection() && m_preMode != SelectionRemove) {
|
||||
return qAbs(m_position - m_selection);
|
||||
}
|
||||
|
||||
qsizetype len = 0;
|
||||
auto res = QtConcurrent::blockingMappedReduced(
|
||||
m_sels, &QHexSelection::length,
|
||||
QOverload<
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
const qsizetype &
|
||||
#else
|
||||
QVector<qsizetype>::parameter_type
|
||||
#endif
|
||||
>::of(&QVector<qsizetype>::append));
|
||||
for (auto &item : res) {
|
||||
len += item;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
qsizetype QHexCursor::currentLine() const { return m_selection.line; }
|
||||
|
||||
int QHexCursor::currentColumn() const { return m_selection.column; }
|
||||
|
||||
int QHexCursor::currentNibble() const { return m_selection.nibbleindex; }
|
||||
|
||||
bool QHexCursor::isLineSelected(qsizetype line) const {
|
||||
if (!this->hasSelection())
|
||||
return false;
|
||||
|
||||
auto first = std::min(m_position.line, m_selection.line);
|
||||
auto last = std::max(m_position.line, m_selection.line);
|
||||
if (isLineSelected(previewSelection(), line)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((line < first) || (line > last))
|
||||
return false;
|
||||
for (auto &sel : m_sels) {
|
||||
if (isLineSelected(sel, line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHexCursor::hasSelection() const { return m_position != m_selection; }
|
||||
bool QHexCursor::hasSelection() const {
|
||||
return hasPreviewSelection() || hasInternalSelection();
|
||||
}
|
||||
|
||||
void QHexCursor::clearSelection() {
|
||||
bool QHexCursor::hasInternalSelection() const { return !m_sels.isEmpty(); }
|
||||
|
||||
void QHexCursor::clearPreviewSelection() {
|
||||
m_selection = m_position;
|
||||
emit positionChanged();
|
||||
m_preMode = SelectionNormal;
|
||||
}
|
||||
|
||||
void QHexCursor::moveTo(const QHexPosition &pos) {
|
||||
this->moveTo(pos.line, pos.column, pos.nibbleindex);
|
||||
void QHexCursor::clearSelection() { m_sels.clear(); }
|
||||
|
||||
void QHexCursor::moveTo(const QHexPosition &pos, bool clearSelection) {
|
||||
this->moveTo(pos.line, pos.column, pos.nibbleindex, clearSelection);
|
||||
}
|
||||
void QHexCursor::select(const QHexPosition &pos) {
|
||||
this->select(pos.line, pos.column, pos.nibbleindex);
|
||||
void QHexCursor::select(const QHexPosition &pos,
|
||||
QHexCursor::SelectionModes mode) {
|
||||
this->select(pos.line, pos.column, pos.nibbleindex, mode);
|
||||
}
|
||||
|
||||
void QHexCursor::moveTo(qsizetype line, int column, int nibbleindex) {
|
||||
m_selection.line = line;
|
||||
m_selection.column = column;
|
||||
m_selection.nibbleindex = nibbleindex;
|
||||
|
||||
this->select(line, column, nibbleindex);
|
||||
}
|
||||
|
||||
void QHexCursor::select(qsizetype line, int column, int nibbleindex) {
|
||||
void QHexCursor::moveTo(qsizetype line, int column, int nibbleindex,
|
||||
bool clearSelection) {
|
||||
m_position.line = line;
|
||||
m_position.column = qMax(0, column); // fix the bug by wingsummer
|
||||
m_position.nibbleindex = nibbleindex;
|
||||
|
||||
m_selection = m_position;
|
||||
|
||||
if (clearSelection) {
|
||||
m_sels.clear();
|
||||
}
|
||||
|
||||
emit positionChanged();
|
||||
}
|
||||
|
||||
void QHexCursor::moveTo(qsizetype offset) {
|
||||
auto line = offset / m_lineWidth;
|
||||
this->moveTo(line, int(offset - (line * m_lineWidth)));
|
||||
void QHexCursor::select(qsizetype line, int column, int nibbleindex,
|
||||
SelectionModes modes) {
|
||||
if (modes.testFlag(SelectionPreview)) {
|
||||
m_selection.line = line;
|
||||
m_selection.column = qMax(0, column); // fix the bug by wingsummer
|
||||
m_selection.nibbleindex = nibbleindex;
|
||||
|
||||
modes.setFlag(SelectionPreview, false);
|
||||
m_preMode = SelectionMode(int(modes));
|
||||
} else {
|
||||
QHexSelection sel;
|
||||
|
||||
sel.start = m_position;
|
||||
|
||||
sel.end.line = line;
|
||||
sel.end.column = column;
|
||||
sel.end.nibbleindex = nibbleindex;
|
||||
|
||||
sel.normalize();
|
||||
|
||||
switch (modes) {
|
||||
case SelectionAdd:
|
||||
mergeAdd(sel);
|
||||
break;
|
||||
case SelectionNormal:
|
||||
m_sels.clear();
|
||||
m_sels.append(sel);
|
||||
break;
|
||||
case SelectionRemove:
|
||||
mergeRemove(sel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emit positionChanged();
|
||||
emit selectionChanged();
|
||||
}
|
||||
|
||||
void QHexCursor::setPos(qsizetype offset, int nibbleindex) {
|
||||
void QHexCursor::moveTo(qsizetype offset, bool clearSelection) {
|
||||
auto line = offset / m_lineWidth;
|
||||
this->moveTo(line, int(offset - (line * m_lineWidth)), nibbleindex);
|
||||
this->moveTo(line, int(offset - (line * m_lineWidth)), clearSelection);
|
||||
}
|
||||
|
||||
void QHexCursor::select(qsizetype length) {
|
||||
this->select(
|
||||
m_position.line,
|
||||
std::min(m_lineWidth - 1, int(m_position.column + length - 1)));
|
||||
void QHexCursor::setPos(qsizetype offset, int nibbleindex,
|
||||
bool clearSelection) {
|
||||
auto line = offset / m_lineWidth;
|
||||
this->moveTo(line, int(offset - (line * m_lineWidth)), nibbleindex,
|
||||
clearSelection);
|
||||
}
|
||||
|
||||
void QHexCursor::select(qsizetype length, QHexCursor::SelectionModes mode) {
|
||||
this->select(m_position.line,
|
||||
std::min(m_lineWidth - 1, int(m_position.column + length - 1)),
|
||||
1, mode);
|
||||
}
|
||||
|
||||
void QHexCursor::selectOffset(qsizetype offset, qsizetype length) {
|
||||
|
@ -137,6 +193,11 @@ void QHexCursor::setLineWidth(quint8 width) {
|
|||
m_lineWidth = width;
|
||||
m_position.lineWidth = width;
|
||||
m_selection.lineWidth = width;
|
||||
|
||||
for (auto &sel : m_sels) {
|
||||
sel.start.lineWidth = width;
|
||||
sel.end.lineWidth = width;
|
||||
}
|
||||
}
|
||||
|
||||
void QHexCursor::switchInsertionMode() {
|
||||
|
@ -147,3 +208,100 @@ void QHexCursor::switchInsertionMode() {
|
|||
|
||||
emit insertionModeChanged();
|
||||
}
|
||||
|
||||
bool QHexCursor::hasPreviewSelection() const {
|
||||
return m_selection != m_position;
|
||||
}
|
||||
|
||||
void QHexCursor::mergeRemove(const QHexSelection &sel) {
|
||||
Q_ASSERT(sel.isNormalized());
|
||||
|
||||
QList<QHexSelection> buffer;
|
||||
QMutex locker;
|
||||
QtConcurrent::blockingMap(m_sels,
|
||||
[&buffer, &locker, &sel](QHexSelection &s) {
|
||||
auto r = s.removeSelection(sel);
|
||||
if (r.has_value()) {
|
||||
QMutexLocker l(&locker);
|
||||
buffer.append(r.value());
|
||||
}
|
||||
});
|
||||
|
||||
// clean up invalid selections
|
||||
auto cleanup = [](const QHexSelection &s) { return s.start == s.end; };
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
m_sels.removeIf(cleanup);
|
||||
#else
|
||||
m_sels.erase(std::remove_if(m_sels.begin(), m_sels.end(), cleanup));
|
||||
#endif
|
||||
|
||||
QtConcurrent::blockingMap(
|
||||
buffer, [&locker, this](QHexSelection &s) { mergeAdd(s, &locker); });
|
||||
}
|
||||
|
||||
void QHexCursor::mergeAdd(const QHexSelection &sel, QMutex *locker) {
|
||||
bool merged = false;
|
||||
Q_ASSERT(sel.isNormalized());
|
||||
|
||||
for (auto p = m_sels.begin(); p != m_sels.end(); ++p) {
|
||||
merged = p->mergeSelection(sel, locker);
|
||||
if (merged) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!merged) {
|
||||
m_sels.append(sel);
|
||||
}
|
||||
}
|
||||
|
||||
bool QHexCursor::isLineSelected(const QHexSelection &sel,
|
||||
qsizetype line) const {
|
||||
auto first = std::min(sel.start.line, sel.end.line);
|
||||
auto last = std::max(sel.start.line, sel.end.line);
|
||||
|
||||
if ((line >= first) && (line <= last))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QHexSelection QHexCursor::previewSelection() const {
|
||||
QHexSelection sel;
|
||||
sel.start = m_position;
|
||||
sel.end = m_selection;
|
||||
return sel;
|
||||
}
|
||||
|
||||
void QHexCursor::setPreviewSelectionMode(SelectionMode mode) {
|
||||
m_preMode = mode;
|
||||
}
|
||||
|
||||
QHexCursor::SelectionMode QHexCursor::previewSelectionMode() const {
|
||||
return m_preMode;
|
||||
}
|
||||
|
||||
void QHexCursor::mergePreviewSelection() {
|
||||
auto ss = QHexSelection{m_position, m_selection}.normalized();
|
||||
switch (m_preMode) {
|
||||
case SelectionNormal:
|
||||
if (m_sels.isEmpty()) {
|
||||
m_sels.append(ss);
|
||||
}
|
||||
break;
|
||||
case SelectionAdd:
|
||||
mergeAdd(ss);
|
||||
break;
|
||||
case SelectionRemove:
|
||||
mergeRemove(ss);
|
||||
break;
|
||||
case SelectionSingle:
|
||||
m_sels.clear();
|
||||
m_sels.append(ss);
|
||||
break;
|
||||
case SelectionPreview:
|
||||
// should not go here
|
||||
break;
|
||||
}
|
||||
clearPreviewSelection();
|
||||
}
|
||||
|
|
|
@ -1,23 +1,40 @@
|
|||
#ifndef QHEXCURSOR_H
|
||||
#define QHEXCURSOR_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#define DEFAULT_HEX_LINE_LENGTH 0x10
|
||||
#define DEFAULT_AREA_IDENTATION 0x01
|
||||
|
||||
struct QHexPosition {
|
||||
qsizetype line;
|
||||
int column;
|
||||
quint8 lineWidth;
|
||||
int nibbleindex;
|
||||
qsizetype line = -1;
|
||||
int column = -1;
|
||||
quint8 lineWidth = 0;
|
||||
int nibbleindex = -1;
|
||||
|
||||
QHexPosition() = default;
|
||||
inline qsizetype offset() const {
|
||||
return static_cast<qsizetype>(line * lineWidth) + column;
|
||||
}
|
||||
inline int operator-(const QHexPosition &rhs) const {
|
||||
return int(this->offset() - rhs.offset());
|
||||
inline qsizetype operator-(const QHexPosition &rhs) const {
|
||||
return this->offset() - rhs.offset();
|
||||
}
|
||||
inline void operator++() {
|
||||
this->column++;
|
||||
if (this->column >= lineWidth) {
|
||||
this->line++;
|
||||
this->column = 0;
|
||||
}
|
||||
}
|
||||
inline void operator--() {
|
||||
this->column--;
|
||||
if (this->column < 0) {
|
||||
this->line--;
|
||||
this->column = lineWidth - 1;
|
||||
}
|
||||
}
|
||||
inline bool operator==(const QHexPosition &rhs) const {
|
||||
return (line == rhs.line) && (column == rhs.column) &&
|
||||
|
@ -27,6 +44,135 @@ struct QHexPosition {
|
|||
return (line != rhs.line) || (column != rhs.column) ||
|
||||
(nibbleindex != rhs.nibbleindex);
|
||||
}
|
||||
inline bool operator<(const QHexPosition &rhs) const {
|
||||
return this->offset() < rhs.offset();
|
||||
}
|
||||
inline bool operator>(const QHexPosition &rhs) const {
|
||||
return this->offset() > rhs.offset();
|
||||
}
|
||||
inline bool operator<=(const QHexPosition &rhs) const {
|
||||
return this->offset() <= rhs.offset();
|
||||
}
|
||||
inline bool operator>=(const QHexPosition &rhs) const {
|
||||
return this->offset() >= rhs.offset();
|
||||
}
|
||||
};
|
||||
|
||||
struct QHexSelection {
|
||||
QHexPosition start;
|
||||
QHexPosition end;
|
||||
|
||||
void normalize(QMutex *locker = nullptr) {
|
||||
if (locker) {
|
||||
locker->lock();
|
||||
}
|
||||
if (end < start) {
|
||||
std::swap(start, end);
|
||||
}
|
||||
if (locker) {
|
||||
locker->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
qsizetype length() const { return qAbs(end - start) + 1; }
|
||||
|
||||
bool contains(const QHexSelection &sel) const {
|
||||
Q_ASSERT(isNormalized());
|
||||
return this->start <= sel.start && this->end >= sel.end;
|
||||
}
|
||||
|
||||
bool isLineSelected(qsizetype line) const {
|
||||
Q_ASSERT(isNormalized());
|
||||
if (this->start.line == line || this->end.line == line) {
|
||||
return true;
|
||||
}
|
||||
return this->start.line < line && line < this->end.line;
|
||||
}
|
||||
|
||||
bool isNormalized() const { return end >= start; }
|
||||
|
||||
QHexSelection normalized() const {
|
||||
QHexSelection sel = *this;
|
||||
if (end < start) {
|
||||
std::swap(sel.start, sel.end);
|
||||
}
|
||||
return sel;
|
||||
}
|
||||
|
||||
bool isIntersected(const QHexSelection &sel) const {
|
||||
Q_ASSERT(isNormalized());
|
||||
return !(sel.end < this->start || sel.start > this->end);
|
||||
}
|
||||
|
||||
void intersect(const QHexSelection &sel, QMutex *locker = nullptr) {
|
||||
Q_ASSERT(isNormalized());
|
||||
auto s = sel.normalized();
|
||||
if (locker) {
|
||||
locker->lock();
|
||||
}
|
||||
this->start = qMax(this->start, s.start);
|
||||
this->end = qMin(this->end, s.end);
|
||||
if (locker) {
|
||||
locker->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
Q_REQUIRED_RESULT std::optional<QHexSelection>
|
||||
removeSelection(const QHexSelection &sel, QMutex *locker = nullptr) {
|
||||
Q_ASSERT(isNormalized());
|
||||
Q_ASSERT(sel.isNormalized());
|
||||
if (locker) {
|
||||
locker->lock();
|
||||
}
|
||||
|
||||
if (sel.start <= this->start) {
|
||||
if (sel.end >= this->start) {
|
||||
if (sel.end < this->end) {
|
||||
this->start = sel.end;
|
||||
++this->start;
|
||||
} else {
|
||||
// makes it invalid, delete later
|
||||
this->end = this->start;
|
||||
}
|
||||
}
|
||||
} else if (sel.start > this->start && sel.start < this->end) {
|
||||
this->end = sel.start;
|
||||
--this->end;
|
||||
if (sel.end < this->end) {
|
||||
// break into two ranges
|
||||
QHexSelection sel;
|
||||
sel.start = sel.end;
|
||||
sel.end = this->end;
|
||||
|
||||
if (locker) {
|
||||
locker->unlock();
|
||||
}
|
||||
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
|
||||
if (locker) {
|
||||
locker->unlock();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool mergeSelection(const QHexSelection &sel, QMutex *locker = nullptr) {
|
||||
Q_ASSERT(isNormalized());
|
||||
if (isIntersected(sel)) {
|
||||
if (locker) {
|
||||
locker->lock();
|
||||
}
|
||||
this->start = qMin(this->start, sel.start);
|
||||
this->end = qMax(this->end, sel.end);
|
||||
if (locker) {
|
||||
locker->unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class QHexCursor : public QObject {
|
||||
|
@ -35,50 +181,89 @@ class QHexCursor : public QObject {
|
|||
public:
|
||||
enum InsertionMode { OverwriteMode, InsertMode };
|
||||
|
||||
enum SelectionMode {
|
||||
SelectionNormal = 0,
|
||||
SelectionAdd = 1,
|
||||
SelectionRemove = 2,
|
||||
SelectionSingle = 4,
|
||||
SelectionPreview = 16 // a flag
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(SelectionModes, SelectionMode)
|
||||
|
||||
public:
|
||||
explicit QHexCursor(QObject *parent = nullptr);
|
||||
|
||||
public:
|
||||
const QHexPosition &selectionStart() const;
|
||||
const QHexPosition &selectionEnd() const;
|
||||
const QHexPosition &position() const;
|
||||
void setSelection(qsizetype offset,
|
||||
qsizetype length); // added by wingsummer
|
||||
|
||||
qsizetype selectionCount() const;
|
||||
const QHexSelection &selection(qsizetype index) const;
|
||||
const QHexPosition &selectionStart(qsizetype index) const;
|
||||
const QHexPosition &selectionEnd(qsizetype index) const;
|
||||
|
||||
InsertionMode insertionMode() const;
|
||||
qsizetype selectionLength() const;
|
||||
|
||||
qsizetype currentLine() const;
|
||||
int currentColumn() const;
|
||||
int currentNibble() const;
|
||||
qsizetype selectionLine() const;
|
||||
qsizetype selectionColumn() const;
|
||||
int selectionNibble() const;
|
||||
|
||||
qsizetype selectionLength(qsizetype index) const;
|
||||
|
||||
qsizetype currentSelectionLength() const;
|
||||
|
||||
bool atEnd() const;
|
||||
|
||||
bool isLineSelected(qsizetype line) const;
|
||||
bool hasSelection() const;
|
||||
bool hasInternalSelection() const;
|
||||
|
||||
void clearPreviewSelection();
|
||||
void clearSelection();
|
||||
|
||||
public:
|
||||
void moveTo(const QHexPosition &pos);
|
||||
void moveTo(qsizetype line, int column, int nibbleindex = 1);
|
||||
void moveTo(qsizetype offset);
|
||||
void setPos(qsizetype offset, int nibbleindex);
|
||||
// 和 moveTo 其实一样,只是为了不冲突
|
||||
void select(const QHexPosition &pos);
|
||||
void select(qsizetype line, int column, int nibbleindex = 1);
|
||||
void select(qsizetype length);
|
||||
void moveTo(const QHexPosition &pos, bool clearSelection = false);
|
||||
void moveTo(qsizetype line, int column, int nibbleindex = 1,
|
||||
bool clearSelection = false);
|
||||
void moveTo(qsizetype offset, bool clearSelection = false);
|
||||
void setPos(qsizetype offset, int nibbleindex, bool clearSelection = false);
|
||||
|
||||
void select(const QHexPosition &pos,
|
||||
QHexCursor::SelectionModes mode = SelectionNormal);
|
||||
void select(qsizetype line, int column, int nibbleindex = 1,
|
||||
QHexCursor::SelectionModes mode = SelectionNormal);
|
||||
void select(qsizetype length,
|
||||
QHexCursor::SelectionModes mode = SelectionNormal);
|
||||
|
||||
void selectOffset(qsizetype offset, qsizetype length);
|
||||
void setInsertionMode(InsertionMode mode);
|
||||
void setLineWidth(quint8 width);
|
||||
void switchInsertionMode();
|
||||
|
||||
bool hasPreviewSelection() const;
|
||||
QHexSelection previewSelection() const;
|
||||
void setPreviewSelectionMode(SelectionMode mode);
|
||||
SelectionMode previewSelectionMode() const;
|
||||
void mergePreviewSelection();
|
||||
|
||||
private:
|
||||
void mergeRemove(const QHexSelection &sel);
|
||||
void mergeAdd(const QHexSelection &sel, QMutex *locker = nullptr);
|
||||
|
||||
bool isLineSelected(const QHexSelection &sel, qsizetype line) const;
|
||||
|
||||
signals:
|
||||
void positionChanged();
|
||||
void insertionModeChanged();
|
||||
void selectionChanged();
|
||||
|
||||
private:
|
||||
InsertionMode m_insertionmode;
|
||||
quint8 m_lineWidth;
|
||||
QHexPosition m_position, m_selection;
|
||||
|
||||
SelectionMode m_preMode;
|
||||
QList<QHexSelection> m_sels;
|
||||
};
|
||||
|
||||
#endif // QHEXCURSOR_H
|
||||
|
|
|
@ -17,32 +17,35 @@
|
|||
#include <QFile>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "buffer/qwindriverbuffer.h"
|
||||
#include "qstoragedevice.h"
|
||||
#endif
|
||||
|
||||
/*======================*/
|
||||
// added by wingsummer
|
||||
|
||||
QList<qsizetype> QHexDocument::getsBookmarkPos(qsizetype line) {
|
||||
QList<qsizetype> QHexDocument::getLineBookmarksPos(qsizetype line) {
|
||||
QList<qsizetype> pos;
|
||||
auto begin = m_hexlinewidth * line;
|
||||
auto end = m_hexlinewidth + begin;
|
||||
for (auto item : bookmarks) {
|
||||
if (item.pos >= begin && item.pos <= end)
|
||||
pos.push_back(item.pos);
|
||||
|
||||
auto lbound = _bookmarks.lowerBound(begin);
|
||||
auto ubound = _bookmarks.upperBound(end);
|
||||
|
||||
for (auto p = lbound; p != ubound; ++p) {
|
||||
pos.append(p.key());
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool QHexDocument::lineHasBookMark(qsizetype line) {
|
||||
auto begin = m_hexlinewidth * line;
|
||||
auto end = m_hexlinewidth + begin;
|
||||
for (auto item : bookmarks) {
|
||||
if (item.pos >= begin && item.pos <= end)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
auto lbound = _bookmarks.lowerBound(begin);
|
||||
auto ubound = _bookmarks.upperBound(end);
|
||||
|
||||
return lbound != ubound;
|
||||
}
|
||||
|
||||
void QHexDocument::addUndoCommand(QUndoCommand *command) {
|
||||
|
@ -90,11 +93,61 @@ bool QHexDocument::metafgVisible() { return m_metafg; }
|
|||
|
||||
bool QHexDocument::metaCommentVisible() { return m_metacomment; }
|
||||
|
||||
bool QHexDocument::isDocSaved() { return m_undostack->isClean(); }
|
||||
void QHexDocument::insertBookMarkAdjust(qsizetype offset, qsizetype length) {
|
||||
QMap<qsizetype, QString> bms;
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
auto rmbegin = _bookmarks.lowerBound(offset);
|
||||
#else
|
||||
auto rmbegin = std::as_const(_bookmarks).lowerBound(offset);
|
||||
#endif
|
||||
auto addbegin = _bookmarks.upperBound(offset);
|
||||
for (auto p = addbegin; p != _bookmarks.end(); ++p) {
|
||||
bms.insert(p.key() + length, p.value());
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
for (auto it = rmbegin; it != _bookmarks.end();) {
|
||||
it = _bookmarks.erase(it);
|
||||
}
|
||||
#else
|
||||
_bookmarks.erase(rmbegin, _bookmarks.cend());
|
||||
#endif
|
||||
|
||||
_bookmarks.insert(bms);
|
||||
}
|
||||
|
||||
void QHexDocument::removeBookMarkAdjust(qsizetype offset, qsizetype length) {
|
||||
QMap<qsizetype, QString> bms;
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
auto rmbegin = _bookmarks.lowerBound(offset);
|
||||
#else
|
||||
auto rmbegin = std::as_const(_bookmarks).lowerBound(offset);
|
||||
#endif
|
||||
auto addbegin = _bookmarks.upperBound(offset);
|
||||
for (auto p = addbegin; p != _bookmarks.end(); ++p) {
|
||||
bms.insert(p.key() - length, p.value());
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
for (auto it = rmbegin; it != _bookmarks.end();) {
|
||||
it = _bookmarks.erase(it);
|
||||
}
|
||||
#else
|
||||
_bookmarks.erase(rmbegin, _bookmarks.cend());
|
||||
#endif
|
||||
|
||||
_bookmarks.insert(bms);
|
||||
}
|
||||
|
||||
bool QHexDocument::isDocSaved() { return m_isSaved; }
|
||||
|
||||
void QHexDocument::setDocSaved(bool b) {
|
||||
if (b) {
|
||||
m_undostack->setClean();
|
||||
}
|
||||
m_isSaved = b;
|
||||
emit documentSaved(b);
|
||||
}
|
||||
|
||||
|
@ -118,10 +171,8 @@ bool QHexDocument::setKeepSize(bool b) {
|
|||
return true;
|
||||
}
|
||||
|
||||
QList<BookMarkStruct> *QHexDocument::bookMarksPtr() { return &bookmarks; }
|
||||
|
||||
const QList<BookMarkStruct> &QHexDocument::bookMarks() const {
|
||||
return bookmarks;
|
||||
const QMap<qsizetype, QString> &QHexDocument::bookMarks() const {
|
||||
return _bookmarks;
|
||||
}
|
||||
|
||||
bool QHexDocument::AddBookMark(qsizetype pos, QString comment) {
|
||||
|
@ -131,25 +182,29 @@ bool QHexDocument::AddBookMark(qsizetype pos, QString comment) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::RemoveBookMark(qsizetype pos) {
|
||||
m_undostack->push(new BookMarkRemoveCommand(this, pos, bookMark(pos)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::ModBookMark(qsizetype pos, QString comment) {
|
||||
if (!m_keepsize)
|
||||
return false;
|
||||
m_undostack->push(
|
||||
new BookMarkReplaceCommand(this, pos, comment, bookMarkComment(pos)));
|
||||
new BookMarkReplaceCommand(this, pos, comment, bookMark(pos)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::ClearBookMark() {
|
||||
if (!m_keepsize)
|
||||
return false;
|
||||
m_undostack->push(new BookMarkClearCommand(this, getAllBookMarks()));
|
||||
m_undostack->push(new BookMarkClearCommand(this, _bookmarks));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::addBookMark(qsizetype pos, QString comment) {
|
||||
if (m_keepsize && !existBookMark(pos)) {
|
||||
BookMarkStruct b{pos, comment};
|
||||
bookmarks.append(b);
|
||||
_bookmarks.insert(pos, comment);
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
emit bookMarkChanged();
|
||||
|
@ -158,97 +213,46 @@ bool QHexDocument::addBookMark(qsizetype pos, QString comment) {
|
|||
return false;
|
||||
}
|
||||
|
||||
QString QHexDocument::bookMarkComment(qsizetype pos) {
|
||||
if (pos > 0 && pos < m_buffer->length()) {
|
||||
for (auto item : bookmarks) {
|
||||
if (item.pos == pos) {
|
||||
return item.comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
QString QHexDocument::bookMark(qsizetype pos) { return _bookmarks.value(pos); }
|
||||
|
||||
bool QHexDocument::bookMarkExists(qsizetype pos) {
|
||||
return _bookmarks.contains(pos);
|
||||
}
|
||||
|
||||
BookMarkStruct QHexDocument::bookMark(qsizetype pos) {
|
||||
if (pos > 0 && pos < m_buffer->length()) {
|
||||
for (auto item : bookmarks) {
|
||||
if (item.pos == pos) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BookMarkStruct{-1, ""};
|
||||
qsizetype QHexDocument::bookMarkPos(qsizetype index) {
|
||||
Q_ASSERT(index >= 0 && index < _bookmarks.size());
|
||||
return *(std::next(_bookmarks.keyBegin(), index));
|
||||
}
|
||||
|
||||
BookMarkStruct QHexDocument::bookMarkByIndex(qsizetype index) {
|
||||
if (index >= 0 && index < bookmarks.count()) {
|
||||
return bookmarks.at(index);
|
||||
} else {
|
||||
BookMarkStruct b;
|
||||
b.pos = -1;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
bool QHexDocument::RemoveBookMarks(QList<qsizetype> &pos) {
|
||||
bool QHexDocument::RemoveBookMarks(const QList<qsizetype> &pos) {
|
||||
if (!m_keepsize)
|
||||
return false;
|
||||
m_undostack->beginMacro("RemoveBookMarks");
|
||||
for (auto p : pos) {
|
||||
m_undostack->push(
|
||||
new BookMarkRemoveCommand(this, p, bookMarkComment(p)));
|
||||
m_undostack->push(new BookMarkRemoveCommand(this, p, bookMark(p)));
|
||||
}
|
||||
m_undostack->endMacro();
|
||||
emit documentChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::RemoveBookMark(qsizetype index) {
|
||||
if (!m_keepsize)
|
||||
return false;
|
||||
auto b = bookmarks.at(index);
|
||||
m_undostack->push(new BookMarkRemoveCommand(this, b.pos, b.comment));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::removeBookMark(qsizetype pos) {
|
||||
if (m_keepsize && pos >= 0 && pos < m_buffer->length()) {
|
||||
int index = 0;
|
||||
for (auto item : bookmarks) {
|
||||
if (pos == item.pos) {
|
||||
bookmarks.removeAt(index);
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
emit bookMarkChanged();
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
if (m_keepsize) {
|
||||
auto ret = _bookmarks.remove(pos) != 0;
|
||||
if (ret) {
|
||||
setDocSaved(false);
|
||||
}
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHexDocument::removeBookMarkByIndex(qsizetype index) {
|
||||
if (m_keepsize && index >= 0 && index < bookmarks.count()) {
|
||||
bookmarks.removeAt(index);
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
emit bookMarkChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHexDocument::modBookMark(qsizetype pos, QString comment) {
|
||||
if (m_keepsize && pos > 0 && pos < m_buffer->length()) {
|
||||
for (auto &item : bookmarks) {
|
||||
if (item.pos == pos) {
|
||||
item.comment = comment;
|
||||
setDocSaved(false);
|
||||
emit bookMarkChanged();
|
||||
return true;
|
||||
}
|
||||
bool QHexDocument::modBookMark(qsizetype pos, const QString &comment) {
|
||||
if (m_keepsize) {
|
||||
if (_bookmarks.contains(pos)) {
|
||||
_bookmarks[pos] = comment;
|
||||
setDocSaved(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -256,7 +260,7 @@ bool QHexDocument::modBookMark(qsizetype pos, QString comment) {
|
|||
|
||||
bool QHexDocument::clearBookMark() {
|
||||
if (m_keepsize) {
|
||||
bookmarks.clear();
|
||||
_bookmarks.clear();
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
emit bookMarkChanged();
|
||||
|
@ -266,22 +270,13 @@ bool QHexDocument::clearBookMark() {
|
|||
}
|
||||
|
||||
bool QHexDocument::existBookMark(qsizetype pos) {
|
||||
for (auto item : bookmarks) {
|
||||
if (item.pos == pos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return _bookmarks.contains(pos);
|
||||
}
|
||||
|
||||
const QList<BookMarkStruct> &QHexDocument::getAllBookMarks() {
|
||||
return bookmarks;
|
||||
}
|
||||
qsizetype QHexDocument::bookMarksCount() const { return _bookmarks.count(); }
|
||||
|
||||
qsizetype QHexDocument::bookMarksCount() const { return bookmarks.count(); }
|
||||
|
||||
void QHexDocument::applyBookMarks(const QList<BookMarkStruct> &books) {
|
||||
bookmarks.append(books);
|
||||
void QHexDocument::applyBookMarks(const QMap<qsizetype, QString> &books) {
|
||||
_bookmarks = books;
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
}
|
||||
|
@ -338,10 +333,7 @@ bool QHexDocument::insert(qsizetype offset, const QByteArray &data) {
|
|||
if (m_keepsize || m_readonly || m_islocked ||
|
||||
(offset < m_buffer->length() && m_metadata->hasMetadata()))
|
||||
return false;
|
||||
m_buffer->insert(offset, data);
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
return true;
|
||||
return this->_insert(offset, data);
|
||||
}
|
||||
|
||||
bool QHexDocument::replace(qsizetype offset, uchar b) {
|
||||
|
@ -353,16 +345,44 @@ bool QHexDocument::replace(qsizetype offset, uchar b) {
|
|||
bool QHexDocument::replace(qsizetype offset, const QByteArray &data) {
|
||||
if (m_readonly || m_islocked)
|
||||
return false;
|
||||
return this->_replace(offset, data);
|
||||
}
|
||||
|
||||
bool QHexDocument::remove(qsizetype offset, qsizetype len) {
|
||||
if (m_keepsize || m_readonly || m_islocked || m_metadata->hasMetadata())
|
||||
return false;
|
||||
return this->_remove(offset, len);
|
||||
}
|
||||
|
||||
bool QHexDocument::_insert(qsizetype offset, uchar b) {
|
||||
return this->_insert(offset, QByteArray(1, char(b)));
|
||||
}
|
||||
|
||||
bool QHexDocument::_insert(qsizetype offset, const QByteArray &data) {
|
||||
m_buffer->insert(offset, data);
|
||||
auto len = data.size();
|
||||
insertBookMarkAdjust(offset, len);
|
||||
m_metadata->insertAdjust(offset, len);
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::_replace(qsizetype offset, uchar b) {
|
||||
return this->_replace(offset, QByteArray(1, char(b)));
|
||||
}
|
||||
|
||||
bool QHexDocument::_replace(qsizetype offset, const QByteArray &data) {
|
||||
m_buffer->replace(offset, data);
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHexDocument::remove(qsizetype offset, qsizetype len) {
|
||||
if (m_keepsize || m_readonly || m_islocked || m_metadata->hasMetadata())
|
||||
return false;
|
||||
bool QHexDocument::_remove(qsizetype offset, qsizetype len) {
|
||||
m_buffer->remove(offset, len);
|
||||
removeBookMarkAdjust(offset, len);
|
||||
m_metadata->removeAdjust(offset, len);
|
||||
setDocSaved(false);
|
||||
emit documentChanged();
|
||||
return true;
|
||||
|
@ -392,8 +412,10 @@ QHexDocument::QHexDocument(QHexBuffer *buffer, bool readonly)
|
|||
m_metadata = new QHexMetadata(m_undostack, this);
|
||||
m_metadata->setLineWidth(m_hexlinewidth);
|
||||
|
||||
connect(m_metadata, &QHexMetadata::metadataChanged, this,
|
||||
&QHexDocument::metaDataChanged);
|
||||
connect(m_metadata, &QHexMetadata::metadataChanged, this, [this] {
|
||||
setDocSaved(false);
|
||||
emit metaDataChanged();
|
||||
});
|
||||
|
||||
/*=======================*/
|
||||
// added by wingsummer
|
||||
|
@ -425,9 +447,6 @@ void QHexDocument::setHexLineWidth(quint8 value) {
|
|||
}
|
||||
|
||||
QHexMetadata *QHexDocument::metadata() const { return m_metadata; }
|
||||
QByteArray QHexDocument::read(qsizetype offset, qsizetype len) {
|
||||
return m_buffer->read(offset, len);
|
||||
}
|
||||
|
||||
char QHexDocument::at(qsizetype offset) const {
|
||||
return char(m_buffer->at(offset));
|
||||
|
@ -457,6 +476,12 @@ void QHexDocument::redo() {
|
|||
emit documentChanged();
|
||||
}
|
||||
|
||||
void QHexDocument::beginMarco(const QString &text) {
|
||||
m_undostack->beginMacro(text);
|
||||
}
|
||||
|
||||
void QHexDocument::endMarco() { m_undostack->endMacro(); }
|
||||
|
||||
void QHexDocument::Insert(QHexCursor *cursor, qsizetype offset, uchar b,
|
||||
int nibbleindex) {
|
||||
if (m_keepsize || m_readonly || m_islocked)
|
||||
|
@ -473,14 +498,12 @@ void QHexDocument::Replace(QHexCursor *cursor, qsizetype offset, uchar b,
|
|||
|
||||
void QHexDocument::Insert(QHexCursor *cursor, qsizetype offset,
|
||||
const QByteArray &data, int nibbleindex) {
|
||||
if (m_keepsize || m_readonly || m_islocked ||
|
||||
(offset < m_buffer->length() && m_metadata->hasMetadata()))
|
||||
if (m_keepsize || m_readonly || m_islocked)
|
||||
return;
|
||||
if (!m_metadata->hasMetadata())
|
||||
m_undostack->push(
|
||||
new InsertCommand(m_buffer, offset, data, cursor, nibbleindex));
|
||||
else
|
||||
m_buffer->insert(offset, data);
|
||||
|
||||
m_undostack->push(
|
||||
new InsertCommand(this, cursor, offset, data, nibbleindex));
|
||||
|
||||
emit documentChanged();
|
||||
}
|
||||
|
||||
|
@ -489,7 +512,7 @@ void QHexDocument::Replace(QHexCursor *cursor, qsizetype offset,
|
|||
if (m_readonly || m_islocked)
|
||||
return;
|
||||
m_undostack->push(
|
||||
new ReplaceCommand(m_buffer, offset, data, cursor, nibbleindex));
|
||||
new ReplaceCommand(this, offset, data, cursor, nibbleindex));
|
||||
emit documentChanged();
|
||||
}
|
||||
|
||||
|
@ -498,7 +521,7 @@ bool QHexDocument::Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
|
|||
if (m_keepsize || m_readonly || m_islocked || m_metadata->hasMetadata())
|
||||
return false;
|
||||
m_undostack->push(
|
||||
new RemoveCommand(m_buffer, offset, len, cursor, nibbleindex));
|
||||
new RemoveCommand(this, offset, len, cursor, nibbleindex));
|
||||
emit documentChanged();
|
||||
return true;
|
||||
}
|
||||
|
@ -539,7 +562,8 @@ QHexDocument *QHexDocument::fromLargeFile(const QString &filename,
|
|||
if (!filename.isEmpty()) {
|
||||
f->setFileName(filename);
|
||||
QHexBuffer *hexbuffer = new QFileBuffer();
|
||||
if (hexbuffer->read(f)) {
|
||||
if (f->open(readonly ? QFile::ReadOnly : QFile::ReadWrite) &&
|
||||
hexbuffer->read(f)) {
|
||||
return new QHexDocument(hexbuffer,
|
||||
readonly); // modified by wingsummer
|
||||
} else {
|
||||
|
@ -558,8 +582,10 @@ QHexDocument *QHexDocument::fromStorageDriver(const QStorageInfo &storage,
|
|||
#ifdef Q_OS_WIN
|
||||
auto f = new QStorageDevice;
|
||||
f->setStorage(storage);
|
||||
auto hexbuffer = new QWinDriverBuffer();
|
||||
if (hexbuffer->read(f)) {
|
||||
auto hexbuffer = new QFileBuffer();
|
||||
if (f->open(readonly ? QStorageDevice::ReadOnly
|
||||
: QStorageDevice::ReadWrite) &&
|
||||
hexbuffer->read(f)) {
|
||||
return new QHexDocument(hexbuffer, readonly);
|
||||
} else {
|
||||
delete hexbuffer;
|
||||
|
@ -569,3 +595,5 @@ QHexDocument *QHexDocument::fromStorageDriver(const QStorageInfo &storage,
|
|||
return fromLargeFile(storage.device(), readonly);
|
||||
#endif
|
||||
}
|
||||
|
||||
QHexBuffer *QHexDocument::buffer() const { return m_buffer; }
|
||||
|
|
|
@ -9,16 +9,6 @@
|
|||
#include <QStorageInfo>
|
||||
#include <QUndoStack>
|
||||
|
||||
/*=========================*/
|
||||
// added by wingsummer
|
||||
|
||||
struct BookMarkStruct {
|
||||
qsizetype pos;
|
||||
QString comment;
|
||||
};
|
||||
|
||||
/*=========================*/
|
||||
|
||||
class QHexDocument : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -44,7 +34,7 @@ public:
|
|||
|
||||
void addUndoCommand(QUndoCommand *command);
|
||||
bool lineHasBookMark(qsizetype line);
|
||||
QList<qsizetype> getsBookmarkPos(qsizetype line);
|
||||
QList<qsizetype> getLineBookmarksPos(qsizetype line);
|
||||
|
||||
bool setLockedFile(bool b);
|
||||
bool setKeepSize(bool b);
|
||||
|
@ -54,28 +44,27 @@ public:
|
|||
|
||||
//----------------------------------
|
||||
bool AddBookMark(qsizetype pos, QString comment);
|
||||
bool RemoveBookMark(qsizetype index);
|
||||
bool RemoveBookMarks(QList<qsizetype> &pos);
|
||||
bool RemoveBookMark(qsizetype pos);
|
||||
bool RemoveBookMarks(const QList<qsizetype> &pos);
|
||||
bool ModBookMark(qsizetype pos, QString comment);
|
||||
bool ClearBookMark();
|
||||
//----------------------------------
|
||||
|
||||
bool addBookMark(qsizetype pos, QString comment);
|
||||
bool modBookMark(qsizetype pos, QString comment);
|
||||
bool modBookMark(qsizetype pos, const QString &comment);
|
||||
|
||||
BookMarkStruct bookMarkByIndex(qsizetype index);
|
||||
BookMarkStruct bookMark(qsizetype pos);
|
||||
QString bookMark(qsizetype pos);
|
||||
bool bookMarkExists(qsizetype pos);
|
||||
|
||||
// note: maybe changed when bookmarks are chaged
|
||||
qsizetype bookMarkPos(qsizetype index);
|
||||
|
||||
QString bookMarkComment(qsizetype pos);
|
||||
const QList<BookMarkStruct> &getAllBookMarks();
|
||||
qsizetype bookMarksCount() const;
|
||||
void applyBookMarks(const QList<BookMarkStruct> &books);
|
||||
bool removeBookMarkByIndex(qsizetype index);
|
||||
void applyBookMarks(const QMap<qsizetype, QString> &books);
|
||||
bool removeBookMark(qsizetype pos);
|
||||
bool clearBookMark();
|
||||
|
||||
QList<BookMarkStruct> *bookMarksPtr();
|
||||
const QList<BookMarkStruct> &bookMarks() const;
|
||||
const QMap<qsizetype, QString> &bookMarks() const;
|
||||
|
||||
bool existBookMark(qsizetype pos);
|
||||
|
||||
|
@ -98,10 +87,13 @@ public:
|
|||
bool metabgVisible();
|
||||
bool metaCommentVisible();
|
||||
|
||||
void insertBookMarkAdjust(qsizetype offset, qsizetype length);
|
||||
void removeBookMarkAdjust(qsizetype offset, qsizetype length);
|
||||
|
||||
/*======================*/
|
||||
|
||||
public:
|
||||
QByteArray read(qsizetype offset, qsizetype len = -1);
|
||||
QByteArray read(qsizetype offset, qsizetype len = -1) const;
|
||||
|
||||
char at(qsizetype offset) const;
|
||||
void SetBaseAddress(quintptr baseaddress);
|
||||
|
@ -112,6 +104,9 @@ public slots:
|
|||
void undo();
|
||||
void redo();
|
||||
|
||||
void beginMarco(const QString &text);
|
||||
void endMarco();
|
||||
|
||||
void Insert(QHexCursor *cursor, qsizetype offset, uchar b, int nibbleindex);
|
||||
void Insert(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
|
||||
int nibbleindex);
|
||||
|
@ -122,7 +117,6 @@ public slots:
|
|||
bool Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
|
||||
int nibbleindex = 0);
|
||||
|
||||
QByteArray read(qsizetype offset, qsizetype len) const;
|
||||
bool saveTo(QIODevice *device, bool cleanUndo);
|
||||
|
||||
// qsizetype searchForward(const QByteArray &ba);
|
||||
|
@ -140,6 +134,12 @@ public slots:
|
|||
bool replace(qsizetype offset, const QByteArray &data);
|
||||
bool remove(qsizetype offset, qsizetype len);
|
||||
|
||||
bool _insert(qsizetype offset, uchar b);
|
||||
bool _insert(qsizetype offset, const QByteArray &data);
|
||||
bool _replace(qsizetype offset, uchar b);
|
||||
bool _replace(qsizetype offset, const QByteArray &data);
|
||||
bool _remove(qsizetype offset, qsizetype len);
|
||||
|
||||
/*================================*/
|
||||
|
||||
/*================================*/
|
||||
|
@ -166,6 +166,8 @@ public:
|
|||
static QHexDocument *fromStorageDriver(const QStorageInfo &storage,
|
||||
bool readonly = false);
|
||||
|
||||
QHexBuffer *buffer() const;
|
||||
|
||||
signals:
|
||||
|
||||
/*================================*/
|
||||
|
@ -202,10 +204,12 @@ private:
|
|||
/*======================*/
|
||||
// added by wingsummer
|
||||
|
||||
bool m_isSaved = true;
|
||||
|
||||
bool m_readonly;
|
||||
bool m_keepsize;
|
||||
bool m_islocked;
|
||||
QList<BookMarkStruct> bookmarks;
|
||||
QMap<qsizetype, QString> _bookmarks;
|
||||
|
||||
bool m_metafg = true;
|
||||
bool m_metabg = true;
|
||||
|
|
|
@ -5,12 +5,23 @@
|
|||
#include "commands/meta/metaremoveposcommand.h"
|
||||
#include "commands/meta/metareplacecommand.h"
|
||||
|
||||
#include <QtAlgorithms>
|
||||
#include <QtConcurrent/QtConcurrentMap>
|
||||
|
||||
QHexMetadata::QHexMetadata(QUndoStack *undo, QObject *parent)
|
||||
: QObject(parent), m_undo(undo) {}
|
||||
|
||||
const QHexLineMetadata &QHexMetadata::get(qsizetype line) const {
|
||||
auto it = m_metadata.find(line);
|
||||
return it.value();
|
||||
QHexLineMetadata QHexMetadata::get(qsizetype line) const {
|
||||
if (!m_linemeta.contains(line)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QHexLineMetadata ret;
|
||||
for (auto &item : m_linemeta[line]) {
|
||||
ret.append(item);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*==================================*/
|
||||
|
@ -18,13 +29,12 @@ const QHexLineMetadata &QHexMetadata::get(qsizetype line) const {
|
|||
|
||||
//----------undo redo wrapper----------
|
||||
|
||||
void QHexMetadata::ModifyMetadata(QHexMetadataAbsoluteItem newmeta,
|
||||
QHexMetadataAbsoluteItem oldmeta) {
|
||||
void QHexMetadata::ModifyMetadata(QHexMetadataItem newmeta,
|
||||
QHexMetadataItem oldmeta) {
|
||||
m_undo->push(new MetaReplaceCommand(this, newmeta, oldmeta));
|
||||
}
|
||||
|
||||
void QHexMetadata::RemoveMetadatas(
|
||||
const QList<QHexMetadataAbsoluteItem> &items) {
|
||||
void QHexMetadata::RemoveMetadatas(const QList<QHexMetadataItem> &items) {
|
||||
m_undo->beginMacro("RemoveMetadatas");
|
||||
for (auto &item : items) {
|
||||
RemoveMetadata(item);
|
||||
|
@ -32,7 +42,7 @@ void QHexMetadata::RemoveMetadatas(
|
|||
m_undo->endMacro();
|
||||
}
|
||||
|
||||
void QHexMetadata::RemoveMetadata(QHexMetadataAbsoluteItem item) {
|
||||
void QHexMetadata::RemoveMetadata(QHexMetadataItem item) {
|
||||
m_undo->push(new MetaRemoveCommand(this, item));
|
||||
}
|
||||
|
||||
|
@ -43,81 +53,98 @@ void QHexMetadata::RemoveMetadata(qsizetype offset) {
|
|||
void QHexMetadata::Metadata(qsizetype begin, qsizetype end,
|
||||
const QColor &fgcolor, const QColor &bgcolor,
|
||||
const QString &comment) {
|
||||
QHexMetadataAbsoluteItem absi{begin, end, fgcolor, bgcolor, comment};
|
||||
QHexMetadataItem absi{begin, end, fgcolor, bgcolor, comment};
|
||||
m_undo->push(new MetaAddCommand(this, absi));
|
||||
}
|
||||
|
||||
void QHexMetadata::Clear() {
|
||||
m_undo->push(new MetaClearCommand(this, getallMetas()));
|
||||
m_undo->push(new MetaClearCommand(this, this->getAllMetadata()));
|
||||
}
|
||||
|
||||
//-------- the real function-----------
|
||||
void QHexMetadata::undo() { m_undo->undo(); }
|
||||
void QHexMetadata::redo() { m_undo->redo(); }
|
||||
bool QHexMetadata::canUndo() { return m_undo->canUndo(); }
|
||||
bool QHexMetadata::canRedo() { return m_undo->canRedo(); }
|
||||
|
||||
QList<QHexMetadataAbsoluteItem> QHexMetadata::getallMetas() {
|
||||
return m_absoluteMetadata;
|
||||
}
|
||||
|
||||
const QList<QHexMetadataAbsoluteItem> &QHexMetadata::getallMetasPtr() {
|
||||
return m_absoluteMetadata;
|
||||
}
|
||||
|
||||
void QHexMetadata::modifyMetadata(QHexMetadataAbsoluteItem newmeta,
|
||||
QHexMetadataAbsoluteItem oldmeta) {
|
||||
removeMetadata(oldmeta);
|
||||
metadata(newmeta.begin, newmeta.end, newmeta.foreground, newmeta.background,
|
||||
newmeta.comment);
|
||||
}
|
||||
|
||||
void QHexMetadata::removeMetadata(QHexMetadataAbsoluteItem item) {
|
||||
auto firstRow = item.begin / m_lineWidth;
|
||||
auto lastRow = item.end / m_lineWidth;
|
||||
|
||||
for (auto i = firstRow; i <= lastRow; i++) {
|
||||
QList<QHexMetadataItem> delmeta;
|
||||
auto it = m_metadata.find(i);
|
||||
if (it != m_metadata.end()) {
|
||||
for (auto iitem : *it) {
|
||||
if (iitem.foreground == item.foreground &&
|
||||
iitem.background == item.background &&
|
||||
iitem.comment == item.comment) {
|
||||
delmeta.push_back(iitem);
|
||||
}
|
||||
}
|
||||
for (auto iitem : delmeta) {
|
||||
it->remove(iitem);
|
||||
}
|
||||
m_absoluteMetadata.removeOne(item);
|
||||
}
|
||||
bool QHexMetadata::modifyMetadata(const QHexMetadataItem &newmeta,
|
||||
const QHexMetadataItem &oldmeta) {
|
||||
if (removeMetadata(oldmeta)) {
|
||||
metadata(newmeta.begin, newmeta.end, newmeta.foreground,
|
||||
newmeta.background, newmeta.comment);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHexMetadata::removeMetadata(const QHexMetadataItem &item) {
|
||||
auto index = m_metadata.indexOf(item);
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
m_metadata.removeAt(index);
|
||||
for (auto &l : m_linemeta) {
|
||||
l.remove(item);
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
m_linemeta.erase(std::remove_if(
|
||||
m_linemeta.begin(), m_linemeta.end(),
|
||||
[](const QHash<QHexMetadataItem, QHexLineMetadata> &item) {
|
||||
return item.isEmpty();
|
||||
}));
|
||||
#else
|
||||
m_linemeta.removeIf(
|
||||
[](const QPair<qsizetype, QHash<QHexMetadataItem, QHexLineMetadata>>
|
||||
&item) { return item.second.isEmpty(); });
|
||||
#endif
|
||||
|
||||
emit metadataChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QHexMetadata::removeMetadata(qsizetype offset) {
|
||||
auto rmfn = [offset, this](const QHexMetadataItem &item) {
|
||||
auto r = offset >= item.begin && offset <= item.end;
|
||||
if (r) {
|
||||
for (auto &l : m_linemeta) {
|
||||
l.remove(item);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
m_metadata.erase(
|
||||
std::remove_if(m_metadata.begin(), m_metadata.end(), rmfn));
|
||||
#else
|
||||
m_metadata.removeIf(rmfn);
|
||||
#endif
|
||||
|
||||
emit metadataChanged();
|
||||
}
|
||||
|
||||
void QHexMetadata::removeMetadata(qsizetype offset) {
|
||||
QList<QHexMetadataAbsoluteItem> delneeded;
|
||||
for (auto item : m_absoluteMetadata) {
|
||||
if (offset >= item.begin && offset <= item.end) {
|
||||
removeMetadata(item);
|
||||
}
|
||||
}
|
||||
QVector<QHexMetadataItem> QHexMetadata::getAllMetadata() const {
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
QList<QHexMetadataAbsoluteItem> QHexMetadata::gets(qsizetype offset) {
|
||||
return m_absoluteMetadata;
|
||||
QVector<QHexMetadataItem> QHexMetadata::gets(qsizetype offset) {
|
||||
QVector<QHexMetadataItem> ret;
|
||||
|
||||
std::copy_if(
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
m_metadata.begin(), m_metadata.end(),
|
||||
#else
|
||||
m_metadata.constBegin(), m_metadata.constEnd(),
|
||||
#endif
|
||||
std::back_inserter(ret), [offset](const QHexMetadataItem &item) {
|
||||
return offset >= item.begin && offset <= item.end;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void QHexMetadata::applyMetas(QList<QHexMetadataAbsoluteItem> metas) {
|
||||
for (auto item : metas) {
|
||||
metadata(item.begin, item.end, item.foreground, item.background,
|
||||
item.comment);
|
||||
}
|
||||
void QHexMetadata::applyMetas(const QVector<QHexMetadataItem> &metas) {
|
||||
m_metadata = metas;
|
||||
}
|
||||
|
||||
bool QHexMetadata::hasMetadata() { return m_absoluteMetadata.count() > 0; }
|
||||
bool QHexMetadata::hasMetadata() { return m_metadata.count() > 0; }
|
||||
|
||||
/*==================================*/
|
||||
|
||||
|
@ -145,41 +172,148 @@ QString QHexMetadata::comments(qsizetype line, qsizetype column) const {
|
|||
}
|
||||
|
||||
bool QHexMetadata::lineHasMetadata(qsizetype line) const {
|
||||
return m_metadata.contains(line);
|
||||
return m_linemeta.contains(line);
|
||||
}
|
||||
|
||||
qsizetype QHexMetadata::size() const { return m_absoluteMetadata.size(); }
|
||||
qsizetype QHexMetadata::size() const { return m_metadata.size(); }
|
||||
|
||||
void QHexMetadata::beginMarco(const QString &text) { m_undo->beginMacro(text); }
|
||||
|
||||
void QHexMetadata::endMarco() { m_undo->endMacro(); }
|
||||
|
||||
void QHexMetadata::clear() {
|
||||
m_absoluteMetadata.clear();
|
||||
m_linemeta.clear();
|
||||
m_metadata.clear();
|
||||
emit metadataChanged();
|
||||
}
|
||||
|
||||
void QHexMetadata::metadata(qsizetype begin, qsizetype end,
|
||||
bool QHexMetadata::metadata(qsizetype begin, qsizetype end,
|
||||
const QColor &fgcolor, const QColor &bgcolor,
|
||||
const QString &comment) {
|
||||
QHexMetadataAbsoluteItem absi{begin, end, fgcolor, bgcolor, comment};
|
||||
m_absoluteMetadata.append(absi);
|
||||
setAbsoluteMetadata(absi);
|
||||
if (begin > end)
|
||||
return false;
|
||||
|
||||
if (!fgcolor.isValid() || fgcolor.alpha() == 0) {
|
||||
if (!bgcolor.isValid() || bgcolor.alpha() == 0) {
|
||||
if (comment.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QHexMetadataItem absi{begin, end, fgcolor, bgcolor, comment};
|
||||
addMetadata(absi);
|
||||
emit metadataChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QHexMetadata::setAbsoluteMetadata(const QHexMetadataAbsoluteItem &mai) {
|
||||
Q_ASSERT(m_lineWidth > 0);
|
||||
void QHexMetadata::setLineWidth(quint8 width) {
|
||||
if (width != m_lineWidth) {
|
||||
m_lineWidth = width;
|
||||
|
||||
const auto firstRow = mai.begin / m_lineWidth;
|
||||
const auto lastRow = mai.end / m_lineWidth;
|
||||
m_linemeta.clear();
|
||||
for (auto &item : m_metadata) {
|
||||
addMetadata(item);
|
||||
}
|
||||
|
||||
emit metadataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QHexMetadata::insertAdjust(qsizetype offset, qsizetype length) {
|
||||
m_linemeta.clear();
|
||||
|
||||
QtConcurrent::blockingMap(
|
||||
m_metadata, [offset, length](QHexMetadataItem &meta) {
|
||||
if (meta.end < offset) {
|
||||
return;
|
||||
}
|
||||
if (meta.begin <= offset && meta.end > offset) {
|
||||
meta.end += length;
|
||||
} else {
|
||||
meta.begin += length;
|
||||
meta.end += length;
|
||||
}
|
||||
});
|
||||
|
||||
for (auto &meta : m_metadata) {
|
||||
addMetaLines(meta);
|
||||
}
|
||||
}
|
||||
|
||||
void QHexMetadata::removeAdjust(qsizetype offset, qsizetype length) {
|
||||
m_linemeta.clear();
|
||||
|
||||
QtConcurrent::blockingMap(
|
||||
m_metadata, [offset, length](QHexMetadataItem &meta) {
|
||||
if (meta.end < offset) {
|
||||
return;
|
||||
}
|
||||
if (meta.begin <= offset && meta.end > offset) {
|
||||
meta.end -= length;
|
||||
if (meta.begin > meta.end) {
|
||||
meta.flag = true;
|
||||
}
|
||||
} else {
|
||||
meta.begin -= length;
|
||||
meta.end -= length;
|
||||
}
|
||||
});
|
||||
|
||||
auto rmfn = [](const QHexMetadataItem &meta) { return meta.flag; };
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
m_metadata.erase(
|
||||
std::remove_if(m_metadata.begin(), m_metadata.end(), rmfn));
|
||||
#else
|
||||
m_metadata.removeIf(rmfn);
|
||||
#endif
|
||||
|
||||
for (auto &meta : m_metadata) {
|
||||
addMetaLines(meta);
|
||||
}
|
||||
}
|
||||
|
||||
bool QHexMetadata::color(qsizetype begin, qsizetype end, const QColor &fgcolor,
|
||||
const QColor &bgcolor) {
|
||||
return this->metadata(begin, end, fgcolor, bgcolor, QString());
|
||||
}
|
||||
|
||||
bool QHexMetadata::foreground(qsizetype begin, qsizetype end,
|
||||
const QColor &fgcolor) {
|
||||
return this->color(begin, end, fgcolor, QColor());
|
||||
}
|
||||
|
||||
bool QHexMetadata::background(qsizetype begin, qsizetype end,
|
||||
const QColor &bgcolor) {
|
||||
return this->color(begin, end, QColor(), bgcolor);
|
||||
}
|
||||
|
||||
bool QHexMetadata::comment(qsizetype begin, qsizetype end,
|
||||
const QString &comment) {
|
||||
return this->metadata(begin, end, QColor(), QColor(), comment);
|
||||
}
|
||||
|
||||
void QHexMetadata::addMetadata(const QHexMetadataItem &mi) {
|
||||
addMetaLines(mi);
|
||||
m_metadata << mi;
|
||||
}
|
||||
|
||||
void QHexMetadata::addMetaLines(const QHexMetadataItem &mi) {
|
||||
const auto firstRow = mi.begin / m_lineWidth;
|
||||
const auto lastRow = mi.end / m_lineWidth;
|
||||
|
||||
for (auto row = firstRow; row <= lastRow; ++row) {
|
||||
qsizetype start, length;
|
||||
if (row == firstRow) {
|
||||
start = mai.begin % m_lineWidth;
|
||||
Q_ASSERT(m_lineWidth > 0);
|
||||
start = mi.begin % m_lineWidth;
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
if (row == lastRow) {
|
||||
const int lastChar = mai.end % m_lineWidth;
|
||||
Q_ASSERT(m_lineWidth > 0);
|
||||
const int lastChar = mi.end % m_lineWidth;
|
||||
length = lastChar - start;
|
||||
} else {
|
||||
// fix the bug by wingsummer
|
||||
|
@ -190,69 +324,8 @@ void QHexMetadata::setAbsoluteMetadata(const QHexMetadataAbsoluteItem &mai) {
|
|||
}
|
||||
|
||||
if (length > 0) {
|
||||
setMetadata({row, start, length, mai.foreground, mai.background,
|
||||
mai.comment});
|
||||
m_linemeta[row][mi].append(
|
||||
{start, length, mi.foreground, mi.background, mi.comment});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QHexMetadata::setLineWidth(quint8 width) {
|
||||
if (width != m_lineWidth) {
|
||||
m_lineWidth = width;
|
||||
// clean m_metadata
|
||||
m_metadata.clear();
|
||||
// and regenerate with new line width size
|
||||
for (int i = 0; i < m_absoluteMetadata.size(); ++i) {
|
||||
setAbsoluteMetadata(m_absoluteMetadata[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QHexMetadata::metadata(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &fgcolor, const QColor &bgcolor,
|
||||
const QString &comment) {
|
||||
const qsizetype begin = qsizetype(line * m_lineWidth + uint(start));
|
||||
const qsizetype end = begin + length;
|
||||
// delegate to the new interface
|
||||
this->metadata(begin, end, fgcolor, bgcolor, comment);
|
||||
emit metadataChanged();
|
||||
}
|
||||
|
||||
void QHexMetadata::color(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &fgcolor, const QColor &bgcolor) {
|
||||
this->metadata(line, start, length, fgcolor, bgcolor, QString());
|
||||
}
|
||||
|
||||
void QHexMetadata::foreground(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &fgcolor) {
|
||||
this->color(line, start, length, fgcolor, QColor());
|
||||
}
|
||||
|
||||
void QHexMetadata::background(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &bgcolor) {
|
||||
this->color(line, start, length, QColor(), bgcolor);
|
||||
}
|
||||
|
||||
void QHexMetadata::comment(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QString &comment) {
|
||||
this->metadata(line, start, length, QColor(), QColor(), comment);
|
||||
}
|
||||
|
||||
void QHexMetadata::setMetadata(const QHexMetadataItem &mi) {
|
||||
if (!m_metadata.contains(mi.line)) {
|
||||
QHexLineMetadata linemetadata;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
linemetadata << mi;
|
||||
#else
|
||||
linemetadata.push_back(mi);
|
||||
#endif
|
||||
m_metadata[mi.line] = linemetadata;
|
||||
} else {
|
||||
QHexLineMetadata &linemetadata = m_metadata[mi.line];
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
linemetadata << mi;
|
||||
#else
|
||||
linemetadata.push_back(mi);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,55 +2,82 @@
|
|||
#define QHEXMETADATA_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
#include <QLinkedList>
|
||||
#else
|
||||
#include <list>
|
||||
#endif
|
||||
|
||||
#include <QColor>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QUndoStack>
|
||||
#include <QVector>
|
||||
|
||||
struct QHexMetadataAbsoluteItem {
|
||||
qsizetype begin;
|
||||
qsizetype end;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
using qhash_result_t = uint;
|
||||
|
||||
// copying from QT6 source code for supporting QT5's qHashMulti
|
||||
namespace QtPrivate {
|
||||
template <typename T>
|
||||
inline constexpr bool
|
||||
QNothrowHashableHelper_v = noexcept(qHash(std::declval<const T &>()));
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct QNothrowHashable : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct QNothrowHashable<T, std::enable_if_t<QNothrowHashableHelper_v<T>>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool QNothrowHashable_v = QNothrowHashable<T>::value;
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
template <typename... T>
|
||||
constexpr qhash_result_t
|
||||
qHashMulti(qhash_result_t seed, const T &...args) noexcept(
|
||||
std::conjunction_v<QtPrivate::QNothrowHashable<T>...>) {
|
||||
QtPrivate::QHashCombine hash;
|
||||
return ((seed = hash(seed, args)), ...), seed;
|
||||
}
|
||||
#else
|
||||
using qhash_result_t = size_t;
|
||||
#endif
|
||||
|
||||
struct QHexMetadataItem {
|
||||
qsizetype begin = -1;
|
||||
qsizetype end = -1;
|
||||
QColor foreground, background;
|
||||
QString comment;
|
||||
bool flag = false;
|
||||
|
||||
// added by wingsummer
|
||||
bool operator==(const QHexMetadataAbsoluteItem &item) const {
|
||||
bool operator==(const QHexMetadataItem &item) const {
|
||||
return begin == item.begin && end == item.end &&
|
||||
foreground == item.foreground && background == item.background &&
|
||||
comment == item.comment;
|
||||
}
|
||||
};
|
||||
|
||||
struct QHexMetadataItem {
|
||||
qsizetype line;
|
||||
qsizetype start, length;
|
||||
inline qhash_result_t qHash(const QHexMetadataItem &c,
|
||||
qhash_result_t seed) noexcept {
|
||||
return qHashMulti(seed, c.begin, c.end, c.foreground.rgba(),
|
||||
c.background.rgba(), c.comment);
|
||||
}
|
||||
|
||||
// only for rendering
|
||||
struct QHexLineMetadataItem {
|
||||
qsizetype start = -1, length = 0;
|
||||
QColor foreground, background;
|
||||
QString comment;
|
||||
|
||||
// added by wingsummer
|
||||
bool operator==(const QHexMetadataItem &item) const {
|
||||
return line == item.line && start == item.start &&
|
||||
foreground == item.foreground && background == item.background &&
|
||||
comment == item.comment;
|
||||
}
|
||||
QHexMetadataItem *parent = nullptr;
|
||||
};
|
||||
|
||||
typedef std::list<QHexMetadataItem> QHexLineMetadata;
|
||||
typedef QList<QHexLineMetadataItem> QHexLineMetadata;
|
||||
|
||||
class QHexMetadata : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QHexMetadata(QUndoStack *undo, QObject *parent = nullptr);
|
||||
const QHexLineMetadata &get(qsizetype line) const;
|
||||
QHexLineMetadata get(qsizetype line) const;
|
||||
QString comments(qsizetype line, qsizetype column) const;
|
||||
bool lineHasMetadata(qsizetype line) const; // modified by wingsummer
|
||||
|
||||
|
@ -59,10 +86,12 @@ public:
|
|||
/*============================*/
|
||||
// added by wingsummer
|
||||
|
||||
void ModifyMetadata(QHexMetadataAbsoluteItem newmeta,
|
||||
QHexMetadataAbsoluteItem oldmeta);
|
||||
void RemoveMetadatas(const QList<QHexMetadataAbsoluteItem> &items);
|
||||
void RemoveMetadata(QHexMetadataAbsoluteItem item);
|
||||
void beginMarco(const QString &text);
|
||||
void endMarco();
|
||||
|
||||
void ModifyMetadata(QHexMetadataItem newmeta, QHexMetadataItem oldmeta);
|
||||
void RemoveMetadatas(const QList<QHexMetadataItem> &items);
|
||||
void RemoveMetadata(QHexMetadataItem item);
|
||||
void RemoveMetadata(qsizetype offset);
|
||||
void Metadata(qsizetype begin, qsizetype end, const QColor &fgcolor,
|
||||
const QColor &bgcolor, const QString &comment);
|
||||
|
@ -70,17 +99,15 @@ public:
|
|||
|
||||
//---------------------------------------------------------
|
||||
|
||||
void modifyMetadata(QHexMetadataAbsoluteItem newmeta,
|
||||
QHexMetadataAbsoluteItem oldmeta);
|
||||
void removeMetadata(QHexMetadataAbsoluteItem item);
|
||||
bool modifyMetadata(const QHexMetadataItem &newmeta,
|
||||
const QHexMetadataItem &oldmeta);
|
||||
bool removeMetadata(const QHexMetadataItem &item);
|
||||
void removeMetadata(qsizetype offset);
|
||||
QList<QHexMetadataAbsoluteItem> gets(qsizetype offset);
|
||||
void applyMetas(QList<QHexMetadataAbsoluteItem> metas);
|
||||
|
||||
void redo();
|
||||
void undo();
|
||||
bool canRedo();
|
||||
bool canUndo();
|
||||
QVector<QHexMetadataItem> getAllMetadata() const;
|
||||
QVector<QHexMetadataItem> gets(qsizetype offset);
|
||||
void applyMetas(const QVector<QHexMetadataItem> &metas);
|
||||
|
||||
bool hasMetadata();
|
||||
|
||||
/*============================*/
|
||||
|
@ -88,44 +115,35 @@ public:
|
|||
void clear();
|
||||
void setLineWidth(quint8 width);
|
||||
|
||||
void insertAdjust(qsizetype offset, qsizetype length);
|
||||
void removeAdjust(qsizetype offset, qsizetype length);
|
||||
|
||||
public:
|
||||
// new interface with begin, end
|
||||
void metadata(qsizetype begin, qsizetype end, const QColor &fgcolor,
|
||||
bool metadata(qsizetype begin, qsizetype end, const QColor &fgcolor,
|
||||
const QColor &bgcolor, const QString &comment);
|
||||
|
||||
// old interface with line, start, length
|
||||
void metadata(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &fgcolor, const QColor &bgcolor,
|
||||
const QString &comment);
|
||||
|
||||
void color(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &fgcolor, const QColor &bgcolor);
|
||||
void foreground(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &fgcolor);
|
||||
void background(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QColor &bgcolor);
|
||||
void comment(qsizetype line, qsizetype start, qsizetype length,
|
||||
const QString &comment);
|
||||
|
||||
QList<QHexMetadataAbsoluteItem>
|
||||
getallMetas(); // added by wingsummer to support workspace
|
||||
|
||||
const QList<QHexMetadataAbsoluteItem> &
|
||||
getallMetasPtr(); // added by wingsummer to support workspace
|
||||
bool color(qsizetype begin, qsizetype end, const QColor &fgcolor,
|
||||
const QColor &bgcolor);
|
||||
bool foreground(qsizetype begin, qsizetype end, const QColor &fgcolor);
|
||||
bool background(qsizetype begin, qsizetype end, const QColor &bgcolor);
|
||||
bool comment(qsizetype begin, qsizetype end, const QString &comment);
|
||||
|
||||
private:
|
||||
void setMetadata(const QHexMetadataItem &mi);
|
||||
void setAbsoluteMetadata(const QHexMetadataAbsoluteItem &mi);
|
||||
void addMetadata(const QHexMetadataItem &mi);
|
||||
|
||||
void addMetaLines(const QHexMetadataItem &mi);
|
||||
|
||||
signals:
|
||||
void metadataChanged();
|
||||
|
||||
private:
|
||||
quint8 m_lineWidth;
|
||||
QHash<qsizetype, QHexLineMetadata> m_metadata;
|
||||
QList<QHexMetadataAbsoluteItem> m_absoluteMetadata;
|
||||
|
||||
QUndoStack *m_undo; // added by wingsummer
|
||||
QMap<qsizetype, QHash<QHexMetadataItem, QHexLineMetadata>> m_linemeta;
|
||||
QVector<QHexMetadataItem> m_metadata;
|
||||
|
||||
QUndoStack *m_undo = nullptr; // added by wingsummer
|
||||
};
|
||||
|
||||
#endif // QHEXMETADATA_H
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <QTextCursor>
|
||||
#include <QWidget>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cwctype>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
|
@ -23,21 +22,21 @@ bool QHexRenderer::stringVisible() { return m_asciiVisible; }
|
|||
|
||||
void QHexRenderer::setStringVisible(bool b) {
|
||||
m_asciiVisible = b;
|
||||
m_document->documentChanged();
|
||||
emit m_document->documentChanged();
|
||||
}
|
||||
|
||||
bool QHexRenderer::headerVisible() { return m_headerVisible; }
|
||||
|
||||
void QHexRenderer::setHeaderVisible(bool b) {
|
||||
m_headerVisible = b;
|
||||
m_document->documentChanged();
|
||||
emit m_document->documentChanged();
|
||||
}
|
||||
|
||||
bool QHexRenderer::addressVisible() { return m_addressVisible; }
|
||||
|
||||
void QHexRenderer::setAddressVisible(bool b) {
|
||||
m_addressVisible = b;
|
||||
m_document->documentChanged();
|
||||
emit m_document->documentChanged();
|
||||
}
|
||||
|
||||
QString QHexRenderer::encoding() {
|
||||
|
@ -66,7 +65,7 @@ bool QHexRenderer::setEncoding(const QString &encoding) {
|
|||
if (encoding.compare(QStringLiteral("ISO-8859-1"), Qt::CaseInsensitive) ==
|
||||
0) {
|
||||
m_encoding = QStringLiteral("ASCII");
|
||||
m_document->documentChanged();
|
||||
emit m_document->documentChanged();
|
||||
return true;
|
||||
}
|
||||
if (QStringConverter::encodingForName(enc.toUtf8())) {
|
||||
|
@ -74,7 +73,7 @@ bool QHexRenderer::setEncoding(const QString &encoding) {
|
|||
if (QTextCodec::codecForName(encoding.toUtf8())) {
|
||||
#endif
|
||||
m_encoding = encoding;
|
||||
m_document->documentChanged();
|
||||
emit m_document->documentChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -406,7 +405,7 @@ void QHexRenderer::applyMetadata(QTextCursor &textcursor, qsizetype line,
|
|||
return;
|
||||
|
||||
const QHexLineMetadata &linemetadata = metadata->get(line);
|
||||
for (const QHexMetadataItem &mi : linemetadata) {
|
||||
for (auto &mi : linemetadata) {
|
||||
QTextCharFormat charformat;
|
||||
if (m_document->metabgVisible() && mi.background.isValid() &&
|
||||
mi.background.rgba())
|
||||
|
@ -430,8 +429,31 @@ void QHexRenderer::applySelection(QTextCursor &textcursor, qsizetype line,
|
|||
if (!m_cursor->isLineSelected(line))
|
||||
return;
|
||||
|
||||
const QHexPosition &startsel = m_cursor->selectionStart();
|
||||
const QHexPosition &endsel = m_cursor->selectionEnd();
|
||||
auto total = m_cursor->selectionCount();
|
||||
for (int i = 0; i < total; ++i) {
|
||||
applySelection(m_cursor->selection(i), textcursor, line, factor, false,
|
||||
false);
|
||||
}
|
||||
|
||||
if (m_cursor->hasPreviewSelection()) {
|
||||
applySelection(
|
||||
m_cursor->previewSelection().normalized(), textcursor, line, factor,
|
||||
m_cursor->previewSelectionMode() == QHexCursor::SelectionRemove,
|
||||
m_cursor->previewSelectionMode() == QHexCursor::SelectionNormal &&
|
||||
m_cursor->hasInternalSelection());
|
||||
}
|
||||
}
|
||||
|
||||
void QHexRenderer::applySelection(const QHexSelection &selection,
|
||||
QTextCursor &textcursor, qsizetype line,
|
||||
Factor factor, bool strikeOut,
|
||||
bool hasSelection) const {
|
||||
if (!selection.isLineSelected(line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QHexPosition &startsel = selection.start;
|
||||
const QHexPosition &endsel = selection.end;
|
||||
|
||||
if (startsel.line == endsel.line) {
|
||||
textcursor.setPosition(startsel.column * factor);
|
||||
|
@ -456,8 +478,13 @@ void QHexRenderer::applySelection(QTextCursor &textcursor, qsizetype line,
|
|||
textcursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
|
||||
|
||||
QTextCharFormat charformat;
|
||||
charformat.setBackground(m_selBackgroundColor);
|
||||
charformat.setForeground(m_selectionColor);
|
||||
charformat.setBackground(strikeOut || hasSelection
|
||||
? m_selBackgroundColor.darker()
|
||||
: m_selBackgroundColor);
|
||||
charformat.setForeground(strikeOut ? m_selectionColor.darker()
|
||||
: m_selectionColor);
|
||||
charformat.setFontStrikeOut(strikeOut);
|
||||
charformat.setFontItalic(strikeOut);
|
||||
textcursor.mergeCharFormat(charformat);
|
||||
}
|
||||
|
||||
|
@ -544,7 +571,7 @@ void QHexRenderer::drawHex(QPainter *painter, const QRect &linerect,
|
|||
textcursor.insertText(this->hexString(line, &rawline));
|
||||
|
||||
if (line == this->documentLastLine())
|
||||
textcursor.insertText(" ");
|
||||
textcursor.insertText(QStringLiteral(" "));
|
||||
|
||||
QRect hexrect = linerect;
|
||||
hexrect.setX(this->getHexColumnX() + this->borderSize());
|
||||
|
@ -577,7 +604,7 @@ void QHexRenderer::applyBookMark(QTextCursor &textcursor, qsizetype line,
|
|||
if (!m_document->lineHasBookMark(line))
|
||||
return;
|
||||
|
||||
auto pos = m_document->getsBookmarkPos(line);
|
||||
auto pos = m_document->getLineBookmarksPos(line);
|
||||
for (auto item : pos) {
|
||||
textcursor.setPosition(int((item % hexLineWidth()) * factor) + 2);
|
||||
auto charformat = textcursor.charFormat();
|
||||
|
|
|
@ -120,8 +120,13 @@ private:
|
|||
Factor factor) const;
|
||||
void applyMetadata(QTextCursor &textcursor, qsizetype line,
|
||||
Factor factor) const;
|
||||
|
||||
void applySelection(QTextCursor &textcursor, qsizetype line,
|
||||
Factor factor) const;
|
||||
void applySelection(const QHexSelection &selection, QTextCursor &textcursor,
|
||||
qsizetype line, Factor factor, bool strikeOut,
|
||||
bool hasSelection) const;
|
||||
|
||||
void applyBookMark(QTextCursor &textcursor, qsizetype line,
|
||||
Factor factor); // added by wingsummer
|
||||
void applyCursorAscii(QTextCursor &textcursor, qsizetype line) const;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#include "qstoragedevice.h"
|
||||
#include "qstorageinfo.h"
|
||||
|
||||
QStorageDevice::QStorageDevice() : QIODevice() {}
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
QStorageDevice::QStorageDevice(QObject *parent)
|
||||
: QIODevice(parent), hDevice(INVALID_HANDLE_VALUE), CHUNK_SIZE(0),
|
||||
_size(0) {}
|
||||
|
||||
void QStorageDevice::setStorage(const QStorageInfo &storage) {
|
||||
_storage = storage;
|
||||
|
@ -9,6 +14,236 @@ void QStorageDevice::setStorage(const QStorageInfo &storage) {
|
|||
|
||||
QStorageInfo QStorageDevice::storage() const { return _storage; }
|
||||
|
||||
qint64 QStorageDevice::readData(char *data, qint64 maxlen) { return -1; }
|
||||
DWORD QStorageDevice::cacheSize() const { return 20 * 1024 * CHUNK_SIZE; }
|
||||
|
||||
qint64 QStorageDevice::writeData(const char *data, qint64 len) { return -1; }
|
||||
bool QStorageDevice::isSequential() const { return false; }
|
||||
|
||||
bool QStorageDevice::open(OpenMode mode) {
|
||||
if (hDevice != INVALID_HANDLE_VALUE) {
|
||||
setErrorString(QStringLiteral("A Storage file is still opened"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode == OpenModeFlag::ReadOnly || mode == OpenModeFlag::WriteOnly ||
|
||||
mode == OpenModeFlag::ReadWrite) {
|
||||
auto device = _storage.device();
|
||||
auto devicePrefix = QStringLiteral("\\\\.\\");
|
||||
QString dd = devicePrefix +
|
||||
device.mid(devicePrefix.length(),
|
||||
device.length() - devicePrefix.length() - 1);
|
||||
|
||||
DWORD flag =
|
||||
(mode.testFlag(OpenModeFlag::ReadOnly) ? GENERIC_READ : 0) |
|
||||
(mode.testFlag(OpenModeFlag::WriteOnly) ? GENERIC_WRITE : 0);
|
||||
|
||||
// Open the physical drive using WinAPI
|
||||
hDevice = CreateFileW(
|
||||
reinterpret_cast<LPCWSTR>(dd.utf16()), flag,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, nullptr);
|
||||
|
||||
if (hDevice == INVALID_HANDLE_VALUE) {
|
||||
qWarning() << "Failed to open device:" << device;
|
||||
return false;
|
||||
}
|
||||
|
||||
DISK_GEOMETRY diskGeometry;
|
||||
DWORD bytesReturned;
|
||||
if (!DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, nullptr, 0,
|
||||
&diskGeometry, sizeof(diskGeometry),
|
||||
&bytesReturned, nullptr)) {
|
||||
CloseHandle(hDevice);
|
||||
hDevice = INVALID_HANDLE_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
this->CHUNK_SIZE = diskGeometry.BytesPerSector;
|
||||
|
||||
_cache.buffer = std::make_unique<char[]>(cacheSize());
|
||||
_cache.length = 0;
|
||||
|
||||
// dont use ioDevice.bytesTotal(),
|
||||
// because it's use GetDiskFreeSpaceEx API to get.
|
||||
// QFile::size() is zero
|
||||
_size = diskGeometry.Cylinders.QuadPart *
|
||||
diskGeometry.TracksPerCylinder * diskGeometry.SectorsPerTrack *
|
||||
diskGeometry.BytesPerSector;
|
||||
|
||||
return QIODevice::open(mode);
|
||||
} else {
|
||||
qWarning() << "Only OpenModeFlag::ReadOnly and OpenModeFlag::WriteOnly "
|
||||
"are supported";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void QStorageDevice::close() {
|
||||
if (hDevice != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hDevice);
|
||||
hDevice = INVALID_HANDLE_VALUE;
|
||||
_cache.clear();
|
||||
}
|
||||
QIODevice::close();
|
||||
}
|
||||
|
||||
qint64 QStorageDevice::size() const { return _size; }
|
||||
|
||||
bool QStorageDevice::seek(qint64 pos) {
|
||||
if (hDevice == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return QIODevice::seek(pos);
|
||||
}
|
||||
|
||||
bool QStorageDevice::canReadLine() const { return false; }
|
||||
|
||||
qint64 QStorageDevice::readData(char *data, qint64 maxlen) {
|
||||
if (hDevice == INVALID_HANDLE_VALUE || !maxlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (maxlen > std::numeric_limits<DWORD>::max() ||
|
||||
(maxlen < 0 && _size > 1024 * 1024 * 1024)) {
|
||||
qWarning() << "Read a lot mount of data once is not allowed";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (maxlen < 0) {
|
||||
maxlen = _size;
|
||||
}
|
||||
|
||||
auto rp = std::div(this->pos(), CHUNK_SIZE);
|
||||
auto off = rp.quot * CHUNK_SIZE;
|
||||
|
||||
if (_cache.offset < 0 || this->pos() < _cache.offset ||
|
||||
this->pos() + maxlen >= _cache.offset + _cache.length) {
|
||||
OVERLAPPED overlapped{0};
|
||||
LARGE_INTEGER offset;
|
||||
offset.QuadPart = off;
|
||||
overlapped.Offset = offset.LowPart;
|
||||
overlapped.OffsetHigh = offset.HighPart;
|
||||
|
||||
if (!ReadFile(hDevice, _cache.buffer.get(), cacheSize(), nullptr,
|
||||
&overlapped)) {
|
||||
auto lastError = GetLastError();
|
||||
if (lastError == ERROR_IO_PENDING) {
|
||||
if (!GetOverlappedResult(hDevice, &overlapped, &_cache.length,
|
||||
TRUE)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
_cache.offset = off;
|
||||
}
|
||||
|
||||
std::memcpy(data, _cache.buffer.get() + this->pos() - _cache.offset,
|
||||
maxlen);
|
||||
|
||||
return maxlen;
|
||||
}
|
||||
|
||||
qint64 QStorageDevice::writeData(const char *data, qint64 len) {
|
||||
// qt will check writeable attr
|
||||
if (!isOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure maxSize is a multiple of the sector size
|
||||
auto rp = std::div(this->pos(), CHUNK_SIZE);
|
||||
auto header = CHUNK_SIZE - rp.rem;
|
||||
auto r = std::div(len - header, CHUNK_SIZE);
|
||||
auto alignLen = r.quot * CHUNK_SIZE;
|
||||
|
||||
OVERLAPPED overlapped{0};
|
||||
LARGE_INTEGER offset;
|
||||
DWORD length = 0;
|
||||
|
||||
if (rp.rem) {
|
||||
// read some and write back
|
||||
offset.QuadPart = rp.quot * CHUNK_SIZE;
|
||||
|
||||
auto buffer = std::make_unique<char[]>(CHUNK_SIZE);
|
||||
|
||||
if (!ReadFile(hDevice, buffer.get(), CHUNK_SIZE, nullptr,
|
||||
&overlapped)) {
|
||||
auto lastError = GetLastError();
|
||||
if (lastError == ERROR_IO_PENDING) {
|
||||
if (!GetOverlappedResult(hDevice, &overlapped, &length, TRUE)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(buffer.get(), data, CHUNK_SIZE - rp.rem);
|
||||
|
||||
if (!WriteFile(hDevice, buffer.get(), CHUNK_SIZE, nullptr,
|
||||
&overlapped)) {
|
||||
auto lastError = GetLastError();
|
||||
if (lastError == ERROR_IO_PENDING) {
|
||||
if (!GetOverlappedResult(hDevice, &overlapped, &length, TRUE)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset.QuadPart += CHUNK_SIZE;
|
||||
overlapped.Offset = offset.LowPart;
|
||||
overlapped.OffsetHigh = offset.HighPart;
|
||||
|
||||
// write aligned
|
||||
if (!WriteFile(hDevice, data - header, alignLen, nullptr, &overlapped)) {
|
||||
auto lastError = GetLastError();
|
||||
if (lastError == ERROR_IO_PENDING) {
|
||||
if (!GetOverlappedResult(hDevice, &overlapped, &length, TRUE)) {
|
||||
return header;
|
||||
}
|
||||
} else {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
if (r.rem) {
|
||||
offset.QuadPart += alignLen;
|
||||
overlapped.Offset = offset.LowPart;
|
||||
overlapped.OffsetHigh = offset.HighPart;
|
||||
|
||||
auto buffer = std::make_unique<char[]>(CHUNK_SIZE);
|
||||
if (!ReadFile(hDevice, buffer.get(), CHUNK_SIZE, nullptr,
|
||||
&overlapped)) {
|
||||
auto lastError = GetLastError();
|
||||
if (lastError == ERROR_IO_PENDING) {
|
||||
if (!GetOverlappedResult(hDevice, &overlapped, &length, TRUE)) {
|
||||
return header + alignLen;
|
||||
}
|
||||
} else {
|
||||
return header + alignLen;
|
||||
}
|
||||
}
|
||||
std::memcpy(buffer.get(), data + len - r.rem, r.rem);
|
||||
|
||||
if (!WriteFile(hDevice, buffer.get(), CHUNK_SIZE, nullptr,
|
||||
&overlapped)) {
|
||||
auto lastError = GetLastError();
|
||||
if (lastError == ERROR_IO_PENDING) {
|
||||
if (!GetOverlappedResult(hDevice, &overlapped, &length, TRUE)) {
|
||||
return header + alignLen;
|
||||
}
|
||||
} else {
|
||||
return header + alignLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,25 +1,60 @@
|
|||
#ifndef QSTORAGEDEVICE_H
|
||||
#define QSTORAGEDEVICE_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QStorageInfo>
|
||||
#include <memory>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
class QStorageDevice : public QIODevice {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QStorageDevice();
|
||||
QStorageDevice(QObject *parent = nullptr);
|
||||
|
||||
void setStorage(const QStorageInfo &storage);
|
||||
|
||||
QStorageInfo storage() const;
|
||||
|
||||
private:
|
||||
struct Buffer {
|
||||
qint64 offset = -1;
|
||||
// length is usually 20 * 1024 * CHUNK_SIZE = 10 MB
|
||||
std::unique_ptr<char[]> buffer;
|
||||
DWORD length = 0;
|
||||
|
||||
void clear() {
|
||||
offset = -1;
|
||||
length = 0;
|
||||
}
|
||||
} _cache;
|
||||
|
||||
DWORD cacheSize() const;
|
||||
|
||||
private:
|
||||
QStorageInfo _storage;
|
||||
HANDLE hDevice;
|
||||
DWORD CHUNK_SIZE;
|
||||
qint64 _size;
|
||||
|
||||
// QIODevice interface
|
||||
public:
|
||||
virtual bool isSequential() const override;
|
||||
virtual bool open(OpenMode mode) override;
|
||||
virtual void close() override;
|
||||
virtual qint64 size() const override;
|
||||
virtual bool seek(qint64 pos) override;
|
||||
virtual bool canReadLine() const override;
|
||||
|
||||
protected:
|
||||
virtual qint64 readData(char *data, qint64 maxlen) override;
|
||||
virtual qint64 writeData(const char *data, qint64 len) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // QSTORAGEDEVICE_H
|
||||
|
|
|
@ -44,7 +44,13 @@ qsizetype QHexView::currentRow() { return m_cursor->currentLine(); }
|
|||
qsizetype QHexView::currentColumn() { return m_cursor->currentColumn(); }
|
||||
qsizetype QHexView::currentOffset() { return m_cursor->position().offset(); }
|
||||
|
||||
qsizetype QHexView::selectlength() { return m_cursor->selectionLength(); }
|
||||
qsizetype QHexView::currentSelectionLength() {
|
||||
return m_cursor->currentSelectionLength();
|
||||
}
|
||||
|
||||
qsizetype QHexView::selectionCount() { return m_cursor->selectionCount(); }
|
||||
|
||||
bool QHexView::hasSelection() { return m_cursor->hasSelection(); }
|
||||
|
||||
bool QHexView::asciiVisible() { return m_renderer->stringVisible(); }
|
||||
|
||||
|
@ -100,13 +106,15 @@ void QHexView::getStatus() {
|
|||
}
|
||||
|
||||
void QHexView::establishSignal(QHexDocument *doc) {
|
||||
connect(doc, &QHexDocument::documentChanged, this, [&]() {
|
||||
connect(doc, &QHexDocument::documentChanged, this, [this]() {
|
||||
this->adjustScrollBars();
|
||||
this->viewport()->update();
|
||||
});
|
||||
|
||||
connect(m_cursor, &QHexCursor::positionChanged, this,
|
||||
&QHexView::moveToSelection);
|
||||
connect(m_cursor, &QHexCursor::selectionChanged, this,
|
||||
[this]() { this->viewport()->update(); });
|
||||
connect(m_cursor, &QHexCursor::insertionModeChanged, this,
|
||||
&QHexView::renderCurrentLine);
|
||||
connect(doc, &QHexDocument::canUndoChanged, this,
|
||||
|
@ -115,16 +123,16 @@ void QHexView::establishSignal(QHexDocument *doc) {
|
|||
&QHexView::canRedoChanged);
|
||||
connect(doc, &QHexDocument::documentSaved, this, &QHexView::documentSaved);
|
||||
connect(doc, &QHexDocument::metabgVisibleChanged, this, [=](bool b) {
|
||||
QHexView::metabgVisibleChanged(b);
|
||||
emit this->metaStatusChanged();
|
||||
emit metabgVisibleChanged(b);
|
||||
emit metaStatusChanged();
|
||||
});
|
||||
connect(doc, &QHexDocument::metafgVisibleChanged, this, [=](bool b) {
|
||||
QHexView::metafgVisibleChanged(b);
|
||||
emit this->metaStatusChanged();
|
||||
emit metafgVisibleChanged(b);
|
||||
emit metaStatusChanged();
|
||||
});
|
||||
connect(doc, &QHexDocument::metaCommentVisibleChanged, this, [=](bool b) {
|
||||
QHexView::metaCommentVisibleChanged(b);
|
||||
emit this->metaStatusChanged();
|
||||
emit metaCommentVisibleChanged(b);
|
||||
emit metaStatusChanged();
|
||||
});
|
||||
connect(doc, &QHexDocument::metaDataChanged, this,
|
||||
[=] { this->viewport()->update(); });
|
||||
|
@ -283,43 +291,27 @@ qsizetype QHexView::searchBackward(qsizetype begin, const QByteArray &ba) {
|
|||
qsizetype startPos;
|
||||
if (begin < 0) {
|
||||
startPos = m_cursor->position().offset() - 1;
|
||||
if (m_cursor->hasSelection()) {
|
||||
startPos = m_cursor->selectionStart().offset() - 1;
|
||||
}
|
||||
} else {
|
||||
startPos = begin;
|
||||
}
|
||||
return m_document->searchBackward(startPos, ba);
|
||||
}
|
||||
|
||||
void QHexView::gotoBookMark(qsizetype index) {
|
||||
if (index >= 0 && index < m_document->bookMarksCount()) {
|
||||
auto bookmark = m_document->bookMarkByIndex(index);
|
||||
m_cursor->moveTo(bookmark.pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool QHexView::existBookMarkByIndex(qsizetype &index) {
|
||||
auto curpos = m_cursor->position().offset();
|
||||
int i = 0;
|
||||
for (auto &item : m_document->getAllBookMarks()) {
|
||||
if (item.pos == curpos) {
|
||||
index = i;
|
||||
return true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHexView::RemoveSelection(int nibbleindex) {
|
||||
if (!m_cursor->hasSelection())
|
||||
return false;
|
||||
|
||||
auto res = m_document->Remove(m_cursor, m_cursor->selectionStart().offset(),
|
||||
m_cursor->selectionLength(), nibbleindex);
|
||||
if (res)
|
||||
m_cursor->clearSelection();
|
||||
auto total = m_cursor->selectionCount();
|
||||
bool res = true;
|
||||
m_document->beginMarco(QStringLiteral(""));
|
||||
for (int i = 0; i < total; ++i) {
|
||||
res &=
|
||||
m_document->Remove(m_cursor, m_cursor->selectionStart(i).offset(),
|
||||
m_cursor->selectionLength(i), nibbleindex);
|
||||
}
|
||||
m_document->endMarco();
|
||||
m_cursor->clearSelection();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -327,10 +319,16 @@ bool QHexView::removeSelection() {
|
|||
if (!m_cursor->hasSelection())
|
||||
return false;
|
||||
|
||||
auto res = m_document->remove(m_cursor->selectionStart().offset(),
|
||||
m_cursor->selectionLength());
|
||||
if (res)
|
||||
m_cursor->clearSelection();
|
||||
// We essure selections are ordered by desending
|
||||
// by selection-start, so it's safe to remove
|
||||
auto total = m_cursor->selectionCount();
|
||||
bool res = true;
|
||||
for (int i = 0; i < total; ++i) {
|
||||
res &= m_document->remove(m_cursor->selectionStart(i).offset(),
|
||||
m_cursor->selectionLength(i));
|
||||
}
|
||||
|
||||
m_cursor->clearSelection();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -338,16 +336,27 @@ bool QHexView::atEnd() const {
|
|||
return m_cursor->position().offset() >= m_document->length();
|
||||
}
|
||||
|
||||
void QHexView::gotoMetaData(qsizetype index) {
|
||||
m_cursor->moveTo(m_document->metadata()->getallMetasPtr().at(index).begin);
|
||||
QByteArray QHexView::selectedBytes(qsizetype index) const {
|
||||
return m_document->read(m_cursor->selectionStart(index).offset(),
|
||||
m_cursor->currentSelectionLength());
|
||||
}
|
||||
|
||||
QByteArray QHexView::selectedBytes() const {
|
||||
if (!m_cursor->hasSelection())
|
||||
return QByteArray();
|
||||
QByteArray QHexView::previewSelectedBytes() const {
|
||||
auto sel = m_cursor->previewSelection().normalized();
|
||||
return m_document->read(sel.start.offset(), sel.length());
|
||||
}
|
||||
|
||||
return m_document->read(m_cursor->selectionStart().offset(),
|
||||
m_cursor->selectionLength());
|
||||
QByteArrayList QHexView::selectedBytes() const {
|
||||
if (!m_cursor->hasSelection())
|
||||
return {};
|
||||
|
||||
QByteArrayList res;
|
||||
auto total = m_cursor->selectionCount();
|
||||
for (int i = 0; i < total; ++i) {
|
||||
res << m_document->read(m_cursor->selectionStart(i).offset(),
|
||||
m_cursor->selectionLength(i));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void QHexView::paste(bool hex) {
|
||||
|
@ -436,7 +445,7 @@ bool QHexView::copy(bool hex) {
|
|||
|
||||
QClipboard *c = qApp->clipboard();
|
||||
|
||||
auto len = m_cursor->selectionLength();
|
||||
auto len = currentSelectionLength();
|
||||
|
||||
// 如果拷贝字节超过 ? MB 阻止
|
||||
if (len > 1024 * 1024 * m_copylimit) {
|
||||
|
@ -444,7 +453,7 @@ bool QHexView::copy(bool hex) {
|
|||
return false;
|
||||
}
|
||||
|
||||
QByteArray bytes = this->selectedBytes();
|
||||
QByteArray bytes = this->selectedBytes().join();
|
||||
|
||||
if (hex)
|
||||
bytes = bytes.toHex(' ').toUpper();
|
||||
|
@ -545,8 +554,18 @@ void QHexView::mousePressEvent(QMouseEvent *e) {
|
|||
|
||||
m_renderer->selectArea(abspos);
|
||||
|
||||
if (m_renderer->editableArea(m_renderer->selectedArea()))
|
||||
m_cursor->moveTo(position);
|
||||
if (m_renderer->editableArea(m_renderer->selectedArea())) {
|
||||
auto m = getSelectionMode();
|
||||
bool clearSelection = false;
|
||||
if (m == QHexCursor::SelectionNormal) {
|
||||
clearSelection = m_cursor->selectionCount() <= 1 ||
|
||||
e->modifiers().testFlag(Qt::ControlModifier);
|
||||
} else if (m == QHexCursor::SelectionSingle) {
|
||||
clearSelection = true;
|
||||
}
|
||||
|
||||
m_cursor->moveTo(position, clearSelection);
|
||||
}
|
||||
|
||||
e->accept();
|
||||
}
|
||||
|
@ -570,7 +589,9 @@ void QHexView::mouseMoveEvent(QMouseEvent *e) {
|
|||
if (!m_renderer->hitTest(abspos, &position, this->firstVisibleLine()))
|
||||
return;
|
||||
|
||||
cursor->select(position.line, position.column, 0);
|
||||
cursor->select(position.line, position.column, 0,
|
||||
QHexCursor::SelectionModes(
|
||||
getSelectionMode() | QHexCursor::SelectionPreview));
|
||||
e->accept();
|
||||
}
|
||||
|
||||
|
@ -591,6 +612,13 @@ void QHexView::mouseReleaseEvent(QMouseEvent *e) {
|
|||
return;
|
||||
if (!m_blinktimer->isActive())
|
||||
m_blinktimer->start();
|
||||
|
||||
if (m_cursor->hasPreviewSelection()) {
|
||||
auto sel = m_cursor->previewSelection();
|
||||
m_cursor->mergePreviewSelection();
|
||||
m_cursor->moveTo(sel.end);
|
||||
}
|
||||
|
||||
e->accept();
|
||||
}
|
||||
|
||||
|
@ -730,7 +758,7 @@ void QHexView::moveNext(bool select) {
|
|||
|
||||
if (select)
|
||||
cur->select(line, std::min(m_renderer->hexLineWidth() - 1, int(column)),
|
||||
nibbleindex);
|
||||
nibbleindex, QHexCursor::SelectionAdd);
|
||||
else
|
||||
cur->moveTo(line, std::min(m_renderer->hexLineWidth() - 1, int(column)),
|
||||
nibbleindex);
|
||||
|
@ -773,6 +801,34 @@ void QHexView::movePrevious(bool select) {
|
|||
cur->moveTo(line, std::max(0, column), nibbleindex);
|
||||
}
|
||||
|
||||
QHexCursor::SelectionMode QHexView::getSelectionMode() const {
|
||||
auto mods = qApp->keyboardModifiers();
|
||||
|
||||
bool pressedShift = mods.testFlag(Qt::ShiftModifier);
|
||||
bool pressedAlt = mods.testFlag(Qt::AltModifier);
|
||||
bool pressedControl = mods.testFlag(Qt::ControlModifier);
|
||||
|
||||
if (pressedAlt && pressedShift) {
|
||||
pressedShift = false;
|
||||
pressedAlt = false;
|
||||
pressedControl = true;
|
||||
}
|
||||
|
||||
if (pressedControl) {
|
||||
return QHexCursor::SelectionSingle;
|
||||
}
|
||||
|
||||
if (pressedShift) {
|
||||
return QHexCursor::SelectionAdd;
|
||||
}
|
||||
|
||||
if (pressedAlt) {
|
||||
return QHexCursor::SelectionRemove;
|
||||
}
|
||||
|
||||
return QHexCursor::SelectionNormal;
|
||||
}
|
||||
|
||||
void QHexView::renderCurrentLine() {
|
||||
if (m_document)
|
||||
this->renderLine(m_cursor->currentLine());
|
||||
|
@ -821,12 +877,15 @@ bool QHexView::processAction(QHexCursor *cur, QKeyEvent *e) {
|
|||
|
||||
// modified by wingsummer
|
||||
if (isKeepSize()) {
|
||||
if (e->key() == Qt::Key_Backspace)
|
||||
m_document->Replace(m_cursor, cur->position().offset() - 1,
|
||||
uchar(0), 0);
|
||||
else
|
||||
m_document->Replace(m_cursor, cur->position().offset(),
|
||||
uchar(0), 0);
|
||||
if (cur->insertionMode() == QHexCursor::OverwriteMode) {
|
||||
if (e->key() == Qt::Key_Backspace)
|
||||
m_document->Replace(m_cursor,
|
||||
cur->position().offset() - 1,
|
||||
uchar(0), 0);
|
||||
else
|
||||
m_document->Replace(m_cursor, cur->position().offset(),
|
||||
uchar(0), 0);
|
||||
}
|
||||
} else {
|
||||
if (e->key() == Qt::Key_Backspace)
|
||||
m_document->Remove(m_cursor, cur->position().offset() - 1,
|
||||
|
@ -917,7 +976,7 @@ bool QHexView::processMove(QHexCursor *cur, QKeyEvent *e) {
|
|||
|
||||
if (e->matches(QKeySequence::MoveToEndOfDocument))
|
||||
cur->moveTo(m_renderer->documentLastLine(),
|
||||
m_renderer->documentLastColumn());
|
||||
int(m_renderer->documentLastColumn()));
|
||||
else
|
||||
cur->select(m_renderer->documentLastLine(),
|
||||
m_renderer->documentLastColumn());
|
||||
|
@ -932,7 +991,7 @@ bool QHexView::processMove(QHexCursor *cur, QKeyEvent *e) {
|
|||
if (e->matches(QKeySequence::MoveToEndOfLine)) {
|
||||
if (cur->currentLine() == m_renderer->documentLastLine())
|
||||
cur->moveTo(cur->currentLine(),
|
||||
m_renderer->documentLastColumn());
|
||||
int(m_renderer->documentLastColumn()));
|
||||
else
|
||||
cur->moveTo(cur->currentLine(), m_renderer->hexLineWidth() - 1,
|
||||
0);
|
||||
|
@ -954,11 +1013,12 @@ bool QHexView::processTextInput(QHexCursor *cur, QKeyEvent *e) {
|
|||
if (isReadOnly() || isLocked() || (e->modifiers() & Qt::ControlModifier))
|
||||
return false;
|
||||
|
||||
if (e->text().isEmpty()) {
|
||||
auto text = e->text();
|
||||
if (text.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uchar key = static_cast<uchar>(e->text()[0].toLatin1());
|
||||
uchar key = static_cast<uchar>(text[0].toLatin1());
|
||||
|
||||
if ((m_renderer->selectedArea() == QHexRenderer::HexArea)) {
|
||||
if (!((key >= '0' && key <= '9') ||
|
||||
|
|
|
@ -51,7 +51,10 @@ public:
|
|||
qsizetype currentRow();
|
||||
qsizetype currentColumn();
|
||||
qsizetype currentOffset();
|
||||
qsizetype selectlength();
|
||||
qsizetype currentSelectionLength();
|
||||
|
||||
qsizetype selectionCount();
|
||||
bool hasSelection();
|
||||
|
||||
void setAsciiVisible(bool b);
|
||||
bool asciiVisible();
|
||||
|
@ -97,15 +100,13 @@ public:
|
|||
qsizetype searchForward(qsizetype begin, const QByteArray &ba);
|
||||
qsizetype searchBackward(qsizetype begin, const QByteArray &ba);
|
||||
|
||||
void gotoBookMark(qsizetype index);
|
||||
bool existBookMarkByIndex(qsizetype &index);
|
||||
bool RemoveSelection(int nibbleindex = 1);
|
||||
bool removeSelection();
|
||||
bool atEnd() const;
|
||||
|
||||
void gotoMetaData(qsizetype index);
|
||||
|
||||
QByteArray selectedBytes() const;
|
||||
QByteArray selectedBytes(qsizetype index) const;
|
||||
QByteArray previewSelectedBytes() const;
|
||||
QByteArrayList selectedBytes() const;
|
||||
|
||||
bool cut(bool hex);
|
||||
bool copy(bool hex = false);
|
||||
|
@ -172,6 +173,8 @@ private:
|
|||
void moveNext(bool select = false);
|
||||
void movePrevious(bool select = false);
|
||||
|
||||
QHexCursor::SelectionMode getSelectionMode() const;
|
||||
|
||||
private:
|
||||
bool processMove(QHexCursor *cur, QKeyEvent *e);
|
||||
bool processTextInput(QHexCursor *cur, QKeyEvent *e);
|
||||
|
|
|
@ -60,6 +60,12 @@ RibbonTabContent *Ribbon::addTab(const QString &tabName) {
|
|||
return ribbonTabContent;
|
||||
}
|
||||
|
||||
void Ribbon::addTab(RibbonTabContent *tabContent, const QString &tabName) {
|
||||
if (tabContent) {
|
||||
QTabWidget::addTab(tabContent, tabName);
|
||||
}
|
||||
}
|
||||
|
||||
RibbonTabContent *Ribbon::addTab(const QIcon &tabIcon, const QString &tabName) {
|
||||
// Note: superclass QTabWidget also has a function addTab()
|
||||
RibbonTabContent *ribbonTabContent = new RibbonTabContent;
|
||||
|
|
|
@ -29,6 +29,11 @@ public:
|
|||
/// \param[in] tabName Name of the tab
|
||||
RibbonTabContent *addTab(const QString &tabName);
|
||||
|
||||
/// Add a tab to the ribbon.
|
||||
///
|
||||
/// \param[in] tabContent pointer of the tab
|
||||
void addTab(RibbonTabContent *tabContent, const QString &tabName);
|
||||
|
||||
/// Add a tab to the ribbon.
|
||||
///
|
||||
/// \param[in] tabIcon Icon of the tab
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a006a7a48bb30a247f0344b788c62c2806edd90b
|
|
@ -40,9 +40,7 @@ set(WIDGETS_SRC
|
|||
lib/widgets/qlinenumberpanel.cpp
|
||||
lib/widgets/qlinenumberpanel.h
|
||||
lib/widgets/qpanel.cpp
|
||||
lib/widgets/qpanel.h
|
||||
lib/widgets/qsimplecolorpicker.cpp
|
||||
lib/widgets/qsimplecolorpicker.h)
|
||||
lib/widgets/qpanel.h)
|
||||
|
||||
set(QNFA_SRC
|
||||
lib/qnfa/light_vector.h lib/qnfa/qnfadefinition.h lib/qnfa/qnfa.h
|
||||
|
@ -54,12 +52,9 @@ set(SNIPPET_SRC
|
|||
lib/snippets/qsnippet_p.h
|
||||
lib/snippets/qsnippetbinding.cpp
|
||||
lib/snippets/qsnippetbinding.h
|
||||
lib/snippets/qsnippetedit.cpp
|
||||
lib/snippets/qsnippetedit.h
|
||||
lib/snippets/qsnippetmanager.cpp
|
||||
lib/snippets/qsnippetmanager.h
|
||||
lib/snippets/qsnippetpatternloader.h
|
||||
lib/snippets/snippetedit.ui)
|
||||
lib/snippets/qsnippetpatternloader.h)
|
||||
|
||||
set(SOURCE_FILES
|
||||
lib/qce-config.h
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -186,24 +186,23 @@ public:
|
|||
QFormatScheme *formatScheme() const;
|
||||
void setFormatScheme(QFormatScheme *f);
|
||||
|
||||
int lineSpacing() const;
|
||||
|
||||
int getNextGroupId();
|
||||
void releaseGroupId(int groupId);
|
||||
void clearMatches(int groupId);
|
||||
void flushMatches(int groupId);
|
||||
void addMatch(int groupId, int line, int pos, int len, int format);
|
||||
|
||||
static QFont font();
|
||||
static void setFont(const QFont &f);
|
||||
static const QFontMetrics &fontMetrics();
|
||||
QFont font();
|
||||
void setFont(const QFont &f);
|
||||
const QFontMetrics &fontMetrics();
|
||||
|
||||
static LineEnding defaultLineEnding();
|
||||
static void setDefaultLineEnding(LineEnding le);
|
||||
int tabStop();
|
||||
void setTabStop(int n);
|
||||
|
||||
static int tabStop();
|
||||
static void setTabStop(int n);
|
||||
|
||||
static WhiteSpaceMode showSpaces();
|
||||
static void setShowSpaces(WhiteSpaceMode y);
|
||||
WhiteSpaceMode showSpaces();
|
||||
void setShowSpaces(WhiteSpaceMode y);
|
||||
|
||||
static QFormatScheme *defaultFormatScheme();
|
||||
static void setDefaultFormatScheme(QFormatScheme *f);
|
||||
|
@ -214,7 +213,7 @@ public:
|
|||
static int screenLength(const QChar *d, int l, int tabStop);
|
||||
static QString screenable(const QChar *d, int l, int tabStop);
|
||||
|
||||
inline void markViewDirty() { formatsChanged(); }
|
||||
inline void markViewDirty() { emit formatsChanged(); }
|
||||
|
||||
bool isClean() const;
|
||||
|
||||
|
@ -257,6 +256,8 @@ signals:
|
|||
|
||||
void lineEndingChanged(int lineEnding);
|
||||
|
||||
void fontChanged(const QFont &font);
|
||||
|
||||
private:
|
||||
QString m_leftOver;
|
||||
QDocumentPrivate *m_impl;
|
||||
|
|
|
@ -54,6 +54,7 @@ Q_DECLARE_TYPEINFO(QDocumentSelection, Q_PRIMITIVE_TYPE);
|
|||
class QCE_EXPORT QDocumentPrivate {
|
||||
friend class QEditConfig;
|
||||
|
||||
friend class QEditor;
|
||||
friend class QDocument;
|
||||
friend class QDocumentCommand;
|
||||
friend class QDocumentLineHandle;
|
||||
|
@ -67,7 +68,7 @@ public:
|
|||
|
||||
void draw(QPainter *p, QDocument::PaintContext &cxt);
|
||||
|
||||
QDocumentLineHandle *lineForPosition(int &position) const;
|
||||
QDocumentLineHandle *lineForPosition(int position) const;
|
||||
int position(const QDocumentLineHandle *l) const;
|
||||
|
||||
QDocumentLineHandle *at(int line) const;
|
||||
|
@ -86,7 +87,7 @@ public:
|
|||
void setWidth();
|
||||
void setHeight();
|
||||
|
||||
static void setFont(const QFont &f);
|
||||
void setFont(const QFont &f);
|
||||
|
||||
void beginChangeBlock();
|
||||
void endChangeBlock();
|
||||
|
@ -120,7 +121,6 @@ public:
|
|||
|
||||
void setWidth(int width);
|
||||
|
||||
void emitFormatsChanged();
|
||||
void emitContentsChanged();
|
||||
|
||||
void emitLineDeleted(QDocumentLineHandle *h);
|
||||
|
@ -165,7 +165,7 @@ private:
|
|||
struct Match {
|
||||
int line;
|
||||
QFormatRange range;
|
||||
QDocumentLineHandle *h;
|
||||
QDocumentLineHandle *h = nullptr;
|
||||
};
|
||||
|
||||
struct MatchList : QList<Match> {
|
||||
|
@ -182,21 +182,24 @@ private:
|
|||
int m_width, m_height;
|
||||
|
||||
int m_tabStop;
|
||||
static int m_defaultTabStop;
|
||||
|
||||
static QFont *m_font;
|
||||
static bool m_fixedPitch;
|
||||
static QFontMetrics *m_fontMetrics;
|
||||
static int m_leftMargin;
|
||||
static QDocument::WhiteSpaceMode m_showSpaces;
|
||||
QFont m_font;
|
||||
bool m_fixedPitch;
|
||||
QFontMetrics m_fontMetrics;
|
||||
int m_leftMargin;
|
||||
QDocument::WhiteSpaceMode m_showSpaces;
|
||||
int m_lineHeight;
|
||||
int m_lineSpacing;
|
||||
int m_spaceWidth;
|
||||
int m_ascent;
|
||||
int m_descent;
|
||||
int m_leading;
|
||||
int m_wrapMargin;
|
||||
|
||||
static QFont m_defaultFont;
|
||||
static int m_defaultTabStop;
|
||||
static QDocument::LineEnding m_defaultLineEnding;
|
||||
static int m_lineHeight;
|
||||
static int m_lineSpacing;
|
||||
static int m_spaceWidth;
|
||||
static int m_ascent;
|
||||
static int m_descent;
|
||||
static int m_leading;
|
||||
static int m_wrapMargin;
|
||||
static QDocument::WhiteSpaceMode m_defaultShowSpaces;
|
||||
|
||||
QFormatScheme *m_formatScheme;
|
||||
QLanguageDefinition *m_language;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#include "qdocument_p.h"
|
||||
#include "qformatscheme.h"
|
||||
|
||||
/*!
|
||||
\ingroup document
|
||||
|
@ -142,7 +143,8 @@ void QDocumentCommand::setUndoOffset(int off) { m_undoOffset = off; }
|
|||
This helper method is provided so that subclasses may actually
|
||||
modify the document contents without using private API.
|
||||
*/
|
||||
void QDocumentCommand::insertText(int line, int pos, const QString &s) {
|
||||
void QDocumentCommand::insertText(int line, int pos, const QString &s,
|
||||
const QString &sfmtID) {
|
||||
if (!m_doc)
|
||||
return;
|
||||
|
||||
|
@ -155,6 +157,21 @@ void QDocumentCommand::insertText(int line, int pos, const QString &s) {
|
|||
h->textBuffer().insert(pos, s);
|
||||
h->shiftOverlays(pos, s.length());
|
||||
|
||||
if (!sfmtID.isEmpty()) {
|
||||
auto fmt = m_doc->formatScheme();
|
||||
if (fmt) {
|
||||
auto id = fmt->id(sfmtID);
|
||||
if (id) {
|
||||
QFormatRange over;
|
||||
over.format = id;
|
||||
over.offset = pos;
|
||||
over.length = s.length();
|
||||
|
||||
h->addOverlay(over);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pd->adjustWidth(line);
|
||||
}
|
||||
|
||||
|
@ -484,8 +501,9 @@ void QDocumentCommand::markUndone(QDocumentLineHandle *h) {
|
|||
QDocumentInsertCommand::QDocumentInsertCommand(int l, int offset,
|
||||
const QString &text,
|
||||
QDocument *doc,
|
||||
const QString &sfmtID,
|
||||
QDocumentCommand *p)
|
||||
: QDocumentCommand(Insert, doc, p) {
|
||||
: QDocumentCommand(Insert, doc, p), m_sfmtID(sfmtID) {
|
||||
QStringList lines = text.split(QLatin1Char('\n'), Qt::KeepEmptyParts);
|
||||
|
||||
if (!m_doc || text.isEmpty())
|
||||
|
@ -497,8 +515,19 @@ QDocumentInsertCommand::QDocumentInsertCommand(int l, int offset,
|
|||
m_data.begin = lines.takeAt(0);
|
||||
m_data.endOffset = lines.count() ? lines.last().length() : -1;
|
||||
|
||||
foreach (const QString &s, lines)
|
||||
m_data.handles << new QDocumentLineHandle(s, m_doc);
|
||||
for (auto &s : lines) {
|
||||
auto lh = new QDocumentLineHandle(s, m_doc);
|
||||
QFormatRange over;
|
||||
auto fmt = m_doc->formatScheme();
|
||||
auto id = fmt->id(sfmtID);
|
||||
if (id) {
|
||||
over.format = id;
|
||||
over.offset = 0;
|
||||
over.length = s.length();
|
||||
lh->addOverlay(over);
|
||||
}
|
||||
m_data.handles << lh;
|
||||
}
|
||||
|
||||
QDocumentLine bl = m_doc->line(l);
|
||||
|
||||
|
@ -541,7 +570,7 @@ void QDocumentInsertCommand::redo() {
|
|||
removeText(m_data.lineNumber, m_data.startOffset, m_data.end.length());
|
||||
}
|
||||
|
||||
insertText(m_data.lineNumber, m_data.startOffset, m_data.begin);
|
||||
insertText(m_data.lineNumber, m_data.startOffset, m_data.begin, m_sfmtID);
|
||||
|
||||
insertLines(m_data.lineNumber, m_data.handles);
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
QList<QDocumentLineHandle *> handles;
|
||||
};
|
||||
|
||||
QDocumentCommand(Command c, QDocument *d, QDocumentCommand *p = 0);
|
||||
QDocumentCommand(Command c, QDocument *d, QDocumentCommand *p = nullptr);
|
||||
virtual ~QDocumentCommand();
|
||||
|
||||
virtual int id() const;
|
||||
|
@ -77,7 +77,8 @@ protected:
|
|||
|
||||
void updateTarget(int l, int offset);
|
||||
|
||||
void insertText(int line, int pos, const QString &s);
|
||||
void insertText(int line, int pos, const QString &s,
|
||||
const QString &sfmtID = {});
|
||||
void removeText(int line, int pos, int length);
|
||||
|
||||
void insertLines(int after, const QList<QDocumentLineHandle *> &l);
|
||||
|
@ -102,7 +103,8 @@ Q_DECLARE_TYPEINFO(QDocumentCommand::TextCommandData, Q_MOVABLE_TYPE);
|
|||
class QCE_EXPORT QDocumentInsertCommand : public QDocumentCommand {
|
||||
public:
|
||||
QDocumentInsertCommand(int l, int offset, const QString &text,
|
||||
QDocument *doc, QDocumentCommand *p = 0);
|
||||
QDocument *doc, const QString &sfmtID = 0,
|
||||
QDocumentCommand *p = nullptr);
|
||||
|
||||
virtual ~QDocumentInsertCommand();
|
||||
|
||||
|
@ -113,12 +115,13 @@ public:
|
|||
|
||||
private:
|
||||
TextCommandData m_data;
|
||||
QString m_sfmtID;
|
||||
};
|
||||
|
||||
class QCE_EXPORT QDocumentEraseCommand : public QDocumentCommand {
|
||||
public:
|
||||
QDocumentEraseCommand(int bl, int bo, int el, int eo, QDocument *doc,
|
||||
QDocumentCommand *p = 0);
|
||||
QDocumentCommand *p = nullptr);
|
||||
|
||||
virtual ~QDocumentEraseCommand();
|
||||
|
||||
|
|
|
@ -418,16 +418,6 @@ void QDocumentCursor::shift(int offset) {
|
|||
m_handle->shift(offset);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Set the text position of the cursor (within the whole document)
|
||||
|
||||
Remark made about position() applies.
|
||||
*/
|
||||
void QDocumentCursor::setPosition(int pos, MoveMode m) {
|
||||
if (m_handle)
|
||||
m_handle->setPosition(pos, m);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Moves the cursor position
|
||||
\param offset number of times the selected move will be done
|
||||
|
@ -515,7 +505,7 @@ void QDocumentCursor::eraseLine() {
|
|||
*/
|
||||
void QDocumentCursor::insertLine(bool keepAnchor) {
|
||||
if (m_handle)
|
||||
m_handle->insertText("\n", keepAnchor);
|
||||
m_handle->insertText(QStringLiteral("\n"), keepAnchor);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -525,9 +515,10 @@ void QDocumentCursor::insertLine(bool keepAnchor) {
|
|||
|
||||
\note Nothing happens if \a s is empty
|
||||
*/
|
||||
void QDocumentCursor::insertText(const QString &s, bool keepAnchor) {
|
||||
void QDocumentCursor::insertText(const QString &s, bool keepAnchor,
|
||||
const QString &sfmtID) {
|
||||
if (m_handle)
|
||||
m_handle->insertText(s, keepAnchor);
|
||||
m_handle->insertText(s, keepAnchor, sfmtID);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include "qce-config.h"
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
/*!
|
||||
\file qdocumentcursor.h
|
||||
\brief Definition of the QDocumentCursor class
|
||||
|
@ -132,7 +134,6 @@ public:
|
|||
QDocumentLine anchorLine() const;
|
||||
|
||||
void shift(int offset);
|
||||
void setPosition(int pos, MoveMode m = MoveAnchor);
|
||||
bool movePosition(int offset, MoveOperation op = NextCharacter,
|
||||
MoveMode m = MoveAnchor);
|
||||
|
||||
|
@ -142,7 +143,8 @@ public:
|
|||
|
||||
void eraseLine();
|
||||
void insertLine(bool keepAnchor = false);
|
||||
void insertText(const QString &s, bool keepAnchor = false);
|
||||
void insertText(const QString &s, bool keepAnchor = false,
|
||||
const QString &sfmtID = {});
|
||||
|
||||
QDocumentCursor selectionStart() const;
|
||||
QDocumentCursor selectionEnd() const;
|
||||
|
@ -181,4 +183,6 @@ private:
|
|||
QDocumentCursorHandle *m_handle;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QDocumentCursor);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -90,10 +90,11 @@ public:
|
|||
int position() const;
|
||||
|
||||
void shift(int offset);
|
||||
void setPosition(int pos, int m);
|
||||
bool movePosition(int offset, int op, int m);
|
||||
bool movePosition(int offset, QDocumentCursor::MoveOperation op,
|
||||
QDocumentCursor::MoveMode m);
|
||||
|
||||
void insertText(const QString &s, bool keepAnchor = false);
|
||||
void insertText(const QString &s, bool keepAnchor = false,
|
||||
const QString &sfmtID = {});
|
||||
|
||||
QChar nextChar() const;
|
||||
QChar previousChar() const;
|
||||
|
|
|
@ -59,11 +59,11 @@ void addDataPath(const QString &path);
|
|||
template <typename Registerable>
|
||||
class Registar {
|
||||
public:
|
||||
Registar() { Registerable::_register(); }
|
||||
constexpr Registar() { Registerable::_register(); }
|
||||
};
|
||||
|
||||
// added by wingsummer
|
||||
[[maybe_unused]] static QStringList getEncodings() {
|
||||
Q_DECL_UNUSED static QStringList getEncodings() {
|
||||
static QStringList encodings;
|
||||
|
||||
if (encodings.isEmpty()) {
|
||||
|
@ -91,8 +91,8 @@ public:
|
|||
return encodings;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static QString convertString(const QString &encoding,
|
||||
const QByteArray &data) {
|
||||
Q_DECL_UNUSED static QString convertString(const QString &encoding,
|
||||
const QByteArray &data) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
auto enc = QStringConverter::encodingForName(encoding.toUtf8());
|
||||
if (enc) {
|
||||
|
@ -113,8 +113,8 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static QByteArray convertByteArray(const QString &encoding,
|
||||
const QString &data) {
|
||||
Q_DECL_UNUSED static QByteArray convertByteArray(const QString &encoding,
|
||||
const QString &data) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
auto enc = QStringConverter::encodingForName(encoding.toUtf8());
|
||||
if (enc) {
|
||||
|
|
|
@ -26,15 +26,11 @@
|
|||
#include <QKeyEvent>
|
||||
#include <QTextCursor>
|
||||
|
||||
#ifdef _QCODE_MODEL_
|
||||
#include "qcodebuffer.h"
|
||||
#endif
|
||||
|
||||
/*!
|
||||
|
||||
*/
|
||||
QCodeCompletionEngine::QCodeCompletionEngine(QObject *p)
|
||||
: QObject(p), m_max(0) {
|
||||
: QObject(p), m_max(0), m_trigWordLen(-1) {
|
||||
pForcedTrigger = new QAction(tr("&Trigger completion"), this);
|
||||
|
||||
connect(pForcedTrigger, SIGNAL(triggered()), this, SLOT(complete()));
|
||||
|
@ -84,11 +80,6 @@ void QCodeCompletionEngine::removeTrigger(const QString &s) {
|
|||
|
||||
/*!
|
||||
|
||||
*/
|
||||
void QCodeCompletionEngine::setCodeModel(QCodeModel *m) { Q_UNUSED(m) }
|
||||
|
||||
/*!
|
||||
|
||||
*/
|
||||
QEditor *QCodeCompletionEngine::editor() const { return pEdit; }
|
||||
|
||||
|
@ -97,7 +88,7 @@ QEditor *QCodeCompletionEngine::editor() const { return pEdit; }
|
|||
*/
|
||||
void QCodeCompletionEngine::setEditor(QEditor *e) {
|
||||
if (pEdit) {
|
||||
pEdit->removeAction(pForcedTrigger, "&Edit");
|
||||
pEdit->removeAction(pForcedTrigger, tr("&Edit"));
|
||||
// pEdit->removeEventFilter(this);
|
||||
|
||||
disconnect(pEdit, SIGNAL(textEdited(QKeyEvent *)), this,
|
||||
|
@ -108,10 +99,9 @@ void QCodeCompletionEngine::setEditor(QEditor *e) {
|
|||
|
||||
if (pEdit) {
|
||||
// pEdit->installEventFilter(this);
|
||||
pEdit->addAction(pForcedTrigger, "&Edit");
|
||||
|
||||
connect(pEdit, SIGNAL(textEdited(QKeyEvent *)), this,
|
||||
SLOT(textEdited(QKeyEvent *)));
|
||||
pEdit->addAction(pForcedTrigger, tr("&Edit"));
|
||||
connect(pEdit, &QEditor::textEdited, this,
|
||||
&QCodeCompletionEngine::textEdited);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,8 +140,14 @@ void QCodeCompletionEngine::textEdited(QKeyEvent *k) {
|
|||
|
||||
auto count = txt.length();
|
||||
|
||||
if (txt.isEmpty() || m_triggers.isEmpty())
|
||||
if (txt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_triggers.isEmpty()) {
|
||||
triggerWordLenComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
// qDebug("should trigger completion? (bis)");
|
||||
|
||||
|
@ -170,7 +166,6 @@ void QCodeCompletionEngine::textEdited(QKeyEvent *k) {
|
|||
}
|
||||
|
||||
// qDebug("text : %s", qPrintable(txt));
|
||||
|
||||
for (auto &trig : m_triggers) {
|
||||
if (txt.endsWith(trig)) {
|
||||
cur = editor()->cursor();
|
||||
|
@ -184,8 +179,11 @@ void QCodeCompletionEngine::textEdited(QKeyEvent *k) {
|
|||
|
||||
// trigger completion
|
||||
complete(cur, trig);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
triggerWordLenComplete();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -253,38 +251,23 @@ bool QCodeCompletionEngine::eventFilter(QObject *o, QEvent *e) {
|
|||
*/
|
||||
void QCodeCompletionEngine::complete(const QDocumentCursor &c,
|
||||
const QString &trigger) {
|
||||
#ifdef _QCODE_MODEL_
|
||||
// TODO :
|
||||
// * use a more efficient design by avoiding deep copy of the data
|
||||
// * only lex the requested part (stop at cursor or topmost frame required
|
||||
// for proper class hierarchy)
|
||||
|
||||
QDocumentCursor cc = c;
|
||||
cc.movePosition(1, QDocumentCursor::Start, QDocumentCursor::KeepAnchor);
|
||||
|
||||
// qDebug("%s", qPrintable(cc.selectedText()));
|
||||
|
||||
QCodeBuffer buffer(cc.selectedText());
|
||||
// QCodeBuffer buffer(c.document()->text());
|
||||
complete(&buffer, trigger);
|
||||
#else
|
||||
Q_UNUSED(c)
|
||||
Q_UNUSED(trigger)
|
||||
qWarning("From complete(QDocumentCursor, QString)");
|
||||
qWarning("QCodeCompletionEngine is not self-sufficient : subclasses should "
|
||||
"reimplement at least on of the complete() method...");
|
||||
#endif
|
||||
"reimplement at least one of the complete() method...");
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
\brief Overloaded completion callback
|
||||
*/
|
||||
void QCodeCompletionEngine::complete(QCodeStream *s, const QString &trigger) {
|
||||
Q_UNUSED(s)
|
||||
Q_UNUSED(trigger)
|
||||
|
||||
qWarning("From complete(QCodeStream*, QString)");
|
||||
qWarning("QCodeCompletionEngine is not self-sufficient : subclasses should"
|
||||
"reimplement at least on of the complete() method...");
|
||||
void QCodeCompletionEngine::triggerWordLenComplete() {
|
||||
if (m_trigWordLen > 0) {
|
||||
QDocumentCursor cur = editor()->cursor();
|
||||
emit completionTriggered({});
|
||||
complete(cur, {});
|
||||
}
|
||||
}
|
||||
|
||||
qsizetype QCodeCompletionEngine::trigWordLen() const { return m_trigWordLen; }
|
||||
|
||||
void QCodeCompletionEngine::setTrigWordLen(qsizetype newTrigWordLen) {
|
||||
m_trigWordLen = newTrigWordLen;
|
||||
}
|
||||
|
|
|
@ -50,17 +50,18 @@ public:
|
|||
QAction *triggerAction() const;
|
||||
|
||||
QEditor *editor() const;
|
||||
void setEditor(QEditor *e);
|
||||
virtual void setEditor(QEditor *e);
|
||||
|
||||
QStringList triggers() const;
|
||||
|
||||
void addTrigger(const QString &s);
|
||||
void removeTrigger(const QString &s);
|
||||
|
||||
virtual void setCodeModel(QCodeModel *m);
|
||||
|
||||
virtual void retranslate();
|
||||
|
||||
qsizetype trigWordLen() const;
|
||||
void setTrigWordLen(qsizetype newTrigWordLen);
|
||||
|
||||
signals:
|
||||
void popup();
|
||||
void cloned(QCodeCompletionEngine *e);
|
||||
|
@ -74,11 +75,15 @@ protected:
|
|||
virtual void run();
|
||||
virtual bool eventFilter(QObject *o, QEvent *e);
|
||||
|
||||
virtual void complete(QCodeStream *s, const QString &trigger);
|
||||
virtual void complete(const QDocumentCursor &c, const QString &trigger);
|
||||
|
||||
private:
|
||||
void triggerWordLenComplete();
|
||||
|
||||
private:
|
||||
qsizetype m_max;
|
||||
qsizetype m_trigWordLen;
|
||||
|
||||
QString m_trig;
|
||||
QDocumentCursor m_cur;
|
||||
QAction *pForcedTrigger;
|
||||
|
|
|
@ -222,7 +222,7 @@ QPanelLayout *QCodeEdit::panelLayout() const { return m_layout; }
|
|||
QAction *QCodeEdit::addPanel(QPanel *panel, Position pos, bool _add) {
|
||||
panel->attach(m_editor);
|
||||
|
||||
QAction *a = new QAction(panel->type(), m_editor);
|
||||
QAction *a = new QAction(panel->name(), m_editor);
|
||||
a->setCheckable(true);
|
||||
a->setChecked(panel->defaultVisibility());
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,10 +33,6 @@
|
|||
#include "qdocument.h"
|
||||
#include "qdocumentcursor.h"
|
||||
|
||||
#ifdef _QMDI_
|
||||
#include "qmdiclient.h"
|
||||
#endif
|
||||
|
||||
class QMenu;
|
||||
class QAction;
|
||||
class QMimeData;
|
||||
|
@ -51,12 +47,7 @@ class QCodeCompletionEngine;
|
|||
|
||||
class QEditorInputBindingInterface;
|
||||
|
||||
class QCE_EXPORT QEditor : public QAbstractScrollArea
|
||||
#ifdef _QMDI_
|
||||
,
|
||||
public qmdiClient
|
||||
#endif
|
||||
{
|
||||
class QCE_EXPORT QEditor : public QAbstractScrollArea {
|
||||
friend class QEditConfig;
|
||||
friend class QEditorFactory;
|
||||
|
||||
|
@ -129,10 +120,8 @@ public:
|
|||
QList<QDocumentCursor> mirrors;
|
||||
};
|
||||
|
||||
QEditor(QWidget *p = nullptr);
|
||||
QEditor(bool actions, QWidget *p = nullptr);
|
||||
QEditor(const QString &s, QWidget *p = nullptr);
|
||||
QEditor(const QString &s, bool actions, QWidget *p = nullptr);
|
||||
explicit QEditor(QWidget *p = nullptr);
|
||||
explicit QEditor(bool actions, QWidget *p = nullptr);
|
||||
virtual ~QEditor();
|
||||
|
||||
bool flag(EditFlag) const;
|
||||
|
@ -165,12 +154,10 @@ public:
|
|||
virtual QRect lineRect(const QDocumentLine &l) const;
|
||||
virtual QRect cursorRect(const QDocumentCursor &c) const;
|
||||
|
||||
#ifndef _QMDI_
|
||||
QString name() const;
|
||||
QString fileName() const;
|
||||
|
||||
bool isContentModified() const;
|
||||
#endif
|
||||
|
||||
bool isInConflict() const;
|
||||
|
||||
|
@ -198,11 +185,25 @@ public:
|
|||
point.y() - verticalOffset());
|
||||
}
|
||||
|
||||
void createSimpleBasicContextMenu(bool shortcut, bool extTool);
|
||||
|
||||
virtual bool protectedCursor(const QDocumentCursor &c) const;
|
||||
|
||||
static int defaultFlags();
|
||||
static void setDefaultFlags(int f);
|
||||
|
||||
static QFont defaultFont();
|
||||
static void setDefaultFont(const QFont &font);
|
||||
|
||||
static int defaultTabStop();
|
||||
static void setDefaultTabStop(int tabstop);
|
||||
|
||||
static QDocument::LineEnding defaultLineEnding();
|
||||
static void setDefaultLineEnding(QDocument::LineEnding le);
|
||||
|
||||
static QDocument::WhiteSpaceMode defaultShowSpaces();
|
||||
static void setDefaultShowSpaces(QDocument::WhiteSpaceMode y);
|
||||
|
||||
static QString defaultCodecName();
|
||||
static void setDefaultCodec(const QString &name, int update);
|
||||
|
||||
|
@ -221,9 +222,11 @@ public slots:
|
|||
void undo();
|
||||
void redo();
|
||||
|
||||
void cut();
|
||||
void copy();
|
||||
void paste();
|
||||
virtual void cut();
|
||||
virtual void copy();
|
||||
virtual void paste();
|
||||
|
||||
void clear();
|
||||
|
||||
void selectAll();
|
||||
|
||||
|
@ -248,7 +251,7 @@ public slots:
|
|||
|
||||
virtual void retranslate();
|
||||
|
||||
virtual void write(const QString &s);
|
||||
virtual void write(const QString &s, const QString &sfmtID = {});
|
||||
|
||||
void addAction(QAction *a, const QString &menu,
|
||||
const QString &toolbar = QString());
|
||||
|
@ -274,8 +277,6 @@ public slots:
|
|||
|
||||
void setScaleRate(qreal rate);
|
||||
|
||||
qreal scaleRate() const;
|
||||
|
||||
void setPanelMargins(int l, int t, int r, int b);
|
||||
void getPanelMargins(int *l, int *t, int *r, int *b) const;
|
||||
|
||||
|
@ -285,19 +286,26 @@ public slots:
|
|||
|
||||
void clearPlaceHolders();
|
||||
void removePlaceHolder(int i);
|
||||
void addPlaceHolder(const PlaceHolder &p, bool autoUpdate = true);
|
||||
|
||||
int placeHolderCount() const;
|
||||
int currentPlaceHolder() const;
|
||||
void addPlaceHolder(const QEditor::PlaceHolder &p, bool autoUpdate = true);
|
||||
|
||||
void nextPlaceHolder();
|
||||
void previousPlaceHolder();
|
||||
void setPlaceHolder(int i);
|
||||
|
||||
void setUndoRedoEnabled(bool b);
|
||||
|
||||
void setCursorMirrorEnabled(bool b);
|
||||
|
||||
virtual void setFileName(const QString &f);
|
||||
|
||||
public:
|
||||
int placeHolderCount() const;
|
||||
int currentPlaceHolder() const;
|
||||
qreal scaleRate() const;
|
||||
|
||||
signals:
|
||||
void loaded(QEditor *e, const QString &s);
|
||||
void needLoading();
|
||||
void saved(QEditor *e, const QString &s);
|
||||
|
||||
void contentModified(bool y);
|
||||
|
@ -356,8 +364,6 @@ protected:
|
|||
|
||||
virtual void contextMenuEvent(QContextMenuEvent *e);
|
||||
|
||||
virtual void closeEvent(QCloseEvent *e);
|
||||
|
||||
virtual bool focusNextPrevChild(bool next);
|
||||
|
||||
virtual bool moveKeyEvent(QDocumentCursor &c, QKeyEvent *e, bool *leave);
|
||||
|
@ -377,15 +383,14 @@ public:
|
|||
void pageUp(QDocumentCursor::MoveMode moveMode);
|
||||
void pageDown(QDocumentCursor::MoveMode moveMode);
|
||||
|
||||
void selectionChange(bool force = false);
|
||||
|
||||
void repaintCursor();
|
||||
void ensureCursorVisible();
|
||||
void ensureVisible(int line);
|
||||
void ensureVisible(const QRect &rect);
|
||||
|
||||
void preInsert(QDocumentCursor &c, const QString &text);
|
||||
void insertText(QDocumentCursor &c, const QString &text);
|
||||
void insertText(QDocumentCursor &c, const QString &text,
|
||||
const QString &sfmtID = {});
|
||||
|
||||
QDocumentLine lineAtPosition(const QPoint &p) const;
|
||||
QDocumentCursor cursorForPosition(const QPoint &p) const;
|
||||
|
@ -399,6 +404,17 @@ public:
|
|||
void clearCursorMirrors();
|
||||
void addCursorMirror(const QDocumentCursor &c);
|
||||
|
||||
bool undoRedoEnabled() const;
|
||||
|
||||
bool cursorMirrorEnabled() const;
|
||||
|
||||
private:
|
||||
bool unindent(const QDocumentCursor &cur);
|
||||
|
||||
bool isAutoCloseChar(const QString &ch);
|
||||
|
||||
QString getPairedCloseChar(const QString &ch);
|
||||
|
||||
protected slots:
|
||||
void documentWidthChanged(int newWidth);
|
||||
void documentHeightChanged(int newWidth);
|
||||
|
@ -406,29 +422,21 @@ protected slots:
|
|||
void repaintContent(int i, int n);
|
||||
void updateContent(int i, int n);
|
||||
|
||||
void markChanged(QDocumentLineHandle *l, int mark, bool on);
|
||||
void emitMarkChanged(QDocumentLineHandle *l, int mark, bool on);
|
||||
|
||||
void bindingSelected(QAction *a);
|
||||
|
||||
void lineEndingSelected(QAction *a);
|
||||
void lineEndingChanged(int lineEnding);
|
||||
|
||||
protected:
|
||||
enum SaveState { Undefined, Saving, Saved, Conflict };
|
||||
|
||||
void init(bool actions = true);
|
||||
void updateBindingsMenu();
|
||||
|
||||
#ifndef _QMDI_
|
||||
QString m_name, m_fileName;
|
||||
#endif
|
||||
|
||||
QMenu *pMenu;
|
||||
QHash<QString, QAction *> m_actions;
|
||||
|
||||
QMenu *m_lineEndingsMenu;
|
||||
QActionGroup *m_lineEndingsActions;
|
||||
|
||||
QMenu *m_bindingsMenu;
|
||||
QAction *aDefaultBinding;
|
||||
QActionGroup *m_bindingsActions;
|
||||
|
@ -450,6 +458,9 @@ protected:
|
|||
int m_curPlaceHolder, m_cphOffset;
|
||||
QList<PlaceHolder> m_placeHolders;
|
||||
|
||||
bool m_undoRedoEnabled = true;
|
||||
bool m_cursorMirrorEnabled = true;
|
||||
|
||||
int m_state;
|
||||
bool m_selection;
|
||||
QRect m_crect, m_margins;
|
||||
|
|
|
@ -91,9 +91,7 @@ QStringList QLanguageFactory::fileFilters() const {
|
|||
QStringList l;
|
||||
|
||||
foreach (QString lng, m_languages)
|
||||
l << tr("%1 files (*.%2)")
|
||||
.arg(lng)
|
||||
.arg(m_data[lng].extensions.join(" *."));
|
||||
l << tr("%1 files (*.%2)").arg(lng, m_data[lng].extensions.join(" *."));
|
||||
|
||||
l << tr("All files (*)");
|
||||
|
||||
|
@ -240,18 +238,6 @@ QLanguageFactory::languageData(const QString &lang) {
|
|||
return m_data[lang];
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Registers a new completion engine
|
||||
|
||||
\note This engine will NOT be used if there are no language definition
|
||||
for the language it supports...
|
||||
*/
|
||||
void QLanguageFactory::addLanguageDefinition(QLanguageDefinition *l) {
|
||||
Q_UNUSED(l)
|
||||
|
||||
qWarning("New design does not allow this sorry...");
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Registers a new completion engine
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@ public:
|
|||
|
||||
public slots:
|
||||
void addLanguage(const LangData &d);
|
||||
void addLanguageDefinition(QLanguageDefinition *l);
|
||||
void addCompletionEngine(QCodeCompletionEngine *e);
|
||||
|
||||
virtual void setLanguage(QEditor *e, const QString &f);
|
||||
|
|
|
@ -516,7 +516,7 @@ void QLineMarksInfoCenter::cursorMoved(QEditor *e) {
|
|||
\internal
|
||||
*/
|
||||
void QLineMarksInfoCenter::lineDeleted(QDocumentLineHandle *h) {
|
||||
QLineMarkHandleList::iterator i = m_lineMarks.begin();
|
||||
auto i = m_lineMarks.begin();
|
||||
|
||||
while (i != m_lineMarks.end()) {
|
||||
if (i->line == h) {
|
||||
|
|
|
@ -156,6 +156,8 @@ public slots:
|
|||
void toggleLineMark(const QLineMarkHandle &mark);
|
||||
void removeLineMark(const QLineMarkHandle &mark);
|
||||
|
||||
void lineDeleted(QDocumentLineHandle *h);
|
||||
|
||||
void flush(const QString &file);
|
||||
|
||||
signals:
|
||||
|
@ -168,7 +170,6 @@ protected:
|
|||
|
||||
protected slots:
|
||||
void cursorMoved(QEditor *e);
|
||||
void lineDeleted(QDocumentLineHandle *h);
|
||||
void markChanged(const QString &f, QDocumentLineHandle *h, int mark,
|
||||
bool on);
|
||||
|
||||
|
|
|
@ -20,34 +20,24 @@
|
|||
\brief Implementation of the QPanelLayout class.
|
||||
*/
|
||||
|
||||
#include "qdebug.h"
|
||||
#include "qeditor.h"
|
||||
#include "qpanel.h"
|
||||
|
||||
#include <QScrollBar>
|
||||
#include <QWidget>
|
||||
|
||||
#ifdef Q_WS_WIN
|
||||
// panel position fix required on some systems to work around a bug in
|
||||
// QAbstractScrollArea
|
||||
#define _PANEL_POSITION_FIX_
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\class QPanelLayout
|
||||
\brief A specialized layout taking care of panel display
|
||||
|
||||
The panel layout is specialized in several ways :
|
||||
<ul>
|
||||
<li>It only operates on specific widgets (which inherit QPanel)</li>
|
||||
<li>It can only layout widgets in the viewport margins of a QEditor
|
||||
(could work with any QAbstractScrollArea if a single method was made public
|
||||
instead of protected...) so it does not qualify as a "real" layout (contrary
|
||||
to grid/box layouts)</li> <li>It positions widgets on the border of the
|
||||
editor in the same way the Border Layout example does (most of the layout
|
||||
code actually comes from there).</li> <li>It provides
|
||||
serialization/deserialization of its layout structure</li>
|
||||
</ul>
|
||||
The panel layout is specialized in several ways :
|
||||
<ul>
|
||||
<li>It only operates on specific widgets (which inherit QPanel)</li>
|
||||
<li>It can only layout widgets in the viewport margins of a QEditor (could work
|
||||
with any QAbstractScrollArea if a single method was made public instead of
|
||||
protected...) so it does not qualify as a "real" layout (contrary to grid/box
|
||||
layouts)</li> <li>It positions widgets on the border of the editor in the same
|
||||
way the Border Layout example does (most of the layout code actually comes from
|
||||
there).</li> <li>It provides serialization/deserialization of its layout
|
||||
structure</li>
|
||||
</ul>
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -88,8 +78,8 @@ QString QPanelLayout::serialized() const {
|
|||
/*
|
||||
Scheme :
|
||||
|
||||
QPanelLayout::Position '{' comma-separated list of identifiers '}'
|
||||
*/
|
||||
QPanelLayout::Position '{' comma-separated list of identifiers '}'
|
||||
*/
|
||||
|
||||
QHash<int, QString> posMap;
|
||||
|
||||
|
@ -108,11 +98,12 @@ QString QPanelLayout::serialized() const {
|
|||
} else {
|
||||
QString &ref = posMap[position];
|
||||
|
||||
ref.insert(ref.size() - 2, QString(",") + panel->id());
|
||||
ref.insert(ref.length() - 2, QStringLiteral(",") + panel->id());
|
||||
}
|
||||
}
|
||||
|
||||
return QStringList(posMap.values()).join("");
|
||||
auto values = posMap.values();
|
||||
return values.join("");
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -146,7 +137,8 @@ void QPanelLayout::addSerialized(const QString &layout) {
|
|||
}
|
||||
} else if (layout.at(i) == '{') {
|
||||
inList = true;
|
||||
position = Position(layout.mid(last, i - last).toInt());
|
||||
auto pos = layout.mid(last, i - last);
|
||||
position = Position(pos.toInt());
|
||||
|
||||
// qDebug("position : %i [%s]", position,
|
||||
// qPrintable(layout.mid(last, i - last)));
|
||||
|
@ -233,7 +225,12 @@ QLayoutItem *QPanelLayout::itemAt(int idx) const {
|
|||
QLayoutItem *QPanelLayout::takeAt(int idx) {
|
||||
if ((idx >= 0) && (idx < m_list.size())) {
|
||||
PanelWrapper *layoutStruct = m_list.takeAt(idx);
|
||||
return layoutStruct->item;
|
||||
Q_ASSERT(layoutStruct);
|
||||
if (!layoutStruct)
|
||||
return 0;
|
||||
QLayoutItem *li = layoutStruct->item;
|
||||
delete layoutStruct;
|
||||
return li;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -243,17 +240,14 @@ QLayoutItem *QPanelLayout::takeAt(int idx) {
|
|||
\internal
|
||||
*/
|
||||
void QPanelLayout::setGeometry(const QRect &r) {
|
||||
// qDebug("laying out %i panels", count());
|
||||
#ifdef _PANEL_POSITION_FIX_
|
||||
// qDebug("laying out %i panels", count());
|
||||
|
||||
QScrollBar *vb = m_parent->verticalScrollBar(),
|
||||
*hb = m_parent->horizontalScrollBar();
|
||||
|
||||
QRect rect(r.x(), r.y(),
|
||||
r.width() - (vb->isVisibleTo(m_parent) ? vb->width() : 0),
|
||||
r.height() - (hb->isVisibleTo(m_parent) ? hb->height() : 0));
|
||||
#else
|
||||
QRect rect(r.x(), r.y(), r.width(), r.height());
|
||||
#endif
|
||||
|
||||
int i, eastWidth = 0, westWidth = 0, northHeight = 0, southHeight = 0,
|
||||
centerHeight = 0;
|
||||
|
@ -269,13 +263,17 @@ void QPanelLayout::setGeometry(const QRect &r) {
|
|||
continue;
|
||||
|
||||
if (position == North) {
|
||||
item->setGeometry(QRect(rect.x(), northHeight, rect.width(),
|
||||
item->sizeHint().height()));
|
||||
item->setGeometry(QRect(rect.x(), rect.y() + northHeight,
|
||||
rect.width(), item->sizeHint().height()));
|
||||
|
||||
northHeight += item->geometry().height() + spacing();
|
||||
} else if (position == South) {
|
||||
int ht = item->sizeHint().height();
|
||||
if (item->hasHeightForWidth()) {
|
||||
ht = item->heightForWidth(rect.width());
|
||||
}
|
||||
item->setGeometry(QRect(item->geometry().x(), item->geometry().y(),
|
||||
rect.width(), item->sizeHint().height()));
|
||||
rect.width(), ht));
|
||||
|
||||
southHeight += item->geometry().height() + spacing();
|
||||
|
||||
|
@ -296,7 +294,8 @@ void QPanelLayout::setGeometry(const QRect &r) {
|
|||
continue;
|
||||
|
||||
if (position == West) {
|
||||
item->setGeometry(QRect(rect.x() + westWidth, northHeight,
|
||||
item->setGeometry(QRect(rect.x() + westWidth,
|
||||
rect.y() + northHeight,
|
||||
item->sizeHint().width(), centerHeight));
|
||||
|
||||
westWidth += item->geometry().width() + spacing();
|
||||
|
@ -306,23 +305,15 @@ void QPanelLayout::setGeometry(const QRect &r) {
|
|||
|
||||
eastWidth += item->geometry().width() + spacing();
|
||||
|
||||
item->setGeometry(QRect(
|
||||
rect.x() + rect.width() - eastWidth + spacing(), northHeight,
|
||||
item->geometry().width(), item->geometry().height()));
|
||||
item->setGeometry(
|
||||
QRect(rect.x() + rect.width() - eastWidth + spacing(),
|
||||
rect.y() + northHeight, item->geometry().width(),
|
||||
item->geometry().height()));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if ( center )
|
||||
center->item->setGeometry(QRect(westWidth, northHeight,
|
||||
rect.width()
|
||||
- eastWidth - westWidth, centerHeight));
|
||||
*/
|
||||
// qDebug("{%i, %i, %i, %i}", westWidth, northHeight, eastWidth,
|
||||
// southHeight);
|
||||
|
||||
// qDebug() << westWidth;
|
||||
m_parent->setPanelMargins(westWidth, northHeight, eastWidth, southHeight);
|
||||
m_parent->setPanelMargins(westWidth, rect.y() + northHeight, eastWidth,
|
||||
southHeight);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -69,7 +69,7 @@ void QSnippetManager::removeSnippet(int i, bool cleanup) {
|
|||
|
||||
QSnippet *snip = m_snippets.takeAt(i);
|
||||
|
||||
emit snippetRemoved(i);
|
||||
emit snippetRemovedByIndex(i);
|
||||
emit snippetRemoved(snip);
|
||||
|
||||
if (cleanup)
|
||||
|
@ -84,7 +84,7 @@ void QSnippetManager::removeSnippet(QSnippet *snip) {
|
|||
|
||||
m_snippets.removeAt(idx);
|
||||
|
||||
emit snippetRemoved(idx);
|
||||
emit snippetRemovedByIndex(idx);
|
||||
emit snippetRemoved(snip);
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ QSnippetManager::patternLoader(const QString &type) const {
|
|||
return pl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QSnippetManager::saveSnippetsToDirectory(const QString &path) {
|
||||
|
@ -179,14 +179,9 @@ void QSnippetManager::saveSnippetsToDirectory(const QString &path) {
|
|||
void QSnippetManager::loadSnippetsFromDirectory(const QString &path) {
|
||||
QDir d(path);
|
||||
|
||||
QFileInfoList l = d.entryInfoList(QDir::Files | QDir::Readable);
|
||||
|
||||
foreach (const QFileInfo &info, l) {
|
||||
if (info.suffix() != "qcs")
|
||||
continue;
|
||||
|
||||
// TODO : pattern selection?
|
||||
loadSnippetFromFile(info.absoluteFilePath(), "Simple");
|
||||
for (auto &info : d.entryInfoList({QStringLiteral("*.qcs")},
|
||||
QDir::Files | QDir::Readable)) {
|
||||
loadSnippetFromFile(info.absoluteFilePath(), QStringLiteral("Simple"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class QCE_EXPORT QSnippetManager : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QSnippetManager(QObject *p = 0);
|
||||
explicit QSnippetManager(QObject *p = nullptr);
|
||||
virtual ~QSnippetManager();
|
||||
|
||||
int snippetCount() const;
|
||||
|
@ -57,8 +57,7 @@ public slots:
|
|||
|
||||
signals:
|
||||
void snippetAdded(QSnippet *s);
|
||||
|
||||
void snippetRemoved(int i);
|
||||
void snippetRemovedByIndex(int i);
|
||||
void snippetRemoved(QSnippet *s);
|
||||
|
||||
private:
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
/****************************************************************************
|
||||
/*==============================================================================
|
||||
** Copyright (C) 2024-2027 WingSummer
|
||||
**
|
||||
** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
|
||||
** This program is free software: you can redistribute it and/or modify it under
|
||||
** the terms of the GNU Affero General Public License as published by the Free
|
||||
** Software Foundation, version 3.
|
||||
**
|
||||
** This file is part of the Edyuk project <http://edyuk.org>
|
||||
** This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
** details.
|
||||
**
|
||||
** This file may be used under the terms of the GNU General Public License
|
||||
** version 3 as published by the Free Software Foundation and appearing in the
|
||||
** file GPL.txt included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
****************************************************************************/
|
||||
** You should have received a copy of the GNU Affero General Public License
|
||||
** along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
** =============================================================================
|
||||
*/
|
||||
|
||||
#include "qcalltip.h"
|
||||
|
||||
|
@ -23,6 +25,8 @@
|
|||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
#include <QToolTip>
|
||||
|
||||
/*!
|
||||
\class QCallTip
|
||||
\brief A widget dedicated to calltips display
|
||||
|
@ -32,6 +36,11 @@ QCallTip::QCallTip(QWidget *p) : QWidget(p), m_index(0) {
|
|||
setCursor(Qt::ArrowCursor);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
auto palette = QToolTip::palette();
|
||||
_background = palette.color(QPalette::ToolTipBase);
|
||||
_foreground = palette.color(QPalette::ToolTipText);
|
||||
_borderColor = palette.color(QPalette::Shadow);
|
||||
}
|
||||
|
||||
QCallTip::~QCallTip() {}
|
||||
|
@ -49,6 +58,8 @@ void QCallTip::paintEvent(QPaintEvent *e) {
|
|||
Q_UNUSED(e)
|
||||
|
||||
QPainter p(this);
|
||||
p.setOpacity(_opacity);
|
||||
|
||||
QFontMetrics fm = fontMetrics();
|
||||
|
||||
m_up = m_down = QRect();
|
||||
|
@ -66,21 +77,18 @@ void QCallTip::paintEvent(QPaintEvent *e) {
|
|||
bg.setWidth(bg.width() + arrowWidth);
|
||||
}
|
||||
|
||||
p.fillRect(bg, QColor(0xca, 0xff, 0x70));
|
||||
p.fillRect(bg, _background);
|
||||
// p.drawRect(bg);
|
||||
|
||||
p.save();
|
||||
|
||||
p.setPen(QColor(0x00, 0x00, 0x00));
|
||||
p.setPen(_borderColor);
|
||||
p.drawLine(0, height() - 1, bg.width() - 1, height() - 1);
|
||||
p.drawLine(bg.width() - 1, height() - 1, bg.width() - 1, 0);
|
||||
|
||||
p.setPen(QColor(0xc0, 0xc0, 0xc0));
|
||||
p.drawLine(0, height() - 1, 0, 0);
|
||||
p.drawLine(0, 0, bg.width() - 1, 0);
|
||||
|
||||
p.restore();
|
||||
|
||||
int top = height() / 3, bottom = height() - height() / 3;
|
||||
|
||||
if (bPrev) {
|
||||
|
@ -107,8 +115,11 @@ void QCallTip::paintEvent(QPaintEvent *e) {
|
|||
offset += arrowWidth;
|
||||
}
|
||||
|
||||
p.setPen(_foreground);
|
||||
p.drawText(offset, fm.ascent() + 2, m_tips.at(m_index));
|
||||
|
||||
p.restore();
|
||||
|
||||
setFixedSize(bg.size() + QSize(1, 1));
|
||||
}
|
||||
|
||||
|
@ -124,7 +135,7 @@ void QCallTip::keyPressEvent(QKeyEvent *e) {
|
|||
return;
|
||||
}
|
||||
|
||||
QString prefix, text = e->text();
|
||||
QString text = e->text();
|
||||
|
||||
switch (e->key()) {
|
||||
case Qt::Key_Escape:
|
||||
|
@ -231,3 +242,39 @@ void QCallTip::mousePressEvent(QMouseEvent *e) {
|
|||
}
|
||||
|
||||
void QCallTip::mouseReleaseEvent(QMouseEvent *e) { e->accept(); }
|
||||
|
||||
qreal QCallTip::opacity() const { return _opacity; }
|
||||
|
||||
void QCallTip::setOpacity(qreal newOpacity) {
|
||||
if (qFuzzyCompare(_opacity, newOpacity))
|
||||
return;
|
||||
_opacity = newOpacity;
|
||||
emit opacityChanged();
|
||||
}
|
||||
|
||||
QColor QCallTip::borderColor() const { return _borderColor; }
|
||||
|
||||
void QCallTip::setBorderColor(const QColor &newBorderColor) {
|
||||
if (_borderColor == newBorderColor)
|
||||
return;
|
||||
_borderColor = newBorderColor;
|
||||
emit borderColorChanged();
|
||||
}
|
||||
|
||||
QColor QCallTip::foreground() const { return _foreground; }
|
||||
|
||||
void QCallTip::setForeground(const QColor &newForeground) {
|
||||
if (_foreground == newForeground)
|
||||
return;
|
||||
_foreground = newForeground;
|
||||
emit foregroundChanged();
|
||||
}
|
||||
|
||||
QColor QCallTip::background() const { return _background; }
|
||||
|
||||
void QCallTip::setBackground(const QColor &newBackground) {
|
||||
if (_background == newBackground)
|
||||
return;
|
||||
_background = newBackground;
|
||||
emit backgroundChanged();
|
||||
}
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
/****************************************************************************
|
||||
/*==============================================================================
|
||||
** Copyright (C) 2024-2027 WingSummer
|
||||
**
|
||||
** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
|
||||
** This program is free software: you can redistribute it and/or modify it under
|
||||
** the terms of the GNU Affero General Public License as published by the Free
|
||||
** Software Foundation, version 3.
|
||||
**
|
||||
** This file is part of the Edyuk project <http://edyuk.org>
|
||||
** This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
** details.
|
||||
**
|
||||
** This file may be used under the terms of the GNU General Public License
|
||||
** version 3 as published by the Free Software Foundation and appearing in the
|
||||
** file GPL.txt included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
****************************************************************************/
|
||||
** You should have received a copy of the GNU Affero General Public License
|
||||
** along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
** =============================================================================
|
||||
*/
|
||||
|
||||
#ifndef _QCALL_TIP_H_
|
||||
#define _QCALL_TIP_H_
|
||||
|
@ -26,6 +28,17 @@
|
|||
#include <QWidget>
|
||||
|
||||
class QCE_EXPORT QCallTip : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QColor background READ background WRITE setBackground NOTIFY
|
||||
backgroundChanged FINAL)
|
||||
Q_PROPERTY(QColor foreground READ foreground WRITE setForeground NOTIFY
|
||||
foregroundChanged FINAL)
|
||||
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor NOTIFY
|
||||
borderColorChanged FINAL)
|
||||
Q_PROPERTY(
|
||||
qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL)
|
||||
|
||||
public:
|
||||
QCallTip(QWidget *p = nullptr);
|
||||
virtual ~QCallTip();
|
||||
|
@ -33,18 +46,43 @@ public:
|
|||
QStringList tips() const;
|
||||
void setTips(const QStringList &l);
|
||||
|
||||
QColor background() const;
|
||||
void setBackground(const QColor &newBackground);
|
||||
|
||||
QColor foreground() const;
|
||||
void setForeground(const QColor &newForeground);
|
||||
|
||||
QColor borderColor() const;
|
||||
void setBorderColor(const QColor &newBorderColor);
|
||||
|
||||
qreal opacity() const;
|
||||
void setOpacity(qreal newOpacity);
|
||||
|
||||
signals:
|
||||
void backgroundChanged();
|
||||
void foregroundChanged();
|
||||
void borderColorChanged();
|
||||
|
||||
void opacityChanged();
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *e);
|
||||
virtual void keyPressEvent(QKeyEvent *e);
|
||||
virtual void focusInEvent(QFocusEvent *e);
|
||||
virtual void focusOutEvent(QFocusEvent *e);
|
||||
virtual void mousePressEvent(QMouseEvent *e);
|
||||
virtual void mouseReleaseEvent(QMouseEvent *e);
|
||||
virtual void paintEvent(QPaintEvent *e) override;
|
||||
virtual void keyPressEvent(QKeyEvent *e) override;
|
||||
virtual void focusInEvent(QFocusEvent *e) override;
|
||||
virtual void focusOutEvent(QFocusEvent *e) override;
|
||||
virtual void mousePressEvent(QMouseEvent *e) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
int m_index;
|
||||
QStringList m_tips;
|
||||
QRect m_up, m_down;
|
||||
|
||||
QColor _background;
|
||||
QColor _foreground;
|
||||
QColor _borderColor;
|
||||
|
||||
qreal _opacity = 1;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -63,6 +63,8 @@ QFoldPanel::~QFoldPanel() {}
|
|||
*/
|
||||
QString QFoldPanel::type() const { return "Fold indicators"; }
|
||||
|
||||
QString QFoldPanel::name() const { return tr("Fold Panel"); }
|
||||
|
||||
/*!
|
||||
|
||||
*/
|
||||
|
@ -102,12 +104,12 @@ void QFoldPanel::mousePressEvent(QMouseEvent *e) {
|
|||
/*!
|
||||
|
||||
*/
|
||||
bool QFoldPanel::paint(QPainter *p, QEditor *e) {
|
||||
void QFoldPanel::paint(QPainter *p, QEditor *e) {
|
||||
QDocument *doc = editor()->document();
|
||||
QLanguageDefinition *def = e->languageDefinition();
|
||||
|
||||
if (!def || !doc) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_rects.clear();
|
||||
|
@ -249,8 +251,6 @@ bool QFoldPanel::paint(QPainter *p, QEditor *e) {
|
|||
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QRect QFoldPanel::drawIcon(QPainter *p, QEditor *, int x, int y,
|
||||
|
|
|
@ -37,10 +37,11 @@ public:
|
|||
virtual ~QFoldPanel();
|
||||
|
||||
virtual QString type() const;
|
||||
virtual QString name() const;
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent *e);
|
||||
virtual bool paint(QPainter *p, QEditor *e);
|
||||
virtual void paint(QPainter *p, QEditor *e);
|
||||
|
||||
QRect drawIcon(QPainter *p, QEditor *e, int x, int y, bool expand);
|
||||
|
||||
|
|
|
@ -60,12 +60,14 @@ QLineChangePanel::~QLineChangePanel() {}
|
|||
*/
|
||||
QString QLineChangePanel::type() const { return "Line changes"; }
|
||||
|
||||
QString QLineChangePanel::name() const { return tr("Line Change Panel"); }
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QLineChangePanel::paint(QPainter *p, QEditor *e) {
|
||||
void QLineChangePanel::paint(QPainter *p, QEditor *e) {
|
||||
if (!e || !e->document())
|
||||
return true;
|
||||
return;
|
||||
|
||||
const QFontMetrics fm(e->document()->font());
|
||||
|
||||
|
@ -73,8 +75,6 @@ bool QLineChangePanel::paint(QPainter *p, QEditor *e) {
|
|||
pageBottom = e->viewport()->height(),
|
||||
contentsY = e->verticalOffset();
|
||||
|
||||
QString txt;
|
||||
|
||||
QDocument *d = e->document();
|
||||
n = d->lineNumber(contentsY);
|
||||
posY = 2 + d->y(n) - contentsY;
|
||||
|
@ -99,8 +99,6 @@ bool QLineChangePanel::paint(QPainter *p, QEditor *e) {
|
|||
|
||||
posY += ls * span;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! @} */
|
||||
|
|
|
@ -40,9 +40,10 @@ public:
|
|||
virtual ~QLineChangePanel();
|
||||
|
||||
virtual QString type() const;
|
||||
virtual QString name() const;
|
||||
|
||||
protected:
|
||||
virtual bool paint(QPainter *p, QEditor *e);
|
||||
virtual void paint(QPainter *p, QEditor *e);
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
|
@ -65,9 +65,9 @@ QString QLineMarkPanel::type() const { return "Line marks"; }
|
|||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QLineMarkPanel::paint(QPainter *p, QEditor *e) {
|
||||
void QLineMarkPanel::paint(QPainter *p, QEditor *e) {
|
||||
if (!e || !e->document())
|
||||
return true;
|
||||
return;
|
||||
|
||||
m_rects.clear();
|
||||
m_lines.clear();
|
||||
|
@ -130,7 +130,6 @@ bool QLineMarkPanel::paint(QPainter *p, QEditor *e) {
|
|||
// qDebug("</session>");
|
||||
|
||||
// setFixedWidth(sfm.width(txt) + 5);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -45,7 +45,7 @@ signals:
|
|||
void onToggleMark(int lineIndex);
|
||||
|
||||
protected:
|
||||
virtual bool paint(QPainter *p, QEditor *e);
|
||||
virtual void paint(QPainter *p, QEditor *e);
|
||||
virtual void mousePressEvent(QMouseEvent *e);
|
||||
virtual void mouseReleaseEvent(QMouseEvent *e);
|
||||
virtual void contextMenuEvent(QContextMenuEvent *e);
|
||||
|
|
|
@ -47,6 +47,7 @@ QCE_AUTO_REGISTER(QLineNumberPanel)
|
|||
*/
|
||||
QLineNumberPanel::QLineNumberPanel(QWidget *p) : QPanel(p), m_verbose(false) {
|
||||
setFixedWidth(20);
|
||||
setObjectName("lineNumberPanel");
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -57,7 +58,11 @@ QLineNumberPanel::~QLineNumberPanel() {}
|
|||
/*!
|
||||
|
||||
*/
|
||||
QString QLineNumberPanel::type() const { return "Line numbers"; }
|
||||
QString QLineNumberPanel::type() const {
|
||||
return QStringLiteral("Line numbers");
|
||||
}
|
||||
|
||||
QString QLineNumberPanel::name() const { return tr("Line Number Panel"); }
|
||||
|
||||
/*!
|
||||
|
||||
|
@ -72,6 +77,8 @@ void QLineNumberPanel::setVerboseMode(bool y) {
|
|||
update();
|
||||
}
|
||||
|
||||
void QLineNumberPanel::setFont_slot(const QFont &font) { setFont(font); }
|
||||
|
||||
/*!
|
||||
|
||||
*/
|
||||
|
@ -79,58 +86,70 @@ void QLineNumberPanel::editorChange(QEditor *e) {
|
|||
if (editor()) {
|
||||
disconnect(editor(), SIGNAL(cursorPositionChanged()), this,
|
||||
SLOT(update()));
|
||||
disconnect(editor()->document(), SIGNAL(fontChanged(QFont)), this,
|
||||
SLOT(setFont_slot(QFont)));
|
||||
}
|
||||
|
||||
if (e) {
|
||||
setFixedWidth(
|
||||
QFontMetrics(e->document()->font())
|
||||
.horizontalAdvance(QString::number(e->document()->lines())) +
|
||||
5);
|
||||
setFixedWidth(fontMetrics().horizontalAdvance(
|
||||
QString::number(e->document()->lines())) +
|
||||
5);
|
||||
|
||||
connect(e, SIGNAL(cursorPositionChanged()), this, SLOT(update()));
|
||||
connect(e, &QEditor::zoomed, this, [=] {
|
||||
setFixedWidth(QFontMetrics(e->document()->font())
|
||||
.horizontalAdvance(
|
||||
QString::number(e->document()->lines())) +
|
||||
5);
|
||||
});
|
||||
connect(e, &QEditor::cursorPositionChanged, this,
|
||||
QOverload<>::of(&QLineNumberPanel::update));
|
||||
|
||||
connect(e->document(), &QDocument::fontChanged, this,
|
||||
&QLineNumberPanel::setFont_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
*/
|
||||
bool QLineNumberPanel::paint(QPainter *p, QEditor *e) {
|
||||
void QLineNumberPanel::paint(QPainter *p, QEditor *e) {
|
||||
/*
|
||||
possible Unicode caracter for wrapping arrow :
|
||||
0x21B3
|
||||
0x2937
|
||||
*/
|
||||
possible Unicode caracter for wrapping arrow :
|
||||
0x21B3
|
||||
0x2937
|
||||
*/
|
||||
|
||||
QFont f(font());
|
||||
f.setWeight(QFont::Bold);
|
||||
// f.setWeight(QFont::Bold);
|
||||
const QFontMetrics sfm(f);
|
||||
bool specialFontUsage = false;
|
||||
QFont specialFont(font());
|
||||
|
||||
#ifndef WIN32
|
||||
static const QChar wrappingArrow(0x2937);
|
||||
const QFontMetrics specialSfm(sfm);
|
||||
#if QT_VERSION >= 0x050000 && defined Q_OS_MAC && (QT_VERSION < 0x050200)
|
||||
if (!specialSfm.inFont(wrappingArrow)) {
|
||||
specialFontUsage = true;
|
||||
specialFont.setFamily("Gothic Regular");
|
||||
// specialSfm(specialFont);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
// 0xC4 gives a decent wrapping arrow in Wingdings fonts, availables on all
|
||||
// windows systems this is a hackish fallback to workaround Windows issues
|
||||
// with Unicode...
|
||||
// windows systems
|
||||
// this is a hackish fallback to workaround Windows issues with Unicode...
|
||||
static const QChar wrappingArrow(0xC4);
|
||||
QFont specialFont(font());
|
||||
specialFont.setFamily("Wingdings");
|
||||
const QFontMetrics specialSfm(specialFont);
|
||||
specialFontUsage = true;
|
||||
#endif
|
||||
|
||||
const int max = e->document()->lines();
|
||||
const int panelWidth = sfm.horizontalAdvance(QString::number(max)) + 5;
|
||||
int max = e->document()->lines();
|
||||
if (max < 100)
|
||||
max = 100; // always reserve 3 line number columns to avoid ugly jumping
|
||||
// of width
|
||||
QString s_width = QString::number(max);
|
||||
s_width.fill('6');
|
||||
const int panelWidth = sfm.horizontalAdvance(s_width) + 5;
|
||||
setFixedWidth(panelWidth);
|
||||
|
||||
const QFontMetrics fm(e->document()->font());
|
||||
|
||||
int n, posY, as = fm.ascent(), ls = fm.lineSpacing(),
|
||||
int n, posY, as = QFontMetrics(e->document()->font()).ascent(),
|
||||
ls = e->document()->lineSpacing(),
|
||||
pageBottom = e->viewport()->height(),
|
||||
contentsY = e->verticalOffset();
|
||||
|
||||
|
@ -144,12 +163,6 @@ bool QLineNumberPanel::paint(QPainter *p, QEditor *e) {
|
|||
// qDebug("first = %i; last = %i", first, last);
|
||||
// qDebug("beg pos : %i", posY);
|
||||
|
||||
p->save();
|
||||
f = p->font();
|
||||
f.setPointSize(d->font().pointSize());
|
||||
|
||||
p->setFont(f);
|
||||
|
||||
for (;; ++n) {
|
||||
// qDebug("n = %i; pos = %i", n, posY);
|
||||
QDocumentLine line = d->line(n);
|
||||
|
@ -163,22 +176,42 @@ bool QLineNumberPanel::paint(QPainter *p, QEditor *e) {
|
|||
bool draw = true;
|
||||
|
||||
if (!m_verbose) {
|
||||
draw = !((n + 1) % 10) || !n || line.marks().count();
|
||||
draw = !((n + 1) % 10) || !n || !line.marks().empty();
|
||||
}
|
||||
|
||||
txt = QString::number(n + 1);
|
||||
|
||||
if (n == cursorLine) {
|
||||
draw = true;
|
||||
|
||||
p->save();
|
||||
auto f = p->font();
|
||||
QFont f = p->font();
|
||||
f.setBold(true);
|
||||
f.setUnderline(true);
|
||||
p->setFont(f);
|
||||
}
|
||||
|
||||
if (draw) {
|
||||
p->drawText(width() - 2 - sfm.horizontalAdvance(txt), posY, txt);
|
||||
if (specialFontUsage) {
|
||||
if (line.lineSpan() > 1) {
|
||||
p->save();
|
||||
specialFont.setBold(n ==
|
||||
cursorLine); // todo: only get bold on
|
||||
// the current wrapped line
|
||||
p->setFont(specialFont);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < line.lineSpan(); ++i)
|
||||
p->drawText(width() - 2 -
|
||||
specialSfm.horizontalAdvance(wrappingArrow),
|
||||
posY + i * ls, wrappingArrow);
|
||||
|
||||
if (specialFontUsage) {
|
||||
if (line.lineSpan() > 1)
|
||||
p->restore();
|
||||
}
|
||||
} else {
|
||||
int yOff = posY - (as + 1) + ls / 2;
|
||||
|
||||
|
@ -188,30 +221,6 @@ bool QLineNumberPanel::paint(QPainter *p, QEditor *e) {
|
|||
p->drawLine(width() - 7, yOff, width() - 2, yOff);
|
||||
}
|
||||
|
||||
if (line.lineSpan() > 1) {
|
||||
#ifdef Q_OS_WIN32
|
||||
p->save();
|
||||
specialFont.setBold(
|
||||
n ==
|
||||
cursorLine); // todo: only get bold on the current wrapped line
|
||||
specialFont.setPointSize(d->font().pointSize());
|
||||
p->setFont(specialFont);
|
||||
#endif
|
||||
|
||||
for (int i = 1; i < line.lineSpan(); ++i) {
|
||||
// draw line wrapping indicators
|
||||
// p->drawText(width() - 2 - sfm.width(wrappingArrow), posY + i
|
||||
// * ls, wrappingArrow);
|
||||
p->drawText(width() - 1 -
|
||||
specialSfm.horizontalAdvance(wrappingArrow),
|
||||
posY + i * ls, wrappingArrow);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
p->restore();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (n == cursorLine) {
|
||||
p->restore();
|
||||
}
|
||||
|
@ -219,13 +228,12 @@ bool QLineNumberPanel::paint(QPainter *p, QEditor *e) {
|
|||
posY += ls * line.lineSpan();
|
||||
}
|
||||
|
||||
p->restore();
|
||||
|
||||
// p->setPen(Qt::DotLine);
|
||||
// p->drawLine(width()-1, 0, width()-1, pageBottom);
|
||||
|
||||
// setFixedWidth(sfm.width(txt) + 5);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! @} */
|
||||
|
||||
QSize QLineNumberPanel::sizeHint() const { return QSize(_fixWidth, 0); }
|
||||
|
|
|
@ -37,16 +37,22 @@ public:
|
|||
bool isVerboseMode() const;
|
||||
|
||||
virtual QString type() const;
|
||||
virtual QString name() const;
|
||||
|
||||
virtual QSize sizeHint() const;
|
||||
|
||||
public slots:
|
||||
void setVerboseMode(bool y);
|
||||
|
||||
void setFont_slot(const QFont &font);
|
||||
|
||||
protected:
|
||||
virtual void editorChange(QEditor *e);
|
||||
virtual bool paint(QPainter *p, QEditor *e);
|
||||
virtual void paint(QPainter *p, QEditor *e);
|
||||
|
||||
bool m_verbose;
|
||||
int mutable m_minWidth = 0;
|
||||
int _fixWidth = 0;
|
||||
};
|
||||
|
||||
#endif // _QLINE_NUMBER_PANEL_H_
|
||||
|
|
|
@ -88,6 +88,8 @@ QPanel::~QPanel() {
|
|||
// qDebug("Panels cleared.");
|
||||
}
|
||||
|
||||
QString QPanel::name() const { return type(); }
|
||||
|
||||
/*!
|
||||
*/
|
||||
QEditor *QPanel::editor() { return m_editor; }
|
||||
|
@ -107,7 +109,7 @@ void QPanel::attach(QEditor *e) {
|
|||
this, SLOT(update()));
|
||||
}
|
||||
|
||||
editorChange(e);
|
||||
QPanel::editorChange(e);
|
||||
|
||||
m_editor = e;
|
||||
setParent(e);
|
||||
|
@ -243,6 +245,8 @@ void QPanel::hideEvent(QHideEvent *e) {
|
|||
\internal
|
||||
*/
|
||||
void QPanel::paintEvent(QPaintEvent *e) {
|
||||
QWidget::paintEvent(e);
|
||||
|
||||
if (!m_editor || !m_editor->document()) {
|
||||
e->ignore();
|
||||
return;
|
||||
|
@ -251,22 +255,12 @@ void QPanel::paintEvent(QPaintEvent *e) {
|
|||
e->accept();
|
||||
|
||||
QPainter p(this);
|
||||
|
||||
if (!paint(&p, m_editor))
|
||||
QWidget::paintEvent(e);
|
||||
paint(&p, m_editor);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QPanel::paint(QPainter *, QEditor *) {
|
||||
/*
|
||||
qWarning("Bad panel implementation : "
|
||||
"QPanel::paint(QPainter*, QEditor*)"
|
||||
" is a stub that should not get called."
|
||||
"\nCheck out the code of %s", qPrintable(type()));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
void QPanel::paint(QPainter *, QEditor *) {}
|
||||
|
||||
/* @} */
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
|
||||
virtual QString id() const = 0;
|
||||
virtual QString type() const = 0;
|
||||
virtual QString name() const;
|
||||
|
||||
QEditor *editor();
|
||||
void attach(QEditor *e);
|
||||
|
@ -70,7 +71,7 @@ protected:
|
|||
virtual void showEvent(QShowEvent *e);
|
||||
virtual void hideEvent(QHideEvent *e);
|
||||
virtual void paintEvent(QPaintEvent *e);
|
||||
virtual bool paint(QPainter *p, QEditor *e);
|
||||
virtual void paint(QPainter *p, QEditor *e);
|
||||
|
||||
private:
|
||||
QPointer<QEditor> m_editor;
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
|
||||
**
|
||||
** This file is part of the Edyuk project <http://edyuk.org>
|
||||
**
|
||||
** This file may be used under the terms of the GNU General Public License
|
||||
** version 3 as published by the Free Software Foundation and appearing in the
|
||||
** file GPL.txt included in the packaging of this file.
|
||||
**
|
||||
** 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 "qsimplecolorpicker.h"
|
||||
|
||||
/*!
|
||||
\file qsimplecolorpicker.cpp
|
||||
\brief Implementation of the QSimpleColorPicker class
|
||||
*/
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QIcon>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QResizeEvent>
|
||||
|
||||
QSimpleColorPicker::QSimpleColorPicker(QWidget *w) : QToolButton(w) {
|
||||
setColor(QColor());
|
||||
|
||||
connect(this, SIGNAL(clicked()), this, SLOT(clicked()));
|
||||
}
|
||||
|
||||
QSimpleColorPicker::QSimpleColorPicker(const QColor &c, QWidget *w)
|
||||
: QToolButton(w) {
|
||||
setColor(c);
|
||||
|
||||
connect(this, SIGNAL(clicked()), this, SLOT(clicked()));
|
||||
}
|
||||
|
||||
const QColor &QSimpleColorPicker::color() const { return m_color; }
|
||||
|
||||
void QSimpleColorPicker::setColor(const QColor &c) {
|
||||
m_color = c;
|
||||
|
||||
updateIcon(size());
|
||||
}
|
||||
|
||||
void QSimpleColorPicker::resizeEvent(QResizeEvent *e) {
|
||||
updateIcon(e->size());
|
||||
|
||||
QToolButton::resizeEvent(e);
|
||||
}
|
||||
|
||||
void QSimpleColorPicker::contextMenuEvent(QContextMenuEvent *e) {
|
||||
setColor(QColor());
|
||||
|
||||
e->accept();
|
||||
|
||||
QToolButton::contextMenuEvent(e);
|
||||
}
|
||||
|
||||
void QSimpleColorPicker::updateIcon(const QSize &sz) {
|
||||
QPixmap px(sz.width() - 3, sz.height() - 3);
|
||||
QPainter p(&px);
|
||||
|
||||
if (m_color.isValid()) {
|
||||
p.fillRect(0, 0, px.width(), px.height(), m_color);
|
||||
setIcon(QIcon(px));
|
||||
} else {
|
||||
// p.fillRect(0, 0, px.width(), px.height(), palette().window());
|
||||
setIcon(QIcon());
|
||||
}
|
||||
}
|
||||
|
||||
void QSimpleColorPicker::clicked() {
|
||||
QColor c = QColorDialog::getColor(m_color);
|
||||
|
||||
if (c.isValid()) {
|
||||
setColor(c);
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
|
||||
**
|
||||
** This file is part of the Edyuk project <http://edyuk.org>
|
||||
**
|
||||
** This file may be used under the terms of the GNU General Public License
|
||||
** version 3 as published by the Free Software Foundation and appearing in the
|
||||
** file GPL.txt included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _QSIMPLE_COLOR_PICKER_H_
|
||||
#define _QSIMPLE_COLOR_PICKER_H_
|
||||
|
||||
#include "qce-config.h"
|
||||
|
||||
/*!
|
||||
\file qsimplecolorpicker.h
|
||||
\brief Definition of the QSimpleColorPicker class
|
||||
*/
|
||||
|
||||
#include <QToolButton>
|
||||
|
||||
class QCE_EXPORT QSimpleColorPicker : public QToolButton {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QColor color READ color WRITE setColor)
|
||||
|
||||
public:
|
||||
QSimpleColorPicker(QWidget *w = 0);
|
||||
QSimpleColorPicker(const QColor &c, QWidget *w = 0);
|
||||
|
||||
const QColor &color() const;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
void contextMenuEvent(QContextMenuEvent *e);
|
||||
|
||||
public slots:
|
||||
void setColor(const QColor &c);
|
||||
|
||||
void updateIcon(const QSize &sz);
|
||||
|
||||
private slots:
|
||||
void clicked();
|
||||
|
||||
private:
|
||||
QColor m_color;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,50 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.6)
|
||||
project(QCodeModel2 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Gui)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Gui)
|
||||
|
||||
add_definitions(-D_QCODE_MODEL_BUILD_STATIC_)
|
||||
|
||||
add_library(
|
||||
QCodeModel2 STATIC
|
||||
qcm-config.h
|
||||
qcodemodel.h
|
||||
qcodeproxymodel.h
|
||||
qcodenode.h
|
||||
qcodenodepool.h
|
||||
qsourcecodewatcher.h
|
||||
qcodeview.h
|
||||
qcodeserializer.h
|
||||
qcodestream.h
|
||||
qcodedevice.h
|
||||
qcodebuffer.h
|
||||
qcodelexer.h
|
||||
qcodeparser.h
|
||||
qcodeloader.h
|
||||
qcodemodel.cpp
|
||||
qcodeproxymodel.cpp
|
||||
qcodenode.cpp
|
||||
qcodenodepool.cpp
|
||||
qsourcecodewatcher.cpp
|
||||
qcodeview.cpp
|
||||
qcodeserializer.cpp
|
||||
qcodestream.cpp
|
||||
qcodedevice.cpp
|
||||
qcodebuffer.cpp
|
||||
qcodelexer.cpp
|
||||
qcodeparser.cpp
|
||||
qcodeloader.cpp)
|
||||
|
||||
target_include_directories(QCodeModel2 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
target_link_libraries(QCodeModel2 PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::Gui)
|
||||
|
||||
target_compile_definitions(QCodeModel2 PUBLIC -D_QCODE_MODEL_BUILD_STATIC_)
|
|
@ -1,46 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
|
||||
**
|
||||
** This file is part of the Edyuk project <http://edyuk.org>
|
||||
**
|
||||
** This file may be used under the terms of the GNU General Public License
|
||||
** version 3 as published by the Free Software Foundation and appearing in the
|
||||
** file GPL.txt included in the packaging of this file.
|
||||
**
|
||||
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _QCM_CONFIG_H_
|
||||
#define _QCM_CONFIG_H_
|
||||
|
||||
#include <qglobal.h>
|
||||
|
||||
/*!
|
||||
\macro QCM_EXPORT
|
||||
Allow a cross-platform and sure possibility to link in static or dynamic
|
||||
way, depending to what's needed...
|
||||
*/
|
||||
#ifdef _QCODE_MODEL_BUILD_DLL_
|
||||
#if (defined(QT_SHARED) || defined(QT_DLL)) && !defined(QT_PLUGIN)
|
||||
#define QCM_EXPORT Q_DECL_EXPORT
|
||||
#endif
|
||||
#else
|
||||
#ifdef _QCODE_MODEL_BUILD_STATIC_
|
||||
#define QCM_EXPORT
|
||||
#else
|
||||
#define QCM_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class QByteArray;
|
||||
|
||||
template <typename T>
|
||||
class QList;
|
||||
|
||||
typedef QByteArray QToken;
|
||||
typedef QList<QToken> QTokenList;
|
||||
|
||||
#endif // _QCM_CONFIG_H_
|
|
@ -1,84 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
|
||||
**
|
||||
** This file is part of the Edyuk project <http://edyuk.org>
|
||||
**
|
||||
** This file may be used under the terms of the GNU General Public License
|
||||
** version 3 as published by the Free Software Foundation and appearing in the
|
||||
** file GPL.txt included in the packaging of this file.
|
||||
**
|
||||
** 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 "qcodebuffer.h"
|
||||
|
||||
/*!
|
||||
\file qcodebuffer.cpp
|
||||
\brief Implementation of the QCodebuffer class.
|
||||
*/
|
||||
|
||||
#include <QString>
|
||||
|
||||
/*!
|
||||
\class QCodebuffer
|
||||
\brief Code stream that operates on a static string buffer
|
||||
*/
|
||||
|
||||
/*!
|
||||
\brief Constructs a code buffer from an Unicode string
|
||||
|
||||
\note The string is converted to local8Bit()
|
||||
*/
|
||||
QCodeBuffer::QCodeBuffer(const QString &s)
|
||||
: QCodeStream(), iPos(0), sBuffer(s.toLocal8Bit()) {
|
||||
sBuffer.replace("\r\n", "\n");
|
||||
sBuffer.replace("\r", "\n");
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Constructs a code buffer from a byte array
|
||||
*/
|
||||
QCodeBuffer::QCodeBuffer(const QByteArray &s)
|
||||
: QCodeStream(), iPos(0), sBuffer(s) {
|
||||
sBuffer.replace("\r\n", "\n");
|
||||
sBuffer.replace("\r", "\n");
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief return the char pointed to by the internal pointer and update the
|
||||
pointer
|
||||
*/
|
||||
char QCodeBuffer::getChar() {
|
||||
if (iPos < sBuffer.length())
|
||||
return sBuffer.at(iPos++);
|
||||
else
|
||||
return '\0';
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Move back by one character
|
||||
*/
|
||||
void QCodeBuffer::ungetChar(char) {
|
||||
if (iPos > 0)
|
||||
--iPos;
|
||||
}
|
||||
|
||||
/*!
|
||||
\return the content of the current line
|
||||
*/
|
||||
QByteArray QCodeBuffer::readLine() {
|
||||
char c;
|
||||
QByteArray s;
|
||||
|
||||
while ((c = getChar())) {
|
||||
if (c == '\n')
|
||||
break;
|
||||
|
||||
s += c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue