Introducing Paragraph::changeCase().

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@21167 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Abdelrazak Younes 2007-10-24 07:49:24 +00:00
parent ce1ec3edd2
commit 1297114b73
4 changed files with 82 additions and 72 deletions

View File

@ -66,10 +66,12 @@ using std::ostream;
namespace lyx { namespace lyx {
using support::contains; using support::contains;
using support::lowercase;
using support::prefixIs; using support::prefixIs;
using support::suffixIs; using support::suffixIs;
using support::rsplit; using support::rsplit;
using support::rtrim; using support::rtrim;
using support::uppercase;
namespace { namespace {
/// Inset identifier (above 0x10ffff, for ucs-4) /// Inset identifier (above 0x10ffff, for ucs-4)
@ -2600,6 +2602,70 @@ int Paragraph::numberOfOptArgs() const
} }
void Paragraph::changeCase(BufferParams const & bparams, pos_type pos,
pos_type right, TextCase action)
{
// process sequences of modified characters; in change
// tracking mode, this approach results in much better
// usability than changing case on a char-by-char basis
docstring changes;
bool const trackChanges = bparams.trackChanges;
bool capitalize = true;
for (; pos < right; ++pos) {
char_type oldChar = d->text_[pos];
char_type newChar = oldChar;
// ignore insets and don't play with deleted text!
if (isInset(pos) && !isDeleted(pos)) {
switch (action) {
case text_lowercase:
newChar = lowercase(oldChar);
break;
case text_capitalization:
if (capitalize) {
newChar = uppercase(oldChar);
capitalize = false;
}
break;
case text_uppercase:
newChar = uppercase(oldChar);
break;
}
}
if (!isLetter(pos) || isDeleted(pos)) {
// permit capitalization again
capitalize = true;
}
if (oldChar != newChar)
changes += newChar;
if (oldChar == newChar || pos == right - 1) {
if (oldChar != newChar) {
// step behind the changing area
pos++;
}
int erasePos = pos - changes.size();
for (size_t i = 0; i < changes.size(); i++) {
insertChar(pos, changes[i],
getFontSettings(bparams,
erasePos),
trackChanges);
if (!eraseChar(erasePos, trackChanges)) {
++erasePos;
++pos; // advance
++right; // expand selection
}
}
changes.clear();
}
}
}
char_type Paragraph::getChar(pos_type pos) const char_type Paragraph::getChar(pos_type pos) const
{ {
return d->text_[pos]; return d->text_[pos];

View File

@ -59,6 +59,16 @@ public:
pos_type first, last; pos_type first, last;
}; };
///
enum TextCase {
///
text_lowercase = 0,
///
text_capitalization = 1,
///
text_uppercase = 2
};
/// A Paragraph holds all text, attributes and insets in a text paragraph /// A Paragraph holds all text, attributes and insets in a text paragraph
/// \todo FIXME: any reference to ParagraphMetrics (including inheritance) /// \todo FIXME: any reference to ParagraphMetrics (including inheritance)
@ -347,6 +357,10 @@ public:
/// return the number of InsetOptArg in a paragraph /// return the number of InsetOptArg in a paragraph
int numberOfOptArgs() const; int numberOfOptArgs() const;
///
void changeCase(BufferParams const & bparams, pos_type pos,
pos_type right, TextCase action);
private: private:
/// Pimpl away stuff /// Pimpl away stuff
class Private; class Private;

View File

@ -85,9 +85,7 @@ namespace lyx {
using support::bformat; using support::bformat;
using support::contains; using support::contains;
using support::lowercase;
using support::split; using support::split;
using support::uppercase;
using cap::cutSelection; using cap::cutSelection;
using cap::pasteParagraphList; using cap::pasteParagraphList;
@ -827,7 +825,7 @@ void Text::deleteWordBackward(Cursor & cur)
// Kill to end of line. // Kill to end of line.
void Text::changeCase(Cursor & cur, Text::TextCase action) void Text::changeCase(Cursor & cur, TextCase action)
{ {
BOOST_ASSERT(this == cur.text()); BOOST_ASSERT(this == cur.text());
CursorSlice from; CursorSlice from;
@ -850,73 +848,14 @@ void Text::changeCase(Cursor & cur, Text::TextCase action)
pos_type begPos = from.pos(); pos_type begPos = from.pos();
pos_type endPos = to.pos(); pos_type endPos = to.pos();
bool const trackChanges = cur.buffer().params().trackChanges;
pos_type right = 0; // needed after the for loop pos_type right = 0; // needed after the for loop
for (pit_type pit = begPit; pit <= endPit; ++pit) { for (pit_type pit = begPit; pit <= endPit; ++pit) {
Paragraph & par = pars_[pit]; Paragraph & par = pars_[pit];
pos_type parSize = par.size(); pos_type parSize = par.size();
pos_type pos = (pit == begPit ? begPos : 0); pos_type pos = (pit == begPit ? begPos : 0);
right = (pit == endPit ? endPos : parSize); right = (pit == endPit ? endPos : parSize);
par.changeCase(cur.buffer().params(), pos, right, action);
// process sequences of modified characters; in change
// tracking mode, this approach results in much better
// usability than changing case on a char-by-char basis
docstring changes;
bool capitalize = true;
for (; pos < right; ++pos) {
char_type oldChar = par.getChar(pos);
char_type newChar = oldChar;
// ignore insets and don't play with deleted text!
if (par.isInset(pos) && !par.isDeleted(pos)) {
switch (action) {
case text_lowercase:
newChar = lowercase(oldChar);
break;
case text_capitalization:
if (capitalize) {
newChar = uppercase(oldChar);
capitalize = false;
}
break;
case text_uppercase:
newChar = uppercase(oldChar);
break;
}
}
if (!par.isLetter(pos) || par.isDeleted(pos)) {
capitalize = true; // permit capitalization again
}
if (oldChar != newChar) {
changes += newChar;
}
if (oldChar == newChar || pos == right - 1) {
if (oldChar != newChar) {
pos++; // step behind the changing area
}
int erasePos = pos - changes.size();
for (size_t i = 0; i < changes.size(); i++) {
par.insertChar(pos, changes[i],
par.getFontSettings(cur.buffer().params(),
erasePos),
trackChanges);
if (!par.eraseChar(erasePos, trackChanges)) {
++erasePos;
++pos; // advance
++right; // expand selection
}
}
changes.clear();
}
}
} }
// the selection may have changed due to logically-only deleted chars // the selection may have changed due to logically-only deleted chars

View File

@ -200,15 +200,6 @@ public:
bool dissolveInset(Cursor & cur); bool dissolveInset(Cursor & cur);
/// ///
bool selectWordWhenUnderCursor(Cursor & cur, word_location); bool selectWordWhenUnderCursor(Cursor & cur, word_location);
///
enum TextCase {
///
text_lowercase = 0,
///
text_capitalization = 1,
///
text_uppercase = 2
};
/// Change the case of the word at cursor position. /// Change the case of the word at cursor position.
void changeCase(Cursor & cur, TextCase action); void changeCase(Cursor & cur, TextCase action);
/// Transposes the character at the cursor with the one before it /// Transposes the character at the cursor with the one before it