#include <stdio.h>
#include <stdlib.h>
#include <FL/fl_utf8.h>
#include <FL/fl_string_functions.h>
#include "flstring.h"
#include <ctype.h>
#include <FL/Fl.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/fl_ask.H>
#ifndef min
static int max(int i1, int i2)
{
return i1 >= i2 ? i1 : i2;
}
static int min(int i1, int i2)
{
return i1 <= i2 ? i1 : i2;
}
#endif
class Fl_Text_Undo_Action {
public:
Fl_Text_Undo_Action() :
undobuffer(NULL),
undobufferlength(0),
undoat(0),
undocut(0),
undoinsert(0),
undoyankcut(0)
{ }
~Fl_Text_Undo_Action() {
if (undobuffer)
::free(undobuffer);
}
char *undobuffer;
int undobufferlength;
int undoat; int undocut; int undoinsert; int undoyankcut;
void undobuffersize(int n)
{
if (n > undobufferlength) {
undobufferlength = n + 128;
undobuffer = (char *)realloc(undobuffer, undobufferlength);
}
}
void clear() {
undocut = undoinsert = 0;
}
bool empty() const {
return (!undocut && !undoinsert);
}
};
class Fl_Text_Undo_Action_List {
Fl_Text_Undo_Action** list_;
int list_size_;
int list_capacity_;
bool locked_;
public:
Fl_Text_Undo_Action_List() :
list_(NULL),
list_size_(0),
list_capacity_(0),
locked_(false)
{ }
~Fl_Text_Undo_Action_List() {
unlock();
clear();
}
int size() const {
return list_size_;
}
void push(Fl_Text_Undo_Action* action) {
if (list_size_ == list_capacity_) {
list_capacity_ += 25;
list_ = (Fl_Text_Undo_Action**)realloc(list_, list_capacity_ * sizeof(Fl_Text_Undo_Action*));
}
list_[list_size_++] = action;
}
Fl_Text_Undo_Action* pop() {
if (list_size_ > 0) {
return list_[--list_size_];
} else {
return NULL;
}
}
void clear() {
if (locked_) return;
if (list_) {
for (int i=0; i<list_size_; i++) {
delete list_[i];
}
::free(list_);
}
list_ = NULL;
list_size_ = 0;
list_capacity_ = 0;
}
void lock() { locked_ = true; }
void unlock() { locked_ = false; }
};
static void def_transcoding_warning_action(Fl_Text_Buffer *text)
{
fl_alert("%s", text->file_encoding_warning_message);
}
Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize)
{
mLength = 0;
mPreferredGapSize = preferredGapSize;
mBuf = (char *) malloc(requestedSize + mPreferredGapSize);
mGapStart = 0;
mGapEnd = requestedSize + mPreferredGapSize;
mTabDist = 8;
mPrimary.mSelected = 0;
mPrimary.mStart = mPrimary.mEnd = 0;
mSecondary.mSelected = 0;
mSecondary.mStart = mSecondary.mEnd = 0;
mHighlight.mSelected = 0;
mHighlight.mStart = mHighlight.mEnd = 0;
mModifyProcs = NULL;
mCbArgs = NULL;
mNModifyProcs = 0;
mNPredeleteProcs = 0;
mPredeleteProcs = NULL;
mPredeleteCbArgs = NULL;
mCursorPosHint = 0;
mCanUndo = 1;
mUndo = new Fl_Text_Undo_Action();
mUndoList = new Fl_Text_Undo_Action_List();
mRedoList = new Fl_Text_Undo_Action_List();
input_file_was_transcoded = 0;
transcoding_warning_action = def_transcoding_warning_action;
}
Fl_Text_Buffer::~Fl_Text_Buffer()
{
free(mBuf);
if (mNModifyProcs != 0) {
delete[]mModifyProcs;
delete[]mCbArgs;
}
if (mNPredeleteProcs > 0) {
delete[] mPredeleteProcs;
delete[] mPredeleteCbArgs;
}
delete mUndo;
delete mUndoList;
delete mRedoList;
}
char *Fl_Text_Buffer::text() const {
char *t = (char *) malloc(mLength + 1);
memcpy(t, mBuf, mGapStart);
memcpy(t+mGapStart, mBuf+mGapEnd, mLength - mGapStart);
t[mLength] = '\0';
return t;
}
void Fl_Text_Buffer::text(const char *t)
{
IS_UTF8_ALIGNED(t)
if (!t) t="";
call_predelete_callbacks(0, length());
const char *deletedText = text();
int deletedLength = mLength;
free((void *) mBuf);
int insertedLength = (int) strlen(t);
mBuf = (char *) malloc(insertedLength + mPreferredGapSize);
mLength = insertedLength;
mGapStart = insertedLength;
mGapEnd = mGapStart + mPreferredGapSize;
memcpy(mBuf, t, insertedLength);
update_selections(0, deletedLength, 0);
call_modify_callbacks(0, deletedLength, insertedLength, 0, deletedText);
free((void *) deletedText);
if (mCanUndo) {
mUndo->clear();
mUndoList->clear();
mRedoList->clear();
}
}
char *Fl_Text_Buffer::text_range(int start, int end) const {
IS_UTF8_ALIGNED2(this, (start))
IS_UTF8_ALIGNED2(this, (end))
char *s = NULL;
if (start < 0 || start > mLength)
{
s = (char *) malloc(1);
s[0] = '\0';
return s;
}
if (end < start) {
int temp = start;
start = end;
end = temp;
}
if (end > mLength)
end = mLength;
int copiedLength = end - start;
s = (char *) malloc(copiedLength + 1);
if (end <= mGapStart) {
memcpy(s, mBuf + start, copiedLength);
} else if (start >= mGapStart) {
memcpy(s, mBuf + start + (mGapEnd - mGapStart), copiedLength);
} else {
int part1Length = mGapStart - start;
memcpy(s, mBuf + start, part1Length);
memcpy(s + part1Length, mBuf + mGapEnd, copiedLength - part1Length);
}
s[copiedLength] = '\0';
return s;
}
unsigned int Fl_Text_Buffer::char_at(int pos) const {
if (pos < 0 || pos >= mLength)
return '\0';
IS_UTF8_ALIGNED2(this, (pos))
const char *src = address(pos);
return fl_utf8decode(src, 0, 0);
}
char Fl_Text_Buffer::byte_at(int pos) const {
if (pos < 0 || pos >= mLength)
return '\0';
const char *src = address(pos);
return *src;
}
void Fl_Text_Buffer::insert(int pos, const char *text, int insertedLength)
{
IS_UTF8_ALIGNED2(this, (pos))
IS_UTF8_ALIGNED(text)
if (!text || !*text)
return;
if (pos > mLength)
pos = mLength;
if (pos < 0)
pos = 0;
call_predelete_callbacks(pos, 0);
int nInserted = insert_(pos, text, insertedLength);
mCursorPosHint = pos + nInserted;
IS_UTF8_ALIGNED2(this, (mCursorPosHint))
call_modify_callbacks(pos, 0, nInserted, 0, NULL);
}
void Fl_Text_Buffer::vprintf(const char *fmt, va_list ap) {
char buffer[1024]; ::vsnprintf(buffer, 1024, fmt, ap);
buffer[1024-1] = 0; append(buffer);
}
void Fl_Text_Buffer::printf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
Fl_Text_Buffer::vprintf(fmt,ap);
va_end(ap);
}
void Fl_Text_Buffer::replace(int start, int end, const char *text, int insertedLength)
{
if (!text)
return;
if (start < 0)
start = 0;
if (end > mLength)
end = mLength;
IS_UTF8_ALIGNED2(this, (start))
IS_UTF8_ALIGNED2(this, (end))
IS_UTF8_ALIGNED(text)
call_predelete_callbacks(start, end - start);
const char *deletedText = text_range(start, end);
remove_(start, end);
int nInserted = insert_(start, text, insertedLength);
mCursorPosHint = start + nInserted;
call_modify_callbacks(start, end - start, nInserted, 0, deletedText);
free((void *) deletedText);
}
void Fl_Text_Buffer::remove(int start, int end)
{
if (start > end) {
int temp = start;
start = end;
end = temp;
}
if (start > mLength)
start = mLength;
if (start < 0)
start = 0;
if (end > mLength)
end = mLength;
if (end < 0)
end = 0;
IS_UTF8_ALIGNED2(this, (start))
IS_UTF8_ALIGNED2(this, (end))
if (start == end)
return;
call_predelete_callbacks(start, end - start);
const char *deletedText = text_range(start, end);
remove_(start, end);
mCursorPosHint = start;
call_modify_callbacks(start, end - start, 0, 0, deletedText);
free((void *) deletedText);
}
void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart,
int fromEnd, int toPos)
{
IS_UTF8_ALIGNED2(fromBuf, fromStart)
IS_UTF8_ALIGNED2(fromBuf, fromEnd)
IS_UTF8_ALIGNED2(this, (toPos))
int copiedLength = fromEnd - fromStart;
if (copiedLength > mGapEnd - mGapStart)
reallocate_with_gap(toPos, copiedLength + mPreferredGapSize);
else if (toPos != mGapStart)
move_gap(toPos);
if (fromEnd <= fromBuf->mGapStart) {
memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], copiedLength);
} else if (fromStart >= fromBuf->mGapStart) {
memcpy(&mBuf[toPos],
&fromBuf->mBuf[fromStart + (fromBuf->mGapEnd - fromBuf->mGapStart)],
copiedLength);
} else {
int part1Length = fromBuf->mGapStart - fromStart;
memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], part1Length);
memcpy(&mBuf[toPos + part1Length],
&fromBuf->mBuf[fromBuf->mGapEnd], copiedLength - part1Length);
}
mGapStart += copiedLength;
mLength += copiedLength;
update_selections(toPos, 0, copiedLength);
}
int Fl_Text_Buffer::apply_undo(Fl_Text_Undo_Action* action, int* cursorPos)
{
if (action->empty())
return 0;
mRedoList->lock();
int ilen = action->undocut;
int xlen = action->undoinsert;
int b = action->undoat - xlen;
if (xlen && action->undoyankcut && !ilen) {
ilen = action->undoyankcut;
}
if (xlen && ilen) {
action->undobuffersize(ilen + 1);
action->undobuffer[ilen] = 0;
char *tmp = fl_strdup(action->undobuffer);
replace(b, action->undoat, tmp);
if (cursorPos)
*cursorPos = mCursorPosHint;
free(tmp);
} else if (xlen) {
remove(b, action->undoat);
if (cursorPos)
*cursorPos = mCursorPosHint;
} else if (ilen) {
action->undobuffersize(ilen + 1);
action->undobuffer[ilen] = 0;
insert(action->undoat, action->undobuffer);
if (cursorPos)
*cursorPos = mCursorPosHint;
action->undoyankcut = 0;
}
mRedoList->unlock();
return 1;
}
int Fl_Text_Buffer::undo(int *cursorPos) {
if (!mCanUndo || mUndo->empty())
return 0;
Fl_Text_Undo_Action* action = mUndo;
mUndo = new Fl_Text_Undo_Action();
int ret = apply_undo(action, cursorPos);
delete action;
if (ret) {
mRedoList->push(mUndo);
mUndo = mUndoList->pop();
if (mUndo) {
delete mUndo;
mUndo = mUndoList->pop();
if (!mUndo) mUndo = new Fl_Text_Undo_Action();
}
}
return ret;
}
bool Fl_Text_Buffer::can_undo() const {
return (mCanUndo && mUndo && !mUndo->empty());
}
int Fl_Text_Buffer::redo(int *cursorPos) {
if (!mCanUndo)
return 0;
Fl_Text_Undo_Action *redo_action = mRedoList->pop();
if (!redo_action)
return 0;
int ret = apply_undo(redo_action, cursorPos);
delete redo_action;
return ret;
}
bool Fl_Text_Buffer::can_redo() const {
return (mCanUndo && mRedoList->size());
}
void Fl_Text_Buffer::canUndo(char flag)
{
if (flag) {
if (!mCanUndo) {
mUndo = new Fl_Text_Undo_Action();
}
} else {
if (mCanUndo) {
delete mUndo;
mUndo = NULL;
}
}
mCanUndo = flag;
}
void Fl_Text_Buffer::tab_distance(int tabDist)
{
call_predelete_callbacks(0, mLength);
mTabDist = tabDist;
const char *deletedText = text();
call_modify_callbacks(0, mLength, mLength, 0, deletedText);
free((void *) deletedText);
}
void Fl_Text_Buffer::select(int start, int end)
{
IS_UTF8_ALIGNED2(this, (start))
IS_UTF8_ALIGNED2(this, (end))
Fl_Text_Selection oldSelection = mPrimary;
mPrimary.set(start, end);
redisplay_selection(&oldSelection, &mPrimary);
}
void Fl_Text_Buffer::unselect()
{
Fl_Text_Selection oldSelection = mPrimary;
mPrimary.mSelected = 0;
redisplay_selection(&oldSelection, &mPrimary);
}
int Fl_Text_Buffer::selection_position(int *start, int *end)
{
return mPrimary.selected(start, end);
}
char *Fl_Text_Buffer::selection_text()
{
return selection_text_(&mPrimary);
}
void Fl_Text_Buffer::remove_selection()
{
remove_selection_(&mPrimary);
}
void Fl_Text_Buffer::replace_selection(const char *text)
{
replace_selection_(&mPrimary, text);
}
void Fl_Text_Buffer::secondary_select(int start, int end)
{
Fl_Text_Selection oldSelection = mSecondary;
mSecondary.set(start, end);
redisplay_selection(&oldSelection, &mSecondary);
}
void Fl_Text_Buffer::secondary_unselect()
{
Fl_Text_Selection oldSelection = mSecondary;
mSecondary.mSelected = 0;
redisplay_selection(&oldSelection, &mSecondary);
}
int Fl_Text_Buffer::secondary_selection_position(int *start, int *end)
{
return mSecondary.selected(start, end);
}
char *Fl_Text_Buffer::secondary_selection_text()
{
return selection_text_(&mSecondary);
}
void Fl_Text_Buffer::remove_secondary_selection()
{
remove_selection_(&mSecondary);
}
void Fl_Text_Buffer::replace_secondary_selection(const char *text)
{
replace_selection_(&mSecondary, text);
}
void Fl_Text_Buffer::highlight(int start, int end)
{
Fl_Text_Selection oldSelection = mHighlight;
mHighlight.set(start, end);
redisplay_selection(&oldSelection, &mHighlight);
}
void Fl_Text_Buffer::unhighlight()
{
Fl_Text_Selection oldSelection = mHighlight;
mHighlight.mSelected = 0;
redisplay_selection(&oldSelection, &mHighlight);
}
int Fl_Text_Buffer::highlight_position(int *start, int *end)
{
return mHighlight.selected(start, end);
}
char *Fl_Text_Buffer::highlight_text()
{
return selection_text_(&mHighlight);
}
void Fl_Text_Buffer::add_modify_callback(Fl_Text_Modify_Cb bufModifiedCB,
void *cbArg)
{
Fl_Text_Modify_Cb *newModifyProcs =
new Fl_Text_Modify_Cb[mNModifyProcs + 1];
void **newCBArgs = new void *[mNModifyProcs + 1];
for (int i = 0; i < mNModifyProcs; i++) {
newModifyProcs[i + 1] = mModifyProcs[i];
newCBArgs[i + 1] = mCbArgs[i];
}
if (mNModifyProcs != 0) {
delete[]mModifyProcs;
delete[]mCbArgs;
}
newModifyProcs[0] = bufModifiedCB;
newCBArgs[0] = cbArg;
mNModifyProcs++;
mModifyProcs = newModifyProcs;
mCbArgs = newCBArgs;
}
void Fl_Text_Buffer::remove_modify_callback(Fl_Text_Modify_Cb bufModifiedCB,
void *cbArg)
{
int i, toRemove = -1;
for (i = 0; i < mNModifyProcs; i++) {
if (mModifyProcs[i] == bufModifiedCB && mCbArgs[i] == cbArg) {
toRemove = i;
break;
}
}
if (toRemove == -1) {
Fl::error
("Fl_Text_Buffer::remove_modify_callback(): Can't find modify CB to remove");
return;
}
mNModifyProcs--;
if (mNModifyProcs == 0) {
mNModifyProcs = 0;
delete[]mModifyProcs;
mModifyProcs = NULL;
delete[]mCbArgs;
mCbArgs = NULL;
return;
}
Fl_Text_Modify_Cb *newModifyProcs = new Fl_Text_Modify_Cb[mNModifyProcs];
void **newCBArgs = new void *[mNModifyProcs];
for (i = 0; i < toRemove; i++) {
newModifyProcs[i] = mModifyProcs[i];
newCBArgs[i] = mCbArgs[i];
}
for (; i < mNModifyProcs; i++) {
newModifyProcs[i] = mModifyProcs[i + 1];
newCBArgs[i] = mCbArgs[i + 1];
}
delete[]mModifyProcs;
delete[]mCbArgs;
mModifyProcs = newModifyProcs;
mCbArgs = newCBArgs;
}
void Fl_Text_Buffer::add_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB,
void *cbArg)
{
Fl_Text_Predelete_Cb *newPreDeleteProcs =
new Fl_Text_Predelete_Cb[mNPredeleteProcs + 1];
void **newCBArgs = new void *[mNPredeleteProcs + 1];
for (int i = 0; i < mNPredeleteProcs; i++) {
newPreDeleteProcs[i + 1] = mPredeleteProcs[i];
newCBArgs[i + 1] = mPredeleteCbArgs[i];
}
if (mNPredeleteProcs > 0) {
delete[] mPredeleteProcs;
delete[] mPredeleteCbArgs;
}
newPreDeleteProcs[0] = bufPreDeleteCB;
newCBArgs[0] = cbArg;
mNPredeleteProcs++;
mPredeleteProcs = newPreDeleteProcs;
mPredeleteCbArgs = newCBArgs;
}
void Fl_Text_Buffer::remove_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg)
{
int i, toRemove = -1;
for (i = 0; i < mNPredeleteProcs; i++) {
if (mPredeleteProcs[i] == bufPreDeleteCB &&
mPredeleteCbArgs[i] == cbArg) {
toRemove = i;
break;
}
}
if (toRemove == -1) {
Fl::error
("Fl_Text_Buffer::remove_predelete_callback(): Can't find pre-delete CB to remove");
return;
}
mNPredeleteProcs--;
if (mNPredeleteProcs == 0) {
delete[]mPredeleteProcs;
mPredeleteProcs = NULL;
delete[]mPredeleteCbArgs;
mPredeleteCbArgs = NULL;
return;
}
Fl_Text_Predelete_Cb *newPreDeleteProcs = new Fl_Text_Predelete_Cb[mNPredeleteProcs];
void **newCBArgs = new void *[mNPredeleteProcs];
for (i = 0; i < toRemove; i++) {
newPreDeleteProcs[i] = mPredeleteProcs[i];
newCBArgs[i] = mPredeleteCbArgs[i];
}
for (; i < mNPredeleteProcs; i++) {
newPreDeleteProcs[i] = mPredeleteProcs[i + 1];
newCBArgs[i] = mPredeleteCbArgs[i + 1];
}
delete[] mPredeleteProcs;
delete[] mPredeleteCbArgs;
mPredeleteProcs = newPreDeleteProcs;
mPredeleteCbArgs = newCBArgs;
}
char *Fl_Text_Buffer::line_text(int pos) const {
return text_range(line_start(pos), line_end(pos));
}
int Fl_Text_Buffer::line_start(int pos) const
{
if (!findchar_backward(pos, '\n', &pos))
return 0;
return pos + 1;
}
int Fl_Text_Buffer::line_end(int pos) const {
if (!findchar_forward(pos, '\n', &pos))
pos = mLength;
return pos;
}
bool Fl_Text_Buffer::is_word_separator(int pos) const {
int c = char_at(pos);
if (c < 128) {
return !(isalnum(c) || c == '_'); }
return (c == 0xA0 || (c >= 0x3000 && c <= 0x301F) );
}
int Fl_Text_Buffer::word_start(int pos) const {
while (pos > 0 && !is_word_separator(pos))
{
pos = prev_char(pos);
}
if (is_word_separator(pos))
pos = next_char(pos);
return pos;
}
int Fl_Text_Buffer::word_end(int pos) const {
while (pos < length() && !is_word_separator(pos))
{
pos = next_char(pos);
}
return pos;
}
int Fl_Text_Buffer::count_displayed_characters(int lineStartPos,
int targetPos) const
{
IS_UTF8_ALIGNED2(this, (lineStartPos))
IS_UTF8_ALIGNED2(this, (targetPos))
int charCount = 0;
int pos = lineStartPos;
while (pos < targetPos) {
pos = next_char(pos);
charCount++;
}
return charCount;
}
int Fl_Text_Buffer::skip_displayed_characters(int lineStartPos, int nChars)
{
IS_UTF8_ALIGNED2(this, (lineStartPos))
int pos = lineStartPos;
for (int charCount = 0; charCount < nChars && pos < mLength; charCount++) {
unsigned int c = char_at(pos);
if (c == '\n')
return pos;
pos = next_char(pos);
}
return pos;
}
int Fl_Text_Buffer::count_lines(int startPos, int endPos) const {
IS_UTF8_ALIGNED2(this, (startPos))
IS_UTF8_ALIGNED2(this, (endPos))
int gapLen = mGapEnd - mGapStart;
int lineCount = 0;
int pos = startPos;
while (pos < mGapStart)
{
if (pos == endPos)
return lineCount;
if (mBuf[pos++] == '\n')
lineCount++;
}
while (pos < mLength) {
if (pos == endPos)
return lineCount;
if (mBuf[pos++ + gapLen] == '\n')
lineCount++;
}
return lineCount;
}
int Fl_Text_Buffer::estimate_lines(int startPos, int endPos, int lineLen) const
{
IS_UTF8_ALIGNED2(this, (startPos))
IS_UTF8_ALIGNED2(this, (endPos))
int gapLen = mGapEnd - mGapStart;
int lineCount = 0;
int softLineBreaks = 0, softLineBreakCount = lineLen;
int pos = startPos;
while (pos < mGapStart)
{
if (pos == endPos)
return lineCount + softLineBreaks;
if (mBuf[pos++] == '\n') {
softLineBreakCount = lineLen;
lineCount++;
}
if (--softLineBreakCount == 0) {
softLineBreakCount = lineLen;
softLineBreaks++;
}
}
while (pos < mLength) {
if (pos == endPos)
return lineCount + softLineBreaks;
if (mBuf[pos++ + gapLen] == '\n') {
softLineBreakCount = lineLen;
lineCount++;
}
if (--softLineBreakCount == 0) {
softLineBreakCount = lineLen;
softLineBreaks++;
}
}
return lineCount + softLineBreaks;
}
int Fl_Text_Buffer::skip_lines(int startPos, int nLines)
{
IS_UTF8_ALIGNED2(this, (startPos))
if (nLines == 0)
return startPos;
int gapLen = mGapEnd - mGapStart;
int pos = startPos;
int lineCount = 0;
while (pos < mGapStart) {
if (mBuf[pos++] == '\n') {
lineCount++;
if (lineCount == nLines) {
IS_UTF8_ALIGNED2(this, (pos))
return pos;
}
}
}
while (pos < mLength) {
if (mBuf[pos++ + gapLen] == '\n') {
lineCount++;
if (lineCount >= nLines) {
IS_UTF8_ALIGNED2(this, (pos))
return pos;
}
}
}
IS_UTF8_ALIGNED2(this, (pos))
return pos;
}
int Fl_Text_Buffer::rewind_lines(int startPos, int nLines)
{
IS_UTF8_ALIGNED2(this, (startPos))
int pos = startPos - 1;
if (pos <= 0)
return 0;
int gapLen = mGapEnd - mGapStart;
int lineCount = -1;
while (pos >= mGapStart) {
if (mBuf[pos + gapLen] == '\n') {
if (++lineCount >= nLines) {
IS_UTF8_ALIGNED2(this, (pos+1))
return pos + 1;
}
}
pos--;
}
while (pos >= 0) {
if (mBuf[pos] == '\n') {
if (++lineCount >= nLines) {
IS_UTF8_ALIGNED2(this, (pos+1))
return pos + 1;
}
}
pos--;
}
return 0;
}
int Fl_Text_Buffer::search_forward(int startPos, const char *searchString,
int *foundPos, int matchCase) const
{
IS_UTF8_ALIGNED2(this, (startPos))
IS_UTF8_ALIGNED(searchString)
if (!searchString)
return 0;
int bp;
const char *sp;
if (matchCase) {
while (startPos < length()) {
bp = startPos;
sp = searchString;
for (;;) {
char c = *sp;
if (!c) {
*foundPos = startPos;
return 1;
}
int l = fl_utf8len1(c);
if (memcmp(sp, address(bp), l))
break;
sp += l; bp += l;
}
startPos = next_char(startPos);
}
} else {
while (startPos < length()) {
bp = startPos;
sp = searchString;
for (;;) {
if (!*sp) {
*foundPos = startPos;
return 1;
}
int l;
unsigned int b = char_at(bp);
unsigned int s = fl_utf8decode(sp, 0, &l);
if (fl_tolower(b)!=fl_tolower(s))
break;
sp += l;
bp = next_char(bp);
}
startPos = next_char(startPos);
}
}
return 0;
}
int Fl_Text_Buffer::search_backward(int startPos, const char *searchString,
int *foundPos, int matchCase) const
{
IS_UTF8_ALIGNED2(this, (startPos))
IS_UTF8_ALIGNED(searchString)
if (!searchString)
return 0;
int bp;
const char *sp;
if (matchCase) {
while (startPos >= 0) {
bp = startPos;
sp = searchString;
for (;;) {
char c = *sp;
if (!c) {
*foundPos = startPos;
return 1;
}
int l = fl_utf8len1(c);
if (memcmp(sp, address(bp), l))
break;
sp += l; bp += l;
}
startPos = prev_char(startPos);
}
} else {
while (startPos >= 0) {
bp = startPos;
sp = searchString;
for (;;) {
if (!*sp) {
*foundPos = startPos;
return 1;
}
int l;
unsigned int b = char_at(bp);
unsigned int s = fl_utf8decode(sp, 0, &l);
if (fl_tolower(b)!=fl_tolower(s))
break;
sp += l;
bp = next_char(bp);
}
startPos = prev_char(startPos);
}
}
return 0;
}
int Fl_Text_Buffer::insert_(int pos, const char *text, int insertedLength)
{
if (!text || !*text)
return 0;
if (insertedLength == -1) insertedLength = (int) strlen(text);
if (insertedLength > mGapEnd - mGapStart)
reallocate_with_gap(pos, insertedLength + mPreferredGapSize);
else if (pos != mGapStart)
move_gap(pos);
memcpy(&mBuf[pos], text, insertedLength);
mGapStart += insertedLength;
mLength += insertedLength;
update_selections(pos, 0, insertedLength);
if (mCanUndo) {
if (mUndo->undoat == pos && mUndo->undoinsert) {
mUndo->undoinsert += insertedLength;
} else {
int yankcut = (mUndo->undoat == pos) ? mUndo->undocut : 0;
if (!yankcut) {
mRedoList->clear();
mUndoList->push(mUndo);
mUndo = new Fl_Text_Undo_Action();
} else {
}
mUndo->undoinsert = insertedLength;
mUndo->undoyankcut = yankcut;
}
mUndo->undoat = pos + insertedLength;
mUndo->undocut = 0;
}
return insertedLength;
}
void Fl_Text_Buffer::remove_(int start, int end)
{
if (start >= end) return;
if (mCanUndo) {
if (mUndo->undoat == end && mUndo->undocut) {
mUndo->undobuffersize(mUndo->undocut + end - start + 1);
memmove(mUndo->undobuffer + end - start, mUndo->undobuffer, mUndo->undocut);
mUndo->undocut += end - start;
} else {
mRedoList->clear();
mUndoList->push(mUndo);
mUndo = new Fl_Text_Undo_Action();
mUndo->undocut = end - start;
mUndo->undobuffersize(mUndo->undocut);
}
mUndo->undoat = start;
mUndo->undoinsert = 0;
mUndo->undoyankcut = 0;
}
if (start > mGapStart) {
if (mCanUndo)
memcpy(mUndo->undobuffer, mBuf + (mGapEnd - mGapStart) + start,
end - start);
move_gap(start);
} else if (end < mGapStart) {
if (mCanUndo)
memcpy(mUndo->undobuffer, mBuf + start, end - start);
move_gap(end);
} else {
int prelen = mGapStart - start;
if (mCanUndo) {
memcpy(mUndo->undobuffer, mBuf + start, prelen);
memcpy(mUndo->undobuffer + prelen, mBuf + mGapEnd, end - start - prelen);
}
}
mGapEnd += end - mGapStart;
mGapStart = start;
mLength -= end - start;
update_selections(start, end - start, 0);
}
void Fl_Text_Selection::set(int startpos, int endpos)
{
mSelected = (startpos != endpos);
mStart = min(startpos, endpos);
mEnd = max(startpos, endpos);
}
int Fl_Text_Selection::selected(int *startpos, int *endpos) const {
if (!mSelected) {
*startpos = 0;
*endpos = 0;
return 0;
}
*startpos = mStart;
*endpos = mEnd;
return 1;
}
int Fl_Text_Selection::includes(int pos) const {
return (selected() && pos >= start() && pos < end() );
}
char *Fl_Text_Buffer::selection_text_(Fl_Text_Selection * sel) const {
int start, end;
if (!sel->selected(&start, &end))
{
char *s = (char *) malloc(1);
*s = '\0';
return s;
}
return text_range(start, end);
}
void Fl_Text_Buffer::remove_selection_(Fl_Text_Selection * sel)
{
int start, end;
if (!sel->selected(&start, &end))
return;
remove(start, end);
}
void Fl_Text_Buffer::replace_selection_(Fl_Text_Selection * sel,
const char *text)
{
Fl_Text_Selection oldSelection = *sel;
int start, end;
if (!sel->selected(&start, &end))
return;
replace(start, end, text);
sel->mSelected = 0;
redisplay_selection(&oldSelection, sel);
}
void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted,
int nInserted, int nRestyled,
const char *deletedText) const {
IS_UTF8_ALIGNED2(this, pos)
for (int i = 0; i < mNModifyProcs; i++)
(*mModifyProcs[i]) (pos, nInserted, nDeleted, nRestyled,
deletedText, mCbArgs[i]);
}
void Fl_Text_Buffer::call_predelete_callbacks(int pos, int nDeleted) const {
for (int i = 0; i < mNPredeleteProcs; i++)
(*mPredeleteProcs[i]) (pos, nDeleted, mPredeleteCbArgs[i]);
}
void Fl_Text_Buffer::redisplay_selection(Fl_Text_Selection *
oldSelection,
Fl_Text_Selection *
newSelection) const
{
int oldStart, oldEnd, newStart, newEnd, ch1Start, ch1End, ch2Start,
ch2End;
oldStart = oldSelection->mStart;
newStart = newSelection->mStart;
oldEnd = oldSelection->mEnd;
newEnd = newSelection->mEnd;
if (!oldSelection->mSelected && !newSelection->mSelected)
return;
if (!oldSelection->mSelected)
{
call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL);
return;
}
if (!newSelection->mSelected) {
call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL);
return;
}
if (oldEnd < newStart || newEnd < oldStart) {
call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL);
call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL);
return;
}
ch1Start = min(oldStart, newStart);
ch2End = max(oldEnd, newEnd);
ch1End = max(oldStart, newStart);
ch2Start = min(oldEnd, newEnd);
if (ch1Start != ch1End)
call_modify_callbacks(ch1Start, 0, 0, ch1End - ch1Start, NULL);
if (ch2Start != ch2End)
call_modify_callbacks(ch2Start, 0, 0, ch2End - ch2Start, NULL);
}
void Fl_Text_Buffer::move_gap(int pos)
{
int gapLen = mGapEnd - mGapStart;
if (pos > mGapStart)
memmove(&mBuf[mGapStart], &mBuf[mGapEnd], pos - mGapStart);
else
memmove(&mBuf[pos + gapLen], &mBuf[pos], mGapStart - pos);
mGapEnd += pos - mGapStart;
mGapStart += pos - mGapStart;
}
void Fl_Text_Buffer::reallocate_with_gap(int newGapStart, int newGapLen)
{
char *newBuf = (char *) malloc(mLength + newGapLen);
int newGapEnd = newGapStart + newGapLen;
if (newGapStart <= mGapStart) {
memcpy(newBuf, mBuf, newGapStart);
memcpy(&newBuf[newGapEnd], &mBuf[newGapStart],
mGapStart - newGapStart);
memcpy(&newBuf[newGapEnd + mGapStart - newGapStart],
&mBuf[mGapEnd], mLength - mGapStart);
} else {
memcpy(newBuf, mBuf, mGapStart);
memcpy(&newBuf[mGapStart], &mBuf[mGapEnd], newGapStart - mGapStart);
memcpy(&newBuf[newGapEnd],
&mBuf[mGapEnd + newGapStart - mGapStart],
mLength - newGapStart);
}
free((void *) mBuf);
mBuf = newBuf;
mGapStart = newGapStart;
mGapEnd = newGapEnd;
}
void Fl_Text_Buffer::update_selections(int pos, int nDeleted,
int nInserted)
{
mPrimary.update(pos, nDeleted, nInserted);
mSecondary.update(pos, nDeleted, nInserted);
mHighlight.update(pos, nDeleted, nInserted);
}
void Fl_Text_Selection::update(int pos, int nDeleted, int nInserted)
{
if (!mSelected || pos > mEnd)
return;
if (pos + nDeleted <= mStart) {
mStart += nInserted - nDeleted;
mEnd += nInserted - nDeleted;
} else if (pos <= mStart && pos + nDeleted >= mEnd) {
mStart = pos;
mEnd = pos;
mSelected = 0;
} else if (pos <= mStart && pos + nDeleted < mEnd) {
mStart = pos;
mEnd = nInserted + mEnd - nDeleted;
} else if (pos < mEnd) {
mEnd += nInserted - nDeleted;
if (mEnd <= mStart)
mSelected = 0;
}
}
int Fl_Text_Buffer::findchar_forward(int startPos, unsigned searchChar,
int *foundPos) const
{
if (startPos >= mLength) {
*foundPos = mLength;
return 0;
}
if (startPos<0)
startPos = 0;
for ( ; startPos<mLength; startPos = next_char(startPos)) {
if (searchChar == char_at(startPos)) {
*foundPos = startPos;
return 1;
}
}
*foundPos = mLength;
return 0;
}
int Fl_Text_Buffer::findchar_backward(int startPos, unsigned int searchChar,
int *foundPos) const {
if (startPos <= 0) {
*foundPos = 0;
return 0;
}
if (startPos > mLength)
startPos = mLength;
for (startPos = prev_char(startPos); startPos>=0; startPos = prev_char(startPos)) {
if (searchChar == char_at(startPos)) {
*foundPos = startPos;
return 1;
}
}
*foundPos = 0;
return 0;
}
#ifdef EXAMPLE_ENCODING
unsigned cp1252toucs(char* &p)
{
static unsigned cp1252[32] = {
0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178
};
unsigned char uc = *(unsigned char*)p;
p++;
return (uc < 0x80 || uc >= 0xa0 ? uc : cp1252[uc - 0x80]);
}
unsigned utf16toucs(char* &p)
{
union {
#if WORDS_BIGENDIAN
struct { unsigned char a, b;} chars;
#else
struct { unsigned char b, a;} chars;
#endif
U16 short_val;
} u;
u.chars.a = *(unsigned char*)p++;
u.chars.b = *(unsigned char*)p++;
return u.short_val;
}
static int general_input_filter(char *buffer, int buflen,
char *line, int sline, char* &endline,
unsigned (*p_trf)(char* &),
FILE *fp)
{
char *p, *q, multibyte[5];
int lq, r, offset;
p = line;
q = buffer;
while (q < buffer + buflen) {
if (p >= endline) {
r = fread(line, 1, sline, fp);
endline = line + r;
if (r == 0) return q - buffer;
p = line;
}
if (q + 4 > buffer + buflen) {
memmove(line, p, endline - p);
endline -= (p - line);
return q - buffer;
}
lq = fl_utf8encode( p_trf(p), multibyte );
memcpy(q, multibyte, lq);
q += lq;
}
memmove(line, p, endline - p);
endline -= (p - line);
return q - buffer;
}
#endif
static int utf8_input_filter(char *buffer, int buflen, char *line, int sline, char* &endline, FILE *fp, int *input_was_changed) {
char *p, *q, multibyte[5];
int l, lp, lq, r;
unsigned u;
p = line;
q = buffer;
while (q < buffer + buflen) {
if (p >= endline) { r = (int) fread(line, 1, sline, fp); endline = line + r;
if (r == 0) return (int) (q - buffer); p = line;
}
l = fl_utf8len1(*p); if (p + l > endline) { memmove(line, p, endline - p); endline -= (p - line);
r = (int) fread(endline, 1, sline - (endline - line), fp); endline += r;
p = line;
if (endline - line < l) break; }
while ( l > 0) {
u = fl_utf8decode(p, p+l, &lp); lq = fl_utf8encode(u, multibyte); if (lp != l || lq != l) *input_was_changed = true;
if (q + lq > buffer + buflen) { memmove(line, p, endline - p); endline -= (p - line); return (int) (q - buffer); }
memcpy(q, multibyte, lq);
q += lq;
p += lp;
l -= lp;
}
}
memmove(line, p, endline - p);
endline -= (p - line);
return (int) (q - buffer);
}
const char *Fl_Text_Buffer::file_encoding_warning_message =
"Displayed text contains the UTF-8 transcoding\n"
"of the input file which was not UTF-8 encoded.\n"
"Some changes may have occurred.";
int Fl_Text_Buffer::insertfile(const char *file, int pos, int buflen)
{
FILE *fp;
if (!(fp = fl_fopen(file, "r")))
return 1;
char *buffer = new char[buflen + 1];
char *endline, line[100];
int l;
input_file_was_transcoded = false;
endline = line;
while (true) {
#ifdef EXAMPLE_ENCODING
l = general_input_filter(buffer, buflen,
line, sizeof(line), endline,
utf16toucs, fp);
input_file_was_transcoded = true;
#else
l = utf8_input_filter(buffer, buflen, line, sizeof(line), endline,
fp, &input_file_was_transcoded);
#endif
if (l == 0) break;
buffer[l] = 0;
insert(pos, buffer);
pos += l;
}
int e = ferror(fp) ? 2 : 0;
fclose(fp);
delete[]buffer;
if ( (!e) && input_file_was_transcoded && transcoding_warning_action) {
transcoding_warning_action(this);
}
return e;
}
int Fl_Text_Buffer::outputfile(const char *file,
int start, int end,
int buflen) {
FILE *fp;
if (!(fp = fl_fopen(file, "w")))
return 1;
for (int n; (n = min(end - start, buflen)); start += n) {
const char *p = text_range(start, start + n);
int r = (int) fwrite(p, 1, n, fp);
free((void *) p);
if (r != n)
break;
}
int e = ferror(fp) ? 2 : 0;
fclose(fp);
return e;
}
int Fl_Text_Buffer::prev_char_clipped(int pos) const
{
if (pos<=0)
return 0;
IS_UTF8_ALIGNED2(this, (pos))
char c;
do {
pos--;
if (pos==0)
return 0;
c = byte_at(pos);
} while ( (c&0xc0) == 0x80);
IS_UTF8_ALIGNED2(this, (pos))
return pos;
}
int Fl_Text_Buffer::prev_char(int pos) const
{
if (pos==0) return -1;
return prev_char_clipped(pos);
}
int Fl_Text_Buffer::next_char(int pos) const
{
IS_UTF8_ALIGNED2(this, (pos))
int n = fl_utf8len1(byte_at(pos));
pos += n;
if (pos>=mLength)
return mLength;
IS_UTF8_ALIGNED2(this, (pos))
return pos;
}
int Fl_Text_Buffer::next_char_clipped(int pos) const
{
return next_char(pos);
}
int Fl_Text_Buffer::utf8_align(int pos) const
{
char c = byte_at(pos);
while ( (c&0xc0) == 0x80) {
pos--;
c = byte_at(pos);
}
return pos;
}