1261 lines
31 KiB
C++
1261 lines
31 KiB
C++
/**
|
|
* @author Edouard DUPIN
|
|
*
|
|
* @copyright 2010, Edouard DUPIN, all right reserved
|
|
*
|
|
* @license GPL v3 (see license file)
|
|
*/
|
|
|
|
#include <etk/unicode.h>
|
|
#include <appl/debug.h>
|
|
#include <appl/global.h>
|
|
#include <EdnBuf.h>
|
|
|
|
|
|
#undef __class__
|
|
#define __class__ "EdnBuf"
|
|
|
|
/**
|
|
* @brief convertion table for non printable control caracters
|
|
*/
|
|
static const char *ControlCodeTable[32] = {
|
|
"NUL", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "nl", "vt", "np", "cr", "so", "si",
|
|
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", "em", "sub", "esc", "fs", "gs", "rs", "us"};
|
|
|
|
|
|
|
|
/**
|
|
* @brief Constructor of the Edn buffer Text :
|
|
*
|
|
* Create an empty text buffer of a pre-determined size
|
|
*
|
|
* @param[in] requestedSize use this to avoid unnecessary re-allocation if you know exactly how much the buffer will need to hold
|
|
*
|
|
*/
|
|
EdnBuf::EdnBuf(void)
|
|
{
|
|
// TODO : set it configurable !!!
|
|
m_tabDist = 8;
|
|
m_useTabs = true;
|
|
|
|
// Current selection
|
|
m_selectionList.selected = false;
|
|
m_selectionList.zeroWidth = false;
|
|
m_selectionList.rectangular = false;
|
|
m_selectionList.start = m_selectionList.end = 0;
|
|
|
|
// charset :
|
|
m_isUtf8 = false;
|
|
m_charsetType = unicode::EDN_CHARSET_ISO_8859_15;
|
|
|
|
m_isUndoProcessing = false;
|
|
m_isRedoProcessing = false;
|
|
|
|
// basicly no HL system ...
|
|
m_Highlight = NULL;
|
|
m_nbLine = 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Destructor of the Edn buffer Text :
|
|
*/
|
|
EdnBuf::~EdnBuf(void)
|
|
{
|
|
// TODO : remove history and Future
|
|
}
|
|
|
|
/**
|
|
* @brief Save in the current file open
|
|
*
|
|
* @param[in,out] myFile pointer on the file where data might be writed
|
|
*
|
|
* @return true if OK / false if an error occured
|
|
*
|
|
*/
|
|
bool EdnBuf::DumpIn(etk::FSNode &file)
|
|
{
|
|
// write Data
|
|
return m_data.DumpIn(file);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief load in the current file open
|
|
*
|
|
* @param[in,out] myFile pointer on the file where data might be read
|
|
*
|
|
* @return true if OK / false if an error occured
|
|
*
|
|
*/
|
|
bool EdnBuf::DumpFrom(etk::FSNode &file)
|
|
{
|
|
if (true == m_data.DumpFrom(file) ) {
|
|
// set no selection
|
|
updateSelection(0, 0, m_data.size() );
|
|
// generate HighLight
|
|
CleanHighLight();
|
|
generateHighLightAt(0, m_data.size());
|
|
CountNumberOfLines();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void EdnBuf::getAll(etk::Vector<int8_t> &text)
|
|
{
|
|
// Clean output vector
|
|
text.clear();
|
|
// set data on the vector
|
|
text = m_data.get(0, m_data.size());
|
|
}
|
|
|
|
|
|
void EdnBuf::setAll(etk::Vector<int8_t> &text)
|
|
{
|
|
etk::Vector<int8_t> deletedText;
|
|
|
|
// extract all data of the buffer :
|
|
getAll(deletedText);
|
|
|
|
// remove all data in the buffer:
|
|
m_data.clear();
|
|
|
|
// inset text data :
|
|
m_data.insert(0, text);
|
|
|
|
// Zero all of the existing selections
|
|
updateSelection(0, deletedText.size(), 0);
|
|
|
|
// Call the modification Event Manager
|
|
eventModification(0, m_data.size(), deletedText);
|
|
}
|
|
|
|
void EdnBuf::getRange(int32_t start, int32_t end, etk::Vector<int8_t> &output)
|
|
{
|
|
// remove all data ...
|
|
output.clear();
|
|
// import data :
|
|
output = m_data.get(start, end-start);
|
|
//APPL_DEBUG("request start=" << start << " end="<< end << " size="<< end-start << " result size=" << output.size() );
|
|
}
|
|
|
|
void EdnBuf::getRange(int32_t start, int32_t end, etk::UString &output)
|
|
{
|
|
// remove all data ...
|
|
output = "";
|
|
// import data :
|
|
etk::Vector<int8_t> localOutput = m_data.get(start, end-start);
|
|
// transcript in UNICODE ...
|
|
if (true == m_isUtf8) {
|
|
localOutput.pushBack('\0');
|
|
output = (char*)&localOutput[0];
|
|
} else {
|
|
etk::Vector<etk::UChar> tmpUnicodeData;
|
|
// transform in unicode :
|
|
convertIsoToUnicode(m_charsetType, localOutput, tmpUnicodeData);
|
|
output = tmpUnicodeData;
|
|
}
|
|
//APPL_DEBUG("request start=" << start << " end="<< end << " size="<< end-start << " result size=" << output.size() );
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief get an element at the selected position
|
|
*
|
|
* @param[in] pos Charecters Position, [0..n]
|
|
*
|
|
* @return The character at buffer position "pos"
|
|
*
|
|
*/
|
|
int8_t EdnBuf::operator[] (int32_t pos) const
|
|
{
|
|
int8_t res = m_data.get(pos);
|
|
return res;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief insert Data in the Buffer
|
|
*
|
|
* @param[in] pos Position in the Buffer
|
|
* @param[in] insertText Text to insert
|
|
*
|
|
* @return ---
|
|
*
|
|
*/
|
|
int32_t EdnBuf::insert(int32_t pos, etk::Vector<int8_t> &insertText)
|
|
{
|
|
// if pos is not contiguous to existing text, make it
|
|
pos = etk_avg(0, pos, m_data.size() );
|
|
// insert Data
|
|
int32_t sizeInsert=LocalInsert(pos, insertText);
|
|
|
|
// Call the redisplay ...
|
|
etk::Vector<int8_t> deletedText;
|
|
eventModification(pos, insertText.size(), deletedText);
|
|
return sizeInsert;
|
|
}
|
|
int32_t EdnBuf::insert(int32_t pos, etk::UString &insertText)
|
|
{
|
|
// if pos is not contiguous to existing text, make it
|
|
pos = etk_avg(0, pos, m_data.size() );
|
|
// insert Data
|
|
int32_t sizeInsert=LocalInsert(pos, insertText);
|
|
|
|
// Call the redisplay ...
|
|
etk::Vector<int8_t> deletedText;
|
|
eventModification(pos, insertText.size(), deletedText);
|
|
return sizeInsert;
|
|
}
|
|
|
|
/**
|
|
* @brief Replace data in the buffer
|
|
*
|
|
* @param[in] start Position started in the buffer
|
|
* @param[in] end Position ended in the buffer
|
|
* @param[in] insertText Test to set in the range [start..end]
|
|
*
|
|
* @return nb Octet inserted
|
|
*
|
|
*/
|
|
int32_t EdnBuf::Replace(int32_t start, int32_t end, etk::Vector<int8_t> &insertText)
|
|
{
|
|
if (end-start == 0) {
|
|
return 0;
|
|
}
|
|
etk::Vector<int8_t> deletedText;
|
|
getRange(start, end, deletedText);
|
|
m_data.Replace(start, end-start, insertText);
|
|
// update internal elements
|
|
eventModification(start, insertText.size(), deletedText);
|
|
return insertText.size();
|
|
}
|
|
|
|
int32_t EdnBuf::Replace(int32_t start, int32_t end, etk::UString &insertText)
|
|
{
|
|
if (end-start == 0) {
|
|
return 0;
|
|
}
|
|
etk::Vector<int8_t> deletedText;
|
|
getRange(start, end, deletedText);
|
|
etk::Vector<int8_t> tmpInsertText;
|
|
if (true == m_isUtf8) {
|
|
etk::Char tmpChar = insertText.c_str();
|
|
const char * tmpPointer = tmpChar;
|
|
while (*tmpPointer != '\0') {
|
|
tmpInsertText.pushBack(*tmpPointer++);
|
|
}
|
|
} else {
|
|
etk::Vector<etk::UChar> tmppp = insertText.getVector();
|
|
convertUnicodeToIso(m_charsetType, tmppp, tmpInsertText);
|
|
}
|
|
if (tmpInsertText.size()>0) {
|
|
if (tmpInsertText[tmpInsertText.size()-1] == '\0') {
|
|
tmpInsertText.popBack();
|
|
}
|
|
}
|
|
if (tmpInsertText.size()>0) {
|
|
if (tmpInsertText[tmpInsertText.size()-1] == '\0') {
|
|
tmpInsertText.popBack();
|
|
}
|
|
}
|
|
m_data.Replace(start, end-start, tmpInsertText);
|
|
// update internal elements
|
|
eventModification(start, tmpInsertText.size(), deletedText);
|
|
return tmpInsertText.size();
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief remove data between [start..end]
|
|
*
|
|
* @param[in] start Position started in the buffer
|
|
* @param[in] end Position ended in the buffer
|
|
*
|
|
* @return ---
|
|
*
|
|
*/
|
|
void EdnBuf::remove(int32_t start, int32_t end)
|
|
{
|
|
|
|
etk::Vector<int8_t> deletedText;
|
|
// Make sure the arguments make sense
|
|
if (start > end) {
|
|
int32_t temp = start;
|
|
start = end;
|
|
end = temp;
|
|
}
|
|
start = etk_avg(0 , start, m_data.size());
|
|
end = etk_avg(0 , end, m_data.size());
|
|
|
|
// remove and redisplay
|
|
getRange(start, end, deletedText);
|
|
m_data.remove(start, end - start);
|
|
eventModification(start, 0, deletedText);
|
|
}
|
|
|
|
|
|
int32_t EdnBuf::Indent(void)
|
|
{
|
|
int32_t SelectionStart, SelectionEnd, SelectionRectStart, SelectionRectEnd;
|
|
bool SelectionIsRect;
|
|
bool haveSelectionActive = getSelectionPos(SelectionStart, SelectionEnd, SelectionIsRect, SelectionRectStart, SelectionRectEnd);
|
|
|
|
if (false == haveSelectionActive) {
|
|
return SelectionEnd;
|
|
}
|
|
// disable selection:
|
|
Unselect();
|
|
// get Range :
|
|
int32_t l_start = StartOfLine(SelectionStart);
|
|
int32_t l_end = EndOfLine(SelectionEnd);
|
|
etk::Vector<int8_t> l_tmpData;
|
|
getRange(l_start, l_end, l_tmpData);
|
|
|
|
l_tmpData.insert(0, '\n');
|
|
for (int32_t i=1; i<l_tmpData.size(); i++) {
|
|
if ('\n' == l_tmpData[i-1]) {
|
|
if (true == m_useTabs) {
|
|
l_tmpData.insert(i, '\t');
|
|
} else {
|
|
for (int32_t j=0; j<m_tabDist; j++) {
|
|
l_tmpData.insert(i, ' ');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
l_tmpData.Erase(0);
|
|
// Real replace of DATA :
|
|
Replace(l_start, l_end, l_tmpData);
|
|
// set the new selection :
|
|
l_end = l_start + l_tmpData.size();
|
|
Select(l_start, l_end);
|
|
// Return the position of the cursor
|
|
return l_end;
|
|
}
|
|
|
|
int32_t EdnBuf::UnIndent(void)
|
|
{
|
|
int32_t SelectionStart, SelectionEnd, SelectionRectStart, SelectionRectEnd;
|
|
bool SelectionIsRect;
|
|
bool haveSelectionActive = getSelectionPos(SelectionStart, SelectionEnd, SelectionIsRect, SelectionRectStart, SelectionRectEnd);
|
|
|
|
if (false == haveSelectionActive) {
|
|
return SelectionEnd;
|
|
}
|
|
// disable selection:
|
|
Unselect();
|
|
// get Range :
|
|
int32_t l_start = StartOfLine(SelectionStart);
|
|
int32_t l_end = EndOfLine(SelectionEnd);
|
|
etk::Vector<int8_t> l_tmpData;
|
|
getRange(l_start, l_end, l_tmpData);
|
|
|
|
l_tmpData.insert(0, '\n');
|
|
for (int32_t i=1; i<l_tmpData.size(); i++) {
|
|
if ('\n' == l_tmpData[i-1]) {
|
|
if('\t' == l_tmpData[i]) {
|
|
l_tmpData.Erase(i);
|
|
} else if(' ' == l_tmpData[i]) {
|
|
for (int32_t j=0; j<m_tabDist && j+i<l_tmpData.size() ; j++) {
|
|
if(' ' == l_tmpData[i]) {
|
|
l_tmpData.Erase(i);
|
|
} else if('\t' == l_tmpData[i]) {
|
|
l_tmpData.Erase(i);
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
l_tmpData.Erase(0);
|
|
// Real replace of DATA :
|
|
Replace(l_start, l_end, l_tmpData);
|
|
// set the new selection :
|
|
l_end = l_start + l_tmpData.size();
|
|
Select(l_start, l_end);
|
|
// Return the position of the cursor
|
|
return l_end;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief get the data of a specific line
|
|
*
|
|
* @param[in] pos Position in a line that might be geted
|
|
* @param[out] text Data in the current line at pos
|
|
*
|
|
* @return ---
|
|
*
|
|
*/
|
|
void EdnBuf::getLineText(int32_t pos, etk::Vector<int8_t> &text)
|
|
{
|
|
getRange( StartOfLine(pos), EndOfLine(pos), text);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief find the position of the start of the current line
|
|
*
|
|
* @param[in] pos position inside the line whe we need to find the start
|
|
*
|
|
* @return position of the start of the line
|
|
*
|
|
*/
|
|
int32_t EdnBuf::StartOfLine(int32_t pos)
|
|
{
|
|
int32_t startPos;
|
|
if (false == SearchBackward(pos, '\n', &startPos)) {
|
|
return 0;
|
|
}
|
|
return startPos + 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief find the position of the end of the current line
|
|
*
|
|
* @param[in] pos position inside the line whe we need to find the end
|
|
*
|
|
* @return position of the end of the line
|
|
*
|
|
*/
|
|
int32_t EdnBuf::EndOfLine(int32_t pos)
|
|
{
|
|
int32_t endPos;
|
|
if (false == SearchForward(pos, '\n', &endPos)) {
|
|
endPos = m_data.size();
|
|
}
|
|
return endPos;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Transform the current caracter in the buffer in a displayable vector of char
|
|
*
|
|
* @param[in,out] pos position of the char that might be converted (incremented to the next char (in case of UTF8)
|
|
* @param[in] indent Curent indentation befor the curent char
|
|
* @param[out] outUTF8 string of the displayed element
|
|
* @param[out] currentChar curent unicode output char
|
|
*
|
|
* @return number of displayable char (display char width)
|
|
*
|
|
*/
|
|
int32_t EdnBuf::getExpandedChar(int32_t &pos, int32_t indent, char outUTF8[MAX_EXP_CHAR_LEN], uint32_t ¤tChar)
|
|
{
|
|
int32_t i, nSpaces;
|
|
char c = m_data.get(pos);
|
|
currentChar = (uint32_t)c & 0xFF;
|
|
/* convert tabs to spaces */
|
|
if (c == '\t') {
|
|
nSpaces = m_tabDist - (indent % m_tabDist);
|
|
for (i=0; i<nSpaces; i++) {
|
|
outUTF8[i] = ' ';
|
|
}
|
|
outUTF8[i] = '\0';
|
|
pos++;
|
|
return nSpaces;
|
|
}
|
|
|
|
// convert ASCII control codes to readable character sequences
|
|
if (c == '\0') {
|
|
outUTF8[0] = '<';
|
|
outUTF8[1] = 'n';
|
|
outUTF8[2] = 'u';
|
|
outUTF8[3] = 'l';
|
|
outUTF8[4] = '>';
|
|
outUTF8[5] = '\0';
|
|
pos++;
|
|
return 5;
|
|
}
|
|
if (((unsigned char)c) == '\n') {
|
|
outUTF8[0] = (unsigned char)c;
|
|
outUTF8[1] = '\0';
|
|
pos++;
|
|
return 1;
|
|
}
|
|
if (((unsigned char)c) <= 31) {
|
|
const char * tmp = ControlCodeTable[(unsigned char)c];
|
|
int32_t nbElem = 2;
|
|
*outUTF8++ = '<';
|
|
while (*tmp!='\0') {
|
|
*outUTF8++ = *tmp;
|
|
tmp++;
|
|
nbElem++;
|
|
}
|
|
*outUTF8++ = '>';
|
|
*outUTF8++ = '\0';
|
|
pos++;
|
|
return nbElem;
|
|
} else if (c == 127) {
|
|
outUTF8[0] = '<';
|
|
outUTF8[1] = 'd';
|
|
outUTF8[2] = 'e';
|
|
outUTF8[3] = 'l';
|
|
outUTF8[4] = '>';
|
|
outUTF8[5] = '\0';
|
|
pos++;
|
|
return 5;
|
|
}
|
|
|
|
// Otherwise, just return the character
|
|
if (m_isUtf8 == false) {
|
|
convertIsoToUtf8(m_charsetType, c, outUTF8);
|
|
pos++;
|
|
} else {
|
|
char tmpString[8];
|
|
for (int32_t k=0; k<6 && k< m_data.size() - pos; k++) {
|
|
tmpString[k] = m_data.get(pos+k);
|
|
}
|
|
tmpString[6] = '\0';
|
|
uint8_t size;
|
|
bool baseValid;
|
|
unicode::Utf8_SizeElement(tmpString, 6 , size, baseValid);
|
|
currentChar = 0; // TODO : set UNICODE char ...
|
|
if (baseValid == true) {
|
|
char *tmp = outUTF8;
|
|
for (int32_t kkk=0; kkk<size; kkk++) {
|
|
*tmp++ = tmpString[kkk];
|
|
}
|
|
*tmp = '\0';
|
|
} else {
|
|
sprintf(outUTF8, "<? ? ? ?>");
|
|
}
|
|
if (0 == size) {
|
|
APPL_ERROR("plop");
|
|
}
|
|
pos+=size;
|
|
return strlen(outUTF8);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Transform the current caracter in the buffer in a displayable vector of char
|
|
*
|
|
* @param[in,out] pos position of the char that might be converted (incremented to the next char (in case of UTF8)
|
|
* @param[in] indent Curent indentation befor the curent char
|
|
* @param[out] outUTF8 string of the displayed element
|
|
* @param[out] currentChar curent unicode output char
|
|
*
|
|
* @return number of displayable char (display char width)
|
|
*
|
|
*/
|
|
int32_t EdnBuf::getExpandedChar(int32_t &pos, int32_t indent, etk::UChar outUnicode[MAX_EXP_CHAR_LEN], uint32_t ¤tChar)
|
|
{
|
|
int32_t i, nSpaces;
|
|
char c = m_data.get(pos);
|
|
currentChar = (uint32_t)c & 0xFF;
|
|
/* convert tabs to spaces */
|
|
if (c == '\t') {
|
|
nSpaces = m_tabDist - (indent % m_tabDist);
|
|
for (i=0; i<nSpaces; i++) {
|
|
outUnicode[i] = ' ';
|
|
}
|
|
outUnicode[i] = '\0';
|
|
pos++;
|
|
return nSpaces;
|
|
}
|
|
|
|
// convert ASCII control codes to readable character sequences
|
|
if (c == '\0') {
|
|
outUnicode[0] = '<';
|
|
outUnicode[1] = 'n';
|
|
outUnicode[2] = 'u';
|
|
outUnicode[3] = 'l';
|
|
outUnicode[4] = '>';
|
|
outUnicode[5] = '\0';
|
|
pos++;
|
|
return 5;
|
|
}
|
|
if (((unsigned char)c) == '\n') {
|
|
outUnicode[0] = (unsigned char)c;
|
|
outUnicode[1] = '\0';
|
|
pos++;
|
|
return 1;
|
|
}
|
|
if (((unsigned char)c) <= 31) {
|
|
const char * tmp = ControlCodeTable[(unsigned char)c];
|
|
int32_t nbElem = 2;
|
|
*outUnicode++ = '<';
|
|
while (*tmp!='\0') {
|
|
*outUnicode++ = *tmp;
|
|
tmp++;
|
|
nbElem++;
|
|
}
|
|
*outUnicode++ = '>';
|
|
*outUnicode++ = '\0';
|
|
pos++;
|
|
return nbElem;
|
|
} else if (c == 127) {
|
|
outUnicode[0] = '<';
|
|
outUnicode[1] = 'd';
|
|
outUnicode[2] = 'e';
|
|
outUnicode[3] = 'l';
|
|
outUnicode[4] = '>';
|
|
outUnicode[5] = '\0';
|
|
pos++;
|
|
return 5;
|
|
}
|
|
// clear all the data ...
|
|
memset(outUnicode, 0, sizeof(etk::UChar)*MAX_EXP_CHAR_LEN);
|
|
|
|
// Otherwise, just return the character
|
|
if (false == m_isUtf8) {
|
|
unicode::convertIsoToUnicode(m_charsetType, c, outUnicode[0]);
|
|
pos++;
|
|
} else {
|
|
char tmpString[8];
|
|
for (int32_t k=0; k<6 && k< m_data.size() - pos; k++) {
|
|
tmpString[k] = m_data.get(pos+k);
|
|
}
|
|
tmpString[6] = '\0';
|
|
uint8_t size;
|
|
bool baseValid;
|
|
unicode::Utf8_SizeElement(tmpString, 6 , size, baseValid);
|
|
currentChar = 0;
|
|
if (true == baseValid) {
|
|
char tmp[20];
|
|
for (int32_t kkk=0; kkk<size; kkk++) {
|
|
tmp[kkk] = tmpString[kkk];
|
|
tmp[kkk+1] = '\0';
|
|
}
|
|
outUnicode[0].setUtf8(tmp);
|
|
outUnicode[1] = 0;
|
|
} else {
|
|
outUnicode[0] = '<';
|
|
outUnicode[1] = '?';
|
|
outUnicode[2] = '?';
|
|
outUnicode[3] = '?';
|
|
outUnicode[4] = '>';
|
|
outUnicode[5] = 0;
|
|
}
|
|
if (0 == size) {
|
|
APPL_ERROR("plop");
|
|
}
|
|
pos+=size;
|
|
int32_t outSize = 0;
|
|
while (outUnicode[outSize]!=0) {
|
|
outSize++;
|
|
}
|
|
return outSize;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief generate the real display of character of the output (ex : \t == > 4 spaces or less ...)
|
|
*
|
|
* @param[in] c Char that might be converted
|
|
* @param[in] indent Curent indentation befor the curent char
|
|
* @param[out] outStr string of the displayed element
|
|
*
|
|
* @return size of the display
|
|
*
|
|
*/
|
|
int32_t EdnBuf::ExpandCharacter(char c, int32_t indent, char outUTF8[MAX_EXP_CHAR_LEN])
|
|
{
|
|
int32_t i, nSpaces;
|
|
|
|
/* convert tabs to spaces */
|
|
if (c == '\t') {
|
|
nSpaces = m_tabDist - (indent % m_tabDist);
|
|
for (i=0; i<nSpaces; i++) {
|
|
outUTF8[i] = ' ';
|
|
}
|
|
outUTF8[i] = '\0';
|
|
return nSpaces;
|
|
}
|
|
|
|
// convert ASCII control codes to readable character sequences
|
|
if (c == '\0') {
|
|
outUTF8[0] = '<';
|
|
outUTF8[1] = 'n';
|
|
outUTF8[2] = 'u';
|
|
outUTF8[3] = 'l';
|
|
outUTF8[4] = '>';
|
|
outUTF8[5] = '\0';
|
|
return 5;
|
|
}
|
|
if (((unsigned char)c) <= 31) {
|
|
const char * tmp = ControlCodeTable[(unsigned char)c];
|
|
*outUTF8++ = '<';
|
|
while (*tmp!='\0') {
|
|
*outUTF8++ = *tmp;
|
|
tmp++;
|
|
}
|
|
*outUTF8++ = '>';
|
|
*outUTF8++ = '\0';
|
|
return ControlCodeTable[(unsigned char)c] - tmp + 2;
|
|
} else if (c == 127) {
|
|
outUTF8[0] = '<';
|
|
outUTF8[1] = 'd';
|
|
outUTF8[2] = 'e';
|
|
outUTF8[3] = 'l';
|
|
outUTF8[4] = '>';
|
|
outUTF8[5] = '\0';
|
|
return 5;
|
|
}
|
|
|
|
// Otherwise, just return the character
|
|
//*outStr = c; // deprecated
|
|
convertIsoToUtf8(unicode::EDN_CHARSET_ISO_8859_15, c, outUTF8);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @brief generate the size of the display of one element
|
|
*
|
|
* @param[in] c Char that might be converted
|
|
* @param[in] indent Curent indentation befor the curent char
|
|
*
|
|
* @return size of the display
|
|
*
|
|
*/
|
|
int32_t EdnBuf::CharWidth(char c, int32_t indent)
|
|
{
|
|
/* Note, this code must parallel that in ExpandCharacter */
|
|
if (c == '\0') {
|
|
return 5;
|
|
} else if (c == '\t') {
|
|
return m_tabDist - (indent % m_tabDist);
|
|
} else if (((unsigned char)c) <= 31) {
|
|
return strlen(ControlCodeTable[(unsigned char)c]) + 2;
|
|
} else if (c == 127) {
|
|
return 5;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Count the number of displayed characters between buffer position
|
|
*
|
|
* displayed characters are the characters shown on the screen to represent characters in the
|
|
* buffer, where tabs and control characters are expanded
|
|
*
|
|
* @param[in] lineStartPos Start position in the line
|
|
* @param[in] targetPos displayed target position in char
|
|
*
|
|
* @return the ID in the buffer of the requested char
|
|
*
|
|
*/
|
|
int32_t EdnBuf::CountDispChars(int32_t lineStartPos, int32_t targetPos)
|
|
{
|
|
int32_t charCount = 0;
|
|
char expandedChar[MAX_EXP_CHAR_LEN];
|
|
//APPL_DEBUG("lineStartPos="<< lineStartPos << " targetPos=" << targetPos);
|
|
for(int32_t iii = lineStartPos; iii< targetPos && iii<m_data.size() ; iii++ ) {
|
|
charCount += ExpandCharacter(m_data[iii], charCount, expandedChar);
|
|
}
|
|
//APPL_DEBUG(" result=" << charCount);
|
|
return charCount;
|
|
}
|
|
|
|
/**
|
|
* @brief Return the position of the nth diplaye char
|
|
* @param[in] lineStartPos Position of the start of the line
|
|
* @param[in] nChars search in the next nChars elements
|
|
* @return number of diaplay char
|
|
*/
|
|
int32_t EdnBuf::CountForwardDispChars(int32_t lineStartPos, int32_t nChars)
|
|
{
|
|
int32_t charCount = 0;
|
|
int32_t iii = 0;
|
|
for(iii = lineStartPos; charCount<nChars && iii<m_data.size() ; iii++ ) {
|
|
char c = m_data[iii];
|
|
if (c == '\n') {
|
|
return iii;
|
|
}
|
|
charCount += CharWidth(c, charCount);
|
|
}
|
|
return iii;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Count the number of newlines ('\n') between startPos and endPos
|
|
* @param[in,out] startPos Fist position in the buffer
|
|
* @param[in,out] endPos Last position in the buffer (not counted)
|
|
*/
|
|
int32_t EdnBuf::CountLines(int32_t startPos, int32_t endPos)
|
|
{
|
|
int32_t lineCount = 0;
|
|
for(int32_t iii = startPos; iii<m_data.size() ; iii++ ) {
|
|
if (iii == endPos) {
|
|
return lineCount;
|
|
}
|
|
if ('\n' == m_data[iii]) {
|
|
lineCount++;
|
|
}
|
|
}
|
|
return lineCount;
|
|
}
|
|
|
|
/**
|
|
* @brief count the number of '\n' in the vector of elements
|
|
* @param[in] text Data to count the lines
|
|
* @return number of line found
|
|
*/
|
|
int32_t EdnBuf::CountLines(etk::Vector<int8_t> &data)
|
|
{
|
|
int32_t lineCount = 0;
|
|
for(int32_t iii=0 ; iii<data.size() ; iii++ ) {
|
|
if ('\n' == data[iii]) {
|
|
lineCount++;
|
|
}
|
|
}
|
|
return lineCount;
|
|
}
|
|
|
|
int32_t EdnBuf::CountLines(void)
|
|
{
|
|
int32_t lineCount = 0;
|
|
for(int32_t iii=0 ; iii<m_data.size() ; iii++ ) {
|
|
if ('\n' == m_data[iii]) {
|
|
lineCount++;
|
|
}
|
|
}
|
|
return lineCount;
|
|
}
|
|
|
|
/**
|
|
* @brief calculate the number of line
|
|
* @return the number of line in the buffer [1..n]
|
|
*/
|
|
void EdnBuf::CountNumberOfLines(void)
|
|
{
|
|
m_nbLine = CountLines(0, m_data.size());
|
|
m_nbLine++;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief find the first character of the line "nLines" forward
|
|
*
|
|
* @param[in,out] startPos Start position to count
|
|
* @param[in,out] nLines number of line to count
|
|
*
|
|
* @return position of the starting the line
|
|
*
|
|
*/
|
|
int32_t EdnBuf::CountForwardNLines(int32_t startPos, int32_t nLines)
|
|
{
|
|
if (nLines == 0) {
|
|
return startPos;
|
|
} else if (startPos > m_data.size() ) {
|
|
return m_data.size();
|
|
}
|
|
int32_t lineCount = 0;
|
|
//APPL_INFO("startPos=" << startPos << " nLines=" << nLines);
|
|
int32_t iii = 0;
|
|
for(iii = startPos; iii<m_data.size() ; iii++ ) {
|
|
if ('\n' == m_data[iii]) {
|
|
lineCount++;
|
|
if (lineCount == nLines) {
|
|
//APPL_INFO(" == > (1) at position=" << myPosIt.Position()+1 );
|
|
return iii+1;
|
|
}
|
|
}
|
|
}
|
|
//APPL_INFO(" == > (2) at position=" << myPosIt.Position() );
|
|
return iii;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief find the first character of the line "nLines" backwards
|
|
*
|
|
* @param[in,out] startPos Start position to count (this caracter is not counted)
|
|
* @param[in,out] nLines number of line to count (if == 0 means find the beginning of the line)
|
|
*
|
|
* @return position of the starting the line
|
|
*
|
|
*/
|
|
int32_t EdnBuf::CountBackwardNLines(int32_t startPos, int32_t nLines)
|
|
{
|
|
if (startPos <= 0) {
|
|
return 0;
|
|
} else if (startPos > m_data.size() ) {
|
|
startPos = m_data.size();
|
|
}
|
|
//APPL_INFO("startPos=" << startPos << " nLines=" << nLines);
|
|
|
|
int32_t lineCount = -1;
|
|
for(int32_t iii = startPos-1; iii >= 0 ; iii-- ) {
|
|
if ('\n' == m_data[iii]) {
|
|
lineCount++;
|
|
if (lineCount >= nLines) {
|
|
//APPL_INFO(" == > (1) at position=" << myPosIt.Position()+1 );
|
|
return iii+1;
|
|
}
|
|
}
|
|
}
|
|
//APPL_INFO(" == > (2) at position=0");
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool EdnBuf::charMatch(char first, char second, bool caseSensitive)
|
|
{
|
|
if (false == caseSensitive) {
|
|
if ('A' <= first && 'Z' >= first) {
|
|
first = first - 'A' + 'a';
|
|
}
|
|
if ('A' <= second && 'Z' >= second) {
|
|
second = second - 'A' + 'a';
|
|
}
|
|
}
|
|
if(first == second) {
|
|
//APPL_DEBUG("charMatch(" << first << ", " << second << ", " << caseSensitive << ") == > true");
|
|
return true;
|
|
} else {
|
|
//APPL_DEBUG("charMatch(" << first << ", " << second << ", " << caseSensitive << ") == > false");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Search forwards in buffer
|
|
*
|
|
* @param[in] startPos Position to start the search
|
|
* @param[in] searchVect String to search
|
|
* @param[out] foundPos Current position founded
|
|
*
|
|
* @return true == > found data
|
|
* @return false == > not found data
|
|
*
|
|
*/
|
|
bool EdnBuf::SearchForward(int32_t startPos, etk::UString &search, int32_t *foundPos, int32_t *foundPosEnd, bool caseSensitive)
|
|
{
|
|
etk::Vector<int8_t> searchVect;
|
|
if (true == m_isUtf8) {
|
|
etk::Char tmpChar = search.c_str();
|
|
const char * tmpPointer = tmpChar;
|
|
while (*tmpPointer != '\0') {
|
|
searchVect.pushBack(*tmpPointer++);
|
|
}
|
|
} else {
|
|
etk::Vector<etk::UChar> tmppp = search.getVector();
|
|
convertUnicodeToIso(m_charsetType, tmppp, searchVect);
|
|
}
|
|
// remove the '\0' at the end of the string ...
|
|
searchVect.popBack();
|
|
int32_t position;
|
|
int32_t searchLen = searchVect.size();
|
|
int32_t dataLen = m_data.size();
|
|
char currentChar = '\0';
|
|
APPL_INFO(" startPos=" << startPos << " searchLen=" << searchLen);
|
|
for (position=startPos; position<dataLen - (searchLen-1); position++) {
|
|
currentChar = m_data[position];
|
|
if (true == charMatch(currentChar, searchVect[0], caseSensitive)) {
|
|
int32_t i;
|
|
bool found = true;
|
|
for (i=1; i<searchLen; i++) {
|
|
currentChar = m_data[position + i];
|
|
if (false == charMatch(currentChar, searchVect[i], caseSensitive)) {
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
if (true == found) {
|
|
*foundPos = position;
|
|
*foundPosEnd = position + searchVect.size();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
*foundPos = m_data.size();
|
|
*foundPosEnd = m_data.size();
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Search backwards in buffer
|
|
*
|
|
* @param[in] startPos Position to start the search
|
|
* @param[in] searchChars String to search
|
|
* @param[out] foundPos Current position founded
|
|
*
|
|
* @return true == > found data
|
|
* @return false == > not found data
|
|
*
|
|
*/
|
|
bool EdnBuf::SearchBackward(int32_t startPos, etk::UString &search, int32_t *foundPos, int32_t *foundPosEnd, bool caseSensitive)
|
|
{
|
|
etk::Vector<int8_t> searchVect;
|
|
if (true == m_isUtf8) {
|
|
etk::Char tmpChar = search.c_str();
|
|
const char * tmpPointer = tmpChar;
|
|
while (*tmpPointer != '\0') {
|
|
searchVect.pushBack(*tmpPointer++);
|
|
}
|
|
} else {
|
|
etk::Vector<etk::UChar> tmppp = search.getVector();
|
|
convertUnicodeToIso(m_charsetType, tmppp, searchVect);
|
|
}
|
|
// remove the '\0' at the end of the string ...
|
|
searchVect.popBack();
|
|
|
|
int32_t position;
|
|
int32_t searchLen = searchVect.size();
|
|
char currentChar = '\0';
|
|
//APPL_INFO(" startPos=" << startPos << " searchLen=" << searchLen);
|
|
for (position=startPos; position >= searchLen-1; position--) {
|
|
currentChar = m_data[position];
|
|
if (true == charMatch(currentChar, searchVect[searchLen-1], caseSensitive)) {
|
|
int32_t i;
|
|
bool found = true;
|
|
for (i=searchLen-1; i >= 0; i--) {
|
|
currentChar = m_data[position - (searchLen-1) + i];
|
|
if (false == charMatch(currentChar, searchVect[i], caseSensitive)) {
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
if (true == found) {
|
|
*foundPos = position - (searchLen-1);
|
|
*foundPosEnd = position + searchVect.size();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
*foundPos = m_data.size();
|
|
*foundPosEnd = m_data.size();
|
|
return false;
|
|
}
|
|
|
|
static bool isChar(char value)
|
|
{
|
|
if( ('a' <= value && 'z' >= value)
|
|
|| ('A' <= value && 'Z' >= value)
|
|
|| ('0' <= value && '9' >= value)
|
|
|| '_' == value
|
|
|| '~' == value)
|
|
{
|
|
//APPL_DEBUG(" is a char \"" << value << "\"");
|
|
return true;
|
|
}
|
|
//APPL_DEBUG(" is NOT a char \"" << value << "\"");
|
|
return false;
|
|
}
|
|
|
|
|
|
bool EdnBuf::SelectAround(int32_t startPos, int32_t &beginPos, int32_t &endPos)
|
|
{
|
|
char currentChar = m_data[startPos];
|
|
if( '\t' == currentChar
|
|
|| ' ' == currentChar)
|
|
{
|
|
APPL_DEBUG("select spacer");
|
|
// special case we are looking for separation
|
|
for (beginPos=startPos; beginPos >= 0; beginPos--) {
|
|
currentChar = m_data[beginPos];
|
|
if( '\t' != currentChar
|
|
&& ' ' != currentChar)
|
|
{
|
|
beginPos++;
|
|
break;
|
|
}
|
|
}
|
|
// special case we are looking for separation
|
|
for (endPos=startPos; endPos<m_data.size(); endPos++) {
|
|
currentChar = m_data[endPos];
|
|
if( '\t' != currentChar
|
|
&& ' ' != currentChar)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
} else if( true == isChar(currentChar)){
|
|
APPL_DEBUG("select normal Char");
|
|
// Search back
|
|
for (beginPos=startPos; beginPos >= 0; beginPos--) {
|
|
currentChar = m_data[beginPos];
|
|
if( false == isChar(currentChar)) {
|
|
beginPos++;
|
|
break;
|
|
}
|
|
}
|
|
// Search forward
|
|
for (endPos=startPos; endPos<m_data.size(); endPos++) {
|
|
currentChar = m_data[endPos];
|
|
if( false == isChar(currentChar)) {
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
char comparechar = currentChar;
|
|
APPL_DEBUG("select same char");
|
|
// Search back
|
|
for (beginPos=startPos; beginPos >= 0; beginPos--) {
|
|
currentChar = m_data[beginPos];
|
|
if(comparechar != currentChar)
|
|
{
|
|
beginPos++;
|
|
break;
|
|
}
|
|
}
|
|
// Search forward
|
|
for (endPos=startPos; endPos<m_data.size(); endPos++) {
|
|
currentChar = m_data[endPos];
|
|
if(comparechar != currentChar)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
beginPos = 0;
|
|
endPos = 0;
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief insert data in the buffer binary and call all needed functions.
|
|
*
|
|
* @param[in] pos Position to insert data in the buffer.
|
|
* @param[in] insertText Data to insert.
|
|
*
|
|
* @return number of element inserted.
|
|
*
|
|
*/
|
|
int32_t EdnBuf::LocalInsert(int32_t pos, etk::Vector<int8_t> &insertText)
|
|
{
|
|
// insert data in buffer
|
|
m_data.insert(pos, insertText);
|
|
// update the current selected area
|
|
updateSelection(pos, 0, insertText.size() );
|
|
// return the number of element inserted ...
|
|
return insertText.size();
|
|
}
|
|
int32_t EdnBuf::LocalInsert(int32_t pos, etk::UString &insertText)
|
|
{
|
|
etk::Vector<int8_t> tmpInsertText;
|
|
if (true == m_isUtf8) {
|
|
etk::Char tmpChar = insertText.c_str();
|
|
const char * tmpPointer = tmpChar;
|
|
while (*tmpPointer != '\0') {
|
|
tmpInsertText.pushBack(*tmpPointer++);
|
|
}
|
|
} else {
|
|
etk::Vector<etk::UChar> tmppp = insertText.getVector();
|
|
convertUnicodeToIso(m_charsetType, tmppp, tmpInsertText);
|
|
}
|
|
if (tmpInsertText.size()>0) {
|
|
if (tmpInsertText[tmpInsertText.size()-1] == '\0') {
|
|
tmpInsertText.popBack();
|
|
}
|
|
}
|
|
return LocalInsert(pos, tmpInsertText);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief when modification appeare in the buffer we save it in the undo vector...
|
|
*
|
|
* @param[in] pos position of the add or remove
|
|
* @param[in] nInserted nb element inserted
|
|
* @param[in] deletedText Deleted elevent in a vector
|
|
*
|
|
* @return ---
|
|
*
|
|
*/
|
|
void EdnBuf::eventModification(int32_t pos, int32_t nInserted, etk::Vector<int8_t> &deletedText)
|
|
{
|
|
if( 0 == deletedText.size()
|
|
&& 0 == nInserted)
|
|
{
|
|
// we do nothing ...
|
|
//APPL_INFO("EdnBuf::eventModification(pos="<<pos<<", ... , nRestyled=" << nRestyled << ", deletedText=\"" << textDisplay << "\");");
|
|
} else {
|
|
APPL_INFO("(pos="<<pos<<", nDeleted="<<deletedText.size()<<", nInserted=" << nInserted << ", deletedText=\"xx???xx\");");
|
|
// update the number of lines :
|
|
//APPL_INFO(" add=" << CountLines(pos, pos+nInserted) << " lines | remove="<< CountLines(deletedText) << " lines");
|
|
m_nbLine += CountLines(pos, pos+nInserted) - CountLines(deletedText);
|
|
// update histories
|
|
if (false == m_isUndoProcessing) {
|
|
// normal or Redo processing
|
|
EdnBufHistory *exempleHistory = new EdnBufHistory(pos, nInserted, deletedText);
|
|
m_historyUndo.pushBack(exempleHistory);
|
|
if (false == m_isRedoProcessing) {
|
|
// remove all element in the redo system ...
|
|
int32_t i;
|
|
for (i=m_historyRedo.size()-1; i >= 0; i--) {
|
|
if (NULL != m_historyRedo[i]) {
|
|
delete(m_historyRedo[i]);
|
|
}
|
|
m_historyRedo.popBack();
|
|
}
|
|
}
|
|
} else {
|
|
// undo processing == > add element in Redo vector ...
|
|
EdnBufHistory *exempleHistory = new EdnBufHistory(pos, nInserted, deletedText);
|
|
m_historyRedo.pushBack(exempleHistory);
|
|
}
|
|
// Regenerate the Highlight :
|
|
RegenerateHighLightAt(pos, deletedText.size(), nInserted);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* @brief Search a character in the current buffer
|
|
*
|
|
* @param[in] startPos Position to start the search of the element
|
|
* @param[in] searchChar Character to search
|
|
* @param[out] foundPos Position where it was found
|
|
*
|
|
* @return true when find element
|
|
*
|
|
*/
|
|
bool EdnBuf::SearchForward(int32_t startPos, char searchChar, int32_t *foundPos)
|
|
{
|
|
// move in the string
|
|
for(int32_t iii=startPos ; iii<m_data.size() ; iii++ ) {
|
|
if (m_data[iii] == searchChar) {
|
|
*foundPos = iii;
|
|
return true;
|
|
}
|
|
}
|
|
*foundPos = m_data.size();
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Search a character in the current buffer (backward
|
|
*
|
|
* @param[in] startPos Position to start the search of the element
|
|
* @param[in] searchChar Character to search
|
|
* @param[out] foundPos Position where it was found
|
|
*
|
|
* @return true when find element
|
|
*
|
|
*/
|
|
bool EdnBuf::SearchBackward(int32_t startPos, char searchChar, int32_t *foundPos)
|
|
{
|
|
// move in the string
|
|
for(int32_t iii=startPos-1 ; iii >= 0 ; iii-- ) {
|
|
if (m_data[iii] == searchChar) {
|
|
*foundPos = iii;
|
|
return true;
|
|
}
|
|
}
|
|
*foundPos = 0;
|
|
return false;
|
|
}
|
|
|
|
|