support writing of editor contents to file

This commit is contained in:
Vadim Lopatin 2015-01-19 14:32:09 +03:00
parent 00e4d207d4
commit ec0f7ea8f4
2 changed files with 134 additions and 15 deletions

View File

@ -208,6 +208,7 @@ class OutputLineStream {
}
/// close stream
void close() {
flush();
_stream.close();
_buf = null;
}
@ -243,6 +244,10 @@ class LineStream {
private uint _textLen; // position of last filled char in text buffer + 1
private dchar[] _textBuf; // text buffer
private bool _eof; // end of file, no more lines
protected bool _bomDetected;
protected int _crCount;
protected int _lfCount;
protected int _crlfCount;
/// Returns file name
@property string filename() { return _filename; }
@ -250,6 +255,25 @@ class LineStream {
@property uint line() { return _line; }
/// Returns file encoding EncodingType
@property EncodingType encoding() { return _encoding; }
@property TextFileFormat textFormat() {
LineEnding le = LineEnding.CRLF;
if (_crlfCount) {
if (_crCount == _lfCount)
le = LineEnding.CRLF;
else
le = LineEnding.MIXED;
} else if (_crCount > _lfCount) {
le = LineEnding.CR;
} else if (_lfCount > _crCount) {
le = LineEnding.CR;
} else {
le = LineEnding.MIXED;
}
return TextFileFormat(_encoding, le, _bomDetected);
}
/// Returns error code
@property int errorCode() { return _errorCode; }
/// Returns error message
@ -397,15 +421,21 @@ class LineStream {
charsLeft = _textLen - _textPos;
}
dchar ch2 = (p < charsLeft - 1) ? _textBuf[_textPos + p + 1] : 0;
if (ch2 == 0x0A)
if (ch2 == 0x0A) {
eol = p + 2;
else
_lfCount++;
_crCount++;
_crlfCount++;
} else {
eol = p + 1;
_lfCount++;
}
break;
} else if (ch == 0x0A || ch == 0x2028 || ch == 0x2029) {
// single char eoln
lastchar = p;
eol = p + 1;
_crCount++;
break;
} else if (ch == 0 || ch == 0x001A) {
// eof
@ -450,25 +480,34 @@ class LineStream {
}
/// Factory for InputStream parser
public static LineStream create(InputStream stream, string filename) {
public static LineStream create(InputStream stream, string filename, bool autodetectUTFIfNoBOM = true) {
ubyte[] buf = new ubyte[BYTE_BUFFER_SIZE];
buf[0] = buf[1] = buf[2] = buf[3] = 0;
if (!stream.isOpen)
return null;
uint len = cast(uint)stream.read(buf);
LineStream res = null;
if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) {
return new Utf8LineStream(stream, filename, buf, len);
res = new Utf8LineStream(stream, filename, buf, len);
} else if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF) {
return new Utf32beLineStream(stream, filename, buf, len);
res = new Utf32beLineStream(stream, filename, buf, len);
} else if (buf[0] == 0xFF && buf[1] == 0xFE && buf[2] == 0x00 && buf[3] == 0x00) {
return new Utf32leLineStream(stream, filename, buf, len);
res = new Utf32leLineStream(stream, filename, buf, len);
} else if (buf[0] == 0xFE && buf[1] == 0xFF) {
return new Utf16beLineStream(stream, filename, buf, len);
res = new Utf16beLineStream(stream, filename, buf, len);
} else if (buf[0] == 0xFF && buf[1] == 0xFE) {
return new Utf16leLineStream(stream, filename, buf, len);
} else {
return new AsciiLineStream(stream, filename, buf, len);
res = new Utf16leLineStream(stream, filename, buf, len);
}
if (res) {
res._bomDetected = true;
} else {
if (autodetectUTFIfNoBOM) {
res = new Utf8LineStream(stream, filename, buf, len);
} else {
res = new AsciiLineStream(stream, filename, buf, len);
}
}
return res;
}
protected bool invalidCharFlag;

View File

@ -27,6 +27,7 @@ import dlangui.widgets.controls;
import dlangui.widgets.scroll;
import dlangui.core.signals;
import dlangui.core.collections;
import dlangui.core.linestream;
import dlangui.platforms.common.platform;
import dlangui.widgets.menu;
import dlangui.widgets.popup;
@ -256,7 +257,10 @@ enum EditAction {
/// delete content in range
//Delete,
/// replace range content with new content
Replace
Replace,
/// replace whole content
ReplaceContent,
}
/// edit operation details for EditableContent
@ -435,6 +439,14 @@ class EditableContent {
}
return cast(dstring)buf;
}
/// call listener to say that whole content is replaced e.g. by loading from file
void notifyContentReplaced() {
TextRange rangeBefore;
TextRange rangeAfter;
handleContentChange(new EditOperation(EditAction.ReplaceContent), rangeBefore, rangeAfter, this);
}
/// replace whole text with another content
@property EditableContent text(dstring newContent) {
clearUndo();
@ -445,6 +457,7 @@ class EditableContent {
_lines.length = 1;
_lines[0] = replaceEolsWithSpaces(newContent);
}
notifyContentReplaced();
return this;
}
@ -817,10 +830,22 @@ class EditableContent {
void clearUndo() {
_undoBuffer.clear();
}
protected string _filename;
protected TextFileFormat _format;
/// file used to load editor content
@property string filename() {
return _filename;
}
/// load content form input stream
bool load(InputStream f, string fname = null) {
import dlangui.core.linestream;
clear();
_filename = fname;
_format = TextFileFormat.init;
try {
LineStream lines = LineStream.create(f, fname);
for (;;) {
@ -832,13 +857,17 @@ class EditableContent {
if (lines.errorCode != 0) {
clear();
Log.e("Error ", lines.errorCode, " ", lines.errorMessage, " -- at line ", lines.errorLine, " position ", lines.errorPos);
notifyContentReplaced();
return false;
}
// EOF
_format = lines.textFormat;
notifyContentReplaced();
return true;
} catch (Exception e) {
Log.e("Exception while trying to read file ", fname, " ", e.toString);
clear();
notifyContentReplaced();
return false;
}
}
@ -855,6 +884,47 @@ class EditableContent {
return false;
}
}
/// save to output stream in specified format
bool save(OutputStream stream, string filename, TextFileFormat format) {
if (!filename)
filename = _filename;
_format = format;
import dlangui.core.linestream;
try {
OutputLineStream writer = new OutputLineStream(stream, filename, format);
scope(exit) { writer.close(); }
for (int i = 0; i < _lines.length; i++) {
writer.writeLine(_lines[i]);
}
// EOF
return true;
} catch (Exception e) {
Log.e("Exception while trying to write file ", filename, " ", e.toString);
return false;
}
return false;
}
/// save to output stream in current format
bool save(OutputStream stream, string filename) {
return save(stream, filename, _format);
}
/// save to file in specified format
bool save(string filename, TextFileFormat format) {
if (!filename)
filename = _filename;
try {
std.stream.File f = new std.stream.File(filename, FileMode.Out);
scope(exit) { f.close(); }
return save(f, filename, format);
} catch (Exception e) {
Log.e("Exception while trying to save file ", filename, " ", e.toString);
return false;
}
}
/// save to file in current format
bool save(string filename) {
return save(filename, _format);
}
}
/// base for all editor widgets
@ -1133,10 +1203,20 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
updateMaxLineWidth();
measureVisibleText();
if (source is this) {
if (operation.action == EditAction.ReplaceContent) {
// loaded from file
_caretPos = rangeAfter.end;
_selectionRange.start = _caretPos;
_selectionRange.end = _caretPos;
ensureCaretVisible();
correctCaretPos();
requestLayout();
} else {
_caretPos = rangeAfter.end;
_selectionRange.start = _caretPos;
_selectionRange.end = _caretPos;
ensureCaretVisible();
}
} else {
correctCaretPos();
// TODO: do something better (e.g. take into account ranges when correcting)