750 lines
27 KiB
C++
750 lines
27 KiB
C++
//=============================================================================
|
|
// MuseScore
|
|
// Music Composition & Notation
|
|
//
|
|
// Copyright (C) 2018 Werner Schweer
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2
|
|
// as published by the Free Software Foundation and appearing in
|
|
// the file LICENCE.GPL
|
|
//=============================================================================
|
|
|
|
#include "textedit.h"
|
|
#include "score.h"
|
|
|
|
namespace Ms {
|
|
|
|
//---------------------------------------------------------
|
|
// editInsertText
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::editInsertText(TextCursor* cursor, const QString& s)
|
|
{
|
|
Q_ASSERT(!layoutInvalid);
|
|
textInvalid = true;
|
|
|
|
int col = 0;
|
|
for (const QChar& c : s) {
|
|
if (!c.isHighSurrogate())
|
|
++col;
|
|
}
|
|
cursor->curLine().insert(cursor, s);
|
|
cursor->setColumn(cursor->column() + col);
|
|
cursor->clearSelection();
|
|
|
|
triggerLayout();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// startEdit
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::startEdit(EditData& ed)
|
|
{
|
|
ed.grips = 0;
|
|
TextEditData* ted = new TextEditData(this);
|
|
ted->e = this;
|
|
ted->cursor.setRow(0);
|
|
ted->cursor.setColumn(0);
|
|
ted->cursor.clearSelection();
|
|
|
|
Q_ASSERT(!score()->undoStack()->active()); // make sure we are not in a Cmd
|
|
|
|
ted->oldXmlText = xmlText();
|
|
ted->startUndoIdx = score()->undoStack()->getCurIdx();
|
|
|
|
if (layoutInvalid)
|
|
layout();
|
|
if (!ted->cursor.set(ed.startMove))
|
|
ted->cursor.init();
|
|
qreal _spatium = spatium();
|
|
// refresh edit bounding box
|
|
score()->addRefresh(canvasBoundingRect().adjusted(-_spatium, -_spatium, _spatium, _spatium));
|
|
ed.addData(ted);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// endEdit
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::endEdit(EditData& ed)
|
|
{
|
|
TextEditData* ted = static_cast<TextEditData*>(ed.getData(this));
|
|
const QString actualText = xmlText();
|
|
UndoStack* undo = score()->undoStack();
|
|
while (undo->getCurIdx() > ted->startUndoIdx)
|
|
undo->undo(&ed);
|
|
|
|
// replace all undo/redo records collected during text editing with
|
|
// one property change
|
|
|
|
if (ted->oldXmlText.isEmpty()) {
|
|
UndoStack* us = score()->undoStack();
|
|
UndoCommand* ucmd = us->last();
|
|
if (ucmd) {
|
|
const QList<UndoCommand*>& cl = ucmd->commands();
|
|
const UndoCommand* cmd = cl.back();
|
|
if (strncmp(cmd->name(), "Add:", 4) == 0) {
|
|
const AddElement* ae = static_cast<const AddElement*>(cmd);
|
|
if (ae->getElement() == this) {
|
|
if (actualText.isEmpty()) {
|
|
// we just created this empty text, rollback that operation
|
|
us->rollback();
|
|
score()->update();
|
|
ed.element = 0;
|
|
}
|
|
else {
|
|
us->reopen();
|
|
// combine undo records of text creation with text editing
|
|
undoChangeProperty(Pid::TEXT, actualText);
|
|
layout1();
|
|
score()->endCmd();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (actualText.isEmpty()) {
|
|
qDebug("actual text is empty");
|
|
score()->startCmd();
|
|
score()->undoRemoveElement(this);
|
|
ed.element = 0;
|
|
score()->endCmd();
|
|
return;
|
|
}
|
|
score()->startCmd();
|
|
undoChangeProperty(Pid::TEXT, actualText); // change property to set text to actual value again
|
|
// this also changes text of linked elements
|
|
layout1();
|
|
triggerLayout(); // force relayout even if text did not change
|
|
score()->endCmd();
|
|
|
|
static const qreal w = 2.0;
|
|
score()->addRefresh(canvasBoundingRect().adjusted(-w, -w, w, w));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// insertSym
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::insertSym(EditData& ed, SymId id)
|
|
{
|
|
TextEditData* ted = static_cast<TextEditData*>(ed.getData(this));
|
|
TextCursor* _cursor = &ted->cursor;
|
|
|
|
deleteSelectedText(ed);
|
|
QString s = score()->scoreFont()->toString(id);
|
|
CharFormat fmt = *_cursor->format(); // save format
|
|
// uint code = ScoreFont::fallbackFont()->sym(id).code();
|
|
_cursor->format()->setFontFamily("ScoreText");
|
|
_cursor->format()->setBold(false);
|
|
_cursor->format()->setItalic(false);
|
|
score()->undo(new InsertText(_cursor, s), &ed);
|
|
_cursor->setFormat(fmt); // restore format
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// insertText
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::insertText(EditData& ed, const QString& s)
|
|
{
|
|
TextEditData* ted = static_cast<TextEditData*>(ed.getData(this));
|
|
TextCursor* _cursor = &ted->cursor;
|
|
score()->undo(new InsertText(_cursor, s), &ed);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// edit
|
|
//---------------------------------------------------------
|
|
|
|
bool TextBase::edit(EditData& ed)
|
|
{
|
|
TextEditData* ted = static_cast<TextEditData*>(ed.getData(this));
|
|
TextCursor* _cursor = &ted->cursor;
|
|
|
|
// do nothing on Shift, it messes up IME on Windows. See #64046
|
|
if (ed.key == Qt::Key_Shift)
|
|
return false;
|
|
QString s = ed.s;
|
|
bool ctrlPressed = ed.modifiers & Qt::ControlModifier;
|
|
bool shiftPressed = ed.modifiers & Qt::ShiftModifier;
|
|
|
|
QTextCursor::MoveMode mm = shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor;
|
|
|
|
bool wasHex = false;
|
|
if (hexState >= 0) {
|
|
if (ed.modifiers == (Qt::ControlModifier | Qt::ShiftModifier | Qt::KeypadModifier)) {
|
|
switch (ed.key) {
|
|
case Qt::Key_0:
|
|
case Qt::Key_1:
|
|
case Qt::Key_2:
|
|
case Qt::Key_3:
|
|
case Qt::Key_4:
|
|
case Qt::Key_5:
|
|
case Qt::Key_6:
|
|
case Qt::Key_7:
|
|
case Qt::Key_8:
|
|
case Qt::Key_9:
|
|
s = QChar::fromLatin1(ed.key);
|
|
++hexState;
|
|
wasHex = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ed.modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) {
|
|
switch (ed.key) {
|
|
case Qt::Key_A:
|
|
case Qt::Key_B:
|
|
case Qt::Key_C:
|
|
case Qt::Key_D:
|
|
case Qt::Key_E:
|
|
case Qt::Key_F:
|
|
s = QChar::fromLatin1(ed.key);
|
|
++hexState;
|
|
wasHex = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!wasHex) {
|
|
//printf("======%x\n", s.isEmpty() ? -1 : s[0].unicode());
|
|
|
|
switch (ed.key) {
|
|
case Qt::Key_Z: // happens when the undo stack is empty
|
|
if (ed.modifiers == Qt::ControlModifier)
|
|
return true;
|
|
break;
|
|
|
|
case Qt::Key_Enter:
|
|
case Qt::Key_Return:
|
|
deleteSelectedText(ed);
|
|
score()->undo(new SplitText(_cursor), &ed);
|
|
return true;
|
|
|
|
case Qt::Key_Delete:
|
|
if (!deleteSelectedText(ed))
|
|
score()->undo(new RemoveText(_cursor, QString(_cursor->currentCharacter())), &ed);
|
|
return true;
|
|
|
|
case Qt::Key_Backspace:
|
|
if (!deleteSelectedText(ed)) {
|
|
if (_cursor->column() == 0 && _cursor->row() != 0)
|
|
score()->undo(new JoinText(_cursor), &ed);
|
|
else {
|
|
if (!_cursor->movePosition(QTextCursor::Left))
|
|
return false;
|
|
score()->undo(new RemoveText(_cursor, QString(_cursor->currentCharacter())), &ed);
|
|
}
|
|
}
|
|
return true;
|
|
|
|
case Qt::Key_Left:
|
|
if (!_cursor->movePosition(ctrlPressed ? QTextCursor::WordLeft : QTextCursor::Left, mm) && type() == ElementType::LYRICS)
|
|
return false;
|
|
s.clear();
|
|
break;
|
|
|
|
case Qt::Key_Right:
|
|
if (!_cursor->movePosition(ctrlPressed ? QTextCursor::NextWord : QTextCursor::Right, mm) && type() == ElementType::LYRICS)
|
|
return false;
|
|
s.clear();
|
|
break;
|
|
|
|
case Qt::Key_Up:
|
|
#if defined(Q_OS_MAC)
|
|
if (!_cursor->movePosition(QTextCursor::Up, mm))
|
|
_cursor->movePosition(QTextCursor::StartOfLine, mm);
|
|
#else
|
|
_cursor->movePosition(QTextCursor::Up, mm);
|
|
#endif
|
|
s.clear();
|
|
break;
|
|
|
|
case Qt::Key_Down:
|
|
#if defined(Q_OS_MAC)
|
|
if (!_cursor->movePosition(QTextCursor::Down, mm))
|
|
_cursor->movePosition(QTextCursor::EndOfLine, mm);
|
|
#else
|
|
_cursor->movePosition(QTextCursor::Down, mm);
|
|
#endif
|
|
s.clear();
|
|
break;
|
|
|
|
case Qt::Key_Home:
|
|
if (ctrlPressed)
|
|
_cursor->movePosition(QTextCursor::Start, mm);
|
|
else
|
|
_cursor->movePosition(QTextCursor::StartOfLine, mm);
|
|
|
|
s.clear();
|
|
break;
|
|
|
|
case Qt::Key_End:
|
|
if (ctrlPressed)
|
|
_cursor->movePosition(QTextCursor::End, mm);
|
|
else
|
|
_cursor->movePosition(QTextCursor::EndOfLine, mm);
|
|
|
|
s.clear();
|
|
break;
|
|
|
|
case Qt::Key_Tab:
|
|
s = " ";
|
|
ed.modifiers = 0;
|
|
break;
|
|
|
|
case Qt::Key_Space:
|
|
if (ed.modifiers & CONTROL_MODIFIER) {
|
|
s = QString(QChar(0xa0)); // non-breaking space
|
|
}
|
|
else {
|
|
if (isFingering() && ed.view) {
|
|
score()->endCmd();
|
|
ed.view->textTab(ed.modifiers & Qt::ShiftModifier);
|
|
return true;
|
|
}
|
|
s = " ";
|
|
}
|
|
ed.modifiers = 0;
|
|
break;
|
|
|
|
case Qt::Key_Minus:
|
|
if (ed.modifiers == 0)
|
|
s = "-";
|
|
break;
|
|
|
|
case Qt::Key_Underscore:
|
|
if (ed.modifiers == 0)
|
|
s = "_";
|
|
break;
|
|
|
|
case Qt::Key_A:
|
|
if (ctrlPressed) {
|
|
selectAll(_cursor);
|
|
s.clear();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (ctrlPressed && shiftPressed) {
|
|
switch (ed.key) {
|
|
case Qt::Key_U:
|
|
if (hexState == -1) {
|
|
hexState = 0;
|
|
s = "u";
|
|
}
|
|
break;
|
|
case Qt::Key_B:
|
|
s = "\u266d"; // Unicode flat
|
|
break;
|
|
case Qt::Key_NumberSign:
|
|
s = "\u266f"; // Unicode sharp
|
|
break;
|
|
case Qt::Key_H:
|
|
s = "\u266e"; // Unicode natural
|
|
break;
|
|
case Qt::Key_Space:
|
|
insertSym(ed, SymId::space);
|
|
return true;
|
|
case Qt::Key_F:
|
|
insertSym(ed, SymId::dynamicForte);
|
|
return true;
|
|
case Qt::Key_M:
|
|
insertSym(ed, SymId::dynamicMezzo);
|
|
return true;
|
|
case Qt::Key_N:
|
|
insertSym(ed, SymId::dynamicNiente);
|
|
return true;
|
|
case Qt::Key_P:
|
|
insertSym(ed, SymId::dynamicPiano);
|
|
return true;
|
|
case Qt::Key_S:
|
|
insertSym(ed, SymId::dynamicSforzando);
|
|
return true;
|
|
case Qt::Key_R:
|
|
insertSym(ed, SymId::dynamicRinforzando);
|
|
return true;
|
|
case Qt::Key_Z:
|
|
// Ctrl+Z is normally "undo"
|
|
// but this code gets hit even if you are also holding Shift
|
|
// so Shift+Ctrl+Z works
|
|
insertSym(ed, SymId::dynamicZ);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (!s.isEmpty()) {
|
|
deleteSelectedText(ed);
|
|
score()->undo(new InsertText(_cursor, s), &ed);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// movePosition
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::movePosition(EditData& ed, QTextCursor::MoveOperation op)
|
|
{
|
|
TextEditData* ted = static_cast<TextEditData*>(ed.getData(this));
|
|
TextCursor* _cursor = &ted->cursor;
|
|
_cursor->movePosition(op);
|
|
score()->addRefresh(canvasBoundingRect());
|
|
score()->update();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// ChangeText::insertText
|
|
//---------------------------------------------------------
|
|
|
|
void ChangeText::insertText(EditData* ed)
|
|
{
|
|
TextCursor tc = c;
|
|
c.text()->editInsertText(&tc, s);
|
|
if (ed) {
|
|
TextCursor* ttc = c.text()->cursor(*ed);
|
|
*ttc = tc;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// ChangeText::removeText
|
|
//---------------------------------------------------------
|
|
|
|
void ChangeText::removeText(EditData* ed)
|
|
{
|
|
TextCursor tc = c;
|
|
TextBlock& l = c.curLine();
|
|
int column = c.column();
|
|
|
|
for (int n = 0; n < s.size(); ++n)
|
|
l.remove(column);
|
|
c.text()->triggerLayout();
|
|
if (ed)
|
|
*c.text()->cursor(*ed) = tc;
|
|
c.text()->setTextInvalid();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// SplitJoinText
|
|
//---------------------------------------------------------
|
|
|
|
void SplitJoinText::join(EditData* ed)
|
|
{
|
|
TextBase* t = c.text();
|
|
int line = c.row();
|
|
t->setTextInvalid();
|
|
t->triggerLayout();
|
|
|
|
CharFormat* charFmt = c.format(); // take current format
|
|
int col = t->textBlock(line-1).columns();
|
|
int eol = t->textBlock(line).eol();
|
|
t->textBlock(line-1).fragments().append(t->textBlock(line).fragments());
|
|
int lines = t->rows();
|
|
if (line < lines)
|
|
t->textBlock(line).setEol(eol);
|
|
t->textBlockList().removeAt(line);
|
|
c.setRow(line-1);
|
|
c.setColumn(col);
|
|
c.setFormat(*charFmt); // restore orig. format at new line
|
|
c.clearSelection();
|
|
if (ed)
|
|
*t->cursor(*ed) = c;
|
|
c.text()->setTextInvalid();
|
|
}
|
|
|
|
void SplitJoinText::split(EditData* ed)
|
|
{
|
|
TextBase* t = c.text();
|
|
int line = c.row();
|
|
t->setTextInvalid();
|
|
t->triggerLayout();
|
|
|
|
CharFormat* charFmt = c.format(); // take current format
|
|
t->textBlockList().insert(line + 1, c.curLine().split(c.column()));
|
|
c.curLine().setEol(true);
|
|
|
|
c.setRow(line+1);
|
|
c.curLine().setEol(true);
|
|
c.setColumn(0);
|
|
c.setFormat(*charFmt); // restore orig. format at new line
|
|
c.clearSelection();
|
|
|
|
if (ed)
|
|
*t->cursor(*ed) = c;
|
|
c.text()->setTextInvalid();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// drop
|
|
//---------------------------------------------------------
|
|
|
|
Element* TextBase::drop(EditData& ed)
|
|
{
|
|
TextCursor* _cursor = cursor(ed);
|
|
|
|
Element* e = ed.dropElement;
|
|
|
|
switch (e->type()) {
|
|
case ElementType::SYMBOL:
|
|
{
|
|
SymId id = toSymbol(e)->sym();
|
|
delete e;
|
|
|
|
insertSym(ed, id);
|
|
}
|
|
break;
|
|
|
|
case ElementType::FSYMBOL:
|
|
{
|
|
uint code = toFSymbol(e)->code();
|
|
QString s = QString::fromUcs4(&code, 1);
|
|
delete e;
|
|
|
|
deleteSelectedText(ed);
|
|
score()->undo(new InsertText(_cursor, s), &ed);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
qDebug("drop <%s> not handled", e->name());
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// paste
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::paste(EditData& ed)
|
|
{
|
|
QString txt = QApplication::clipboard()->text(QClipboard::Clipboard);
|
|
if (MScore::debugMode)
|
|
qDebug("<%s>", qPrintable(txt));
|
|
|
|
int state = 0;
|
|
QString token;
|
|
QString sym;
|
|
bool symState = false;
|
|
|
|
score()->startCmd();
|
|
for (int i = 0; i < txt.length(); i++ ) {
|
|
QChar c = txt[i];
|
|
if (state == 0) {
|
|
if (c == '<') {
|
|
state = 1;
|
|
token.clear();
|
|
}
|
|
else if (c == '&') {
|
|
state = 2;
|
|
token.clear();
|
|
}
|
|
else {
|
|
if (symState)
|
|
sym += c;
|
|
else {
|
|
deleteSelectedText(ed);
|
|
if (c.isHighSurrogate()) {
|
|
QChar highSurrogate = c;
|
|
Q_ASSERT(i + 1 < txt.length());
|
|
i++;
|
|
QChar lowSurrogate = txt[i];
|
|
insertText(ed, QString(QChar::surrogateToUcs4(highSurrogate, lowSurrogate)));
|
|
}
|
|
else {
|
|
insertText(ed, QString(QChar(c.unicode())));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (state == 1) {
|
|
if (c == '>') {
|
|
state = 0;
|
|
if (token == "sym") {
|
|
symState = true;
|
|
sym.clear();
|
|
}
|
|
else if (token == "/sym") {
|
|
symState = false;
|
|
insertSym(ed, Sym::name2id(sym));
|
|
}
|
|
}
|
|
else
|
|
token += c;
|
|
}
|
|
else if (state == 2) {
|
|
if (c == ';') {
|
|
state = 0;
|
|
if (token == "lt")
|
|
insertText(ed, "<");
|
|
else if (token == "gt")
|
|
insertText(ed, ">");
|
|
else if (token == "amp")
|
|
insertText(ed, "&");
|
|
else if (token == "quot")
|
|
insertText(ed, "\"");
|
|
else
|
|
insertSym(ed, Sym::name2id(token));
|
|
}
|
|
else if (!c.isLetter()) {
|
|
state = 0;
|
|
insertText(ed, "&");
|
|
insertText(ed, token);
|
|
insertText(ed, c);
|
|
}
|
|
else
|
|
token += c;
|
|
}
|
|
}
|
|
if (state == 2) {
|
|
insertText(ed, "&");
|
|
insertText(ed, token);
|
|
}
|
|
score()->endCmd();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// inputTransition
|
|
// - preedit string should not influence then undo/redo stack
|
|
// - commit string goes onto the undo/redo stack
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::inputTransition(EditData& ed, QInputMethodEvent* ie)
|
|
{
|
|
TextEditData* ted = static_cast<TextEditData*>(ed.getData(this));
|
|
TextCursor* _cursor = &ted->cursor;
|
|
|
|
// remove preedit string
|
|
int n = preEdit.size();
|
|
while (n--) {
|
|
if (_cursor->movePosition(QTextCursor::Left)) {
|
|
TextBlock& l = _cursor->curLine();
|
|
l.remove(_cursor->column());
|
|
_cursor->text()->triggerLayout();
|
|
_cursor->text()->setTextInvalid();
|
|
}
|
|
}
|
|
|
|
qDebug("<%s><%s> len %d start %d, preEdit size %d",
|
|
qPrintable(ie->commitString()),
|
|
qPrintable(ie->preeditString()),
|
|
ie->replacementLength(), ie->replacementStart(), preEdit.size());
|
|
|
|
if (!ie->commitString().isEmpty()) {
|
|
_cursor->format()->setPreedit(false);
|
|
score()->startCmd();
|
|
insertText(ed, ie->commitString());
|
|
score()->endCmd();
|
|
preEdit.clear();
|
|
}
|
|
else {
|
|
preEdit = ie->preeditString();
|
|
if (!preEdit.isEmpty()) {
|
|
#if 0
|
|
for (auto a : ie->attributes()) {
|
|
switch(a.type) {
|
|
case QInputMethodEvent::TextFormat:
|
|
{
|
|
qDebug(" attribute TextFormat: %d-%d", a.start, a.length);
|
|
QTextFormat tf = a.value.value<QTextFormat>();
|
|
}
|
|
break;
|
|
case QInputMethodEvent::Cursor:
|
|
qDebug(" attribute Cursor at %d", a.start);
|
|
break;
|
|
default:
|
|
qDebug(" attribute %d", a.type);
|
|
}
|
|
}
|
|
#endif
|
|
_cursor->format()->setPreedit(true);
|
|
_cursor->updateCursorFormat();
|
|
editInsertText(_cursor, preEdit);
|
|
setTextInvalid();
|
|
layout1();
|
|
score()->update();
|
|
}
|
|
}
|
|
ie->accept();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// endHexState
|
|
//---------------------------------------------------------
|
|
|
|
void TextBase::endHexState(EditData& ed)
|
|
{
|
|
TextEditData* ted = static_cast<TextEditData*>(ed.getData(this));
|
|
TextCursor* _cursor = &ted->cursor;
|
|
|
|
if (hexState >= 0) {
|
|
if (hexState > 0) {
|
|
int c2 = _cursor->column();
|
|
int c1 = c2 - (hexState + 1);
|
|
|
|
TextBlock& t = _layout[_cursor->row()];
|
|
QString ss = t.remove(c1, hexState + 1);
|
|
bool ok;
|
|
int code = ss.mid(1).toInt(&ok, 16);
|
|
_cursor->setColumn(c1);
|
|
_cursor->clearSelection();
|
|
if (ok)
|
|
editInsertText(_cursor, QString(code));
|
|
else
|
|
qDebug("cannot convert hex string <%s>, state %d (%d-%d)",
|
|
qPrintable(ss.mid(1)), hexState, c1, c2);
|
|
}
|
|
hexState = -1;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// deleteSelectedText
|
|
//---------------------------------------------------------
|
|
|
|
bool TextBase::deleteSelectedText(EditData& ed)
|
|
{
|
|
TextCursor* _cursor = cursor(ed);
|
|
|
|
if (!_cursor->hasSelection())
|
|
return false;
|
|
|
|
int r1 = _cursor->selectLine();
|
|
int c1 = _cursor->selectColumn();
|
|
|
|
if (r1 > _cursor->row() || (r1 == _cursor->row() && c1 > _cursor->column())) {
|
|
// swap start end of selection
|
|
r1 = _cursor->row();
|
|
c1 = _cursor->column();
|
|
_cursor->setRow(_cursor->selectLine());
|
|
_cursor->setColumn(_cursor->selectColumn());
|
|
}
|
|
|
|
_cursor->clearSelection();
|
|
for (;;) {
|
|
if (r1 == _cursor->row() && c1 == _cursor->column())
|
|
break;
|
|
if (_cursor->column() == 0 && _cursor->row() != 0)
|
|
score()->undo(new JoinText(_cursor), &ed);
|
|
else {
|
|
// move cursor left:
|
|
if (!_cursor->movePosition(QTextCursor::Left))
|
|
break;
|
|
score()->undo(new RemoveText(_cursor, QString(_cursor->currentCharacter())), &ed);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace Ms
|
|
|