mirror of https://github.com/buggins/dlangui.git
support writing of editor contents to file
This commit is contained in:
parent
00e4d207d4
commit
ec0f7ea8f4
|
@ -208,6 +208,7 @@ class OutputLineStream {
|
||||||
}
|
}
|
||||||
/// close stream
|
/// close stream
|
||||||
void close() {
|
void close() {
|
||||||
|
flush();
|
||||||
_stream.close();
|
_stream.close();
|
||||||
_buf = null;
|
_buf = null;
|
||||||
}
|
}
|
||||||
|
@ -243,6 +244,10 @@ class LineStream {
|
||||||
private uint _textLen; // position of last filled char in text buffer + 1
|
private uint _textLen; // position of last filled char in text buffer + 1
|
||||||
private dchar[] _textBuf; // text buffer
|
private dchar[] _textBuf; // text buffer
|
||||||
private bool _eof; // end of file, no more lines
|
private bool _eof; // end of file, no more lines
|
||||||
|
protected bool _bomDetected;
|
||||||
|
protected int _crCount;
|
||||||
|
protected int _lfCount;
|
||||||
|
protected int _crlfCount;
|
||||||
|
|
||||||
/// Returns file name
|
/// Returns file name
|
||||||
@property string filename() { return _filename; }
|
@property string filename() { return _filename; }
|
||||||
|
@ -250,6 +255,25 @@ class LineStream {
|
||||||
@property uint line() { return _line; }
|
@property uint line() { return _line; }
|
||||||
/// Returns file encoding EncodingType
|
/// Returns file encoding EncodingType
|
||||||
@property EncodingType encoding() { return _encoding; }
|
@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
|
/// Returns error code
|
||||||
@property int errorCode() { return _errorCode; }
|
@property int errorCode() { return _errorCode; }
|
||||||
/// Returns error message
|
/// Returns error message
|
||||||
|
@ -397,15 +421,21 @@ class LineStream {
|
||||||
charsLeft = _textLen - _textPos;
|
charsLeft = _textLen - _textPos;
|
||||||
}
|
}
|
||||||
dchar ch2 = (p < charsLeft - 1) ? _textBuf[_textPos + p + 1] : 0;
|
dchar ch2 = (p < charsLeft - 1) ? _textBuf[_textPos + p + 1] : 0;
|
||||||
if (ch2 == 0x0A)
|
if (ch2 == 0x0A) {
|
||||||
eol = p + 2;
|
eol = p + 2;
|
||||||
else
|
_lfCount++;
|
||||||
|
_crCount++;
|
||||||
|
_crlfCount++;
|
||||||
|
} else {
|
||||||
eol = p + 1;
|
eol = p + 1;
|
||||||
|
_lfCount++;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
} else if (ch == 0x0A || ch == 0x2028 || ch == 0x2029) {
|
} else if (ch == 0x0A || ch == 0x2028 || ch == 0x2029) {
|
||||||
// single char eoln
|
// single char eoln
|
||||||
lastchar = p;
|
lastchar = p;
|
||||||
eol = p + 1;
|
eol = p + 1;
|
||||||
|
_crCount++;
|
||||||
break;
|
break;
|
||||||
} else if (ch == 0 || ch == 0x001A) {
|
} else if (ch == 0 || ch == 0x001A) {
|
||||||
// eof
|
// eof
|
||||||
|
@ -450,25 +480,34 @@ class LineStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Factory for InputStream parser
|
/// 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];
|
ubyte[] buf = new ubyte[BYTE_BUFFER_SIZE];
|
||||||
buf[0] = buf[1] = buf[2] = buf[3] = 0;
|
buf[0] = buf[1] = buf[2] = buf[3] = 0;
|
||||||
if (!stream.isOpen)
|
if (!stream.isOpen)
|
||||||
return null;
|
return null;
|
||||||
uint len = cast(uint)stream.read(buf);
|
uint len = cast(uint)stream.read(buf);
|
||||||
|
LineStream res = null;
|
||||||
if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) {
|
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) {
|
} 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) {
|
} 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) {
|
} 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) {
|
} else if (buf[0] == 0xFF && buf[1] == 0xFE) {
|
||||||
return new Utf16leLineStream(stream, filename, buf, len);
|
res = new Utf16leLineStream(stream, filename, buf, len);
|
||||||
} else {
|
|
||||||
return new AsciiLineStream(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;
|
protected bool invalidCharFlag;
|
||||||
|
|
|
@ -27,6 +27,7 @@ import dlangui.widgets.controls;
|
||||||
import dlangui.widgets.scroll;
|
import dlangui.widgets.scroll;
|
||||||
import dlangui.core.signals;
|
import dlangui.core.signals;
|
||||||
import dlangui.core.collections;
|
import dlangui.core.collections;
|
||||||
|
import dlangui.core.linestream;
|
||||||
import dlangui.platforms.common.platform;
|
import dlangui.platforms.common.platform;
|
||||||
import dlangui.widgets.menu;
|
import dlangui.widgets.menu;
|
||||||
import dlangui.widgets.popup;
|
import dlangui.widgets.popup;
|
||||||
|
@ -256,7 +257,10 @@ enum EditAction {
|
||||||
/// delete content in range
|
/// delete content in range
|
||||||
//Delete,
|
//Delete,
|
||||||
/// replace range content with new content
|
/// replace range content with new content
|
||||||
Replace
|
Replace,
|
||||||
|
|
||||||
|
/// replace whole content
|
||||||
|
ReplaceContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// edit operation details for EditableContent
|
/// edit operation details for EditableContent
|
||||||
|
@ -435,6 +439,14 @@ class EditableContent {
|
||||||
}
|
}
|
||||||
return cast(dstring)buf;
|
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
|
/// replace whole text with another content
|
||||||
@property EditableContent text(dstring newContent) {
|
@property EditableContent text(dstring newContent) {
|
||||||
clearUndo();
|
clearUndo();
|
||||||
|
@ -445,6 +457,7 @@ class EditableContent {
|
||||||
_lines.length = 1;
|
_lines.length = 1;
|
||||||
_lines[0] = replaceEolsWithSpaces(newContent);
|
_lines[0] = replaceEolsWithSpaces(newContent);
|
||||||
}
|
}
|
||||||
|
notifyContentReplaced();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -817,10 +830,22 @@ class EditableContent {
|
||||||
void clearUndo() {
|
void clearUndo() {
|
||||||
_undoBuffer.clear();
|
_undoBuffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected string _filename;
|
||||||
|
protected TextFileFormat _format;
|
||||||
|
|
||||||
|
/// file used to load editor content
|
||||||
|
@property string filename() {
|
||||||
|
return _filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// load content form input stream
|
/// load content form input stream
|
||||||
bool load(InputStream f, string fname = null) {
|
bool load(InputStream f, string fname = null) {
|
||||||
import dlangui.core.linestream;
|
import dlangui.core.linestream;
|
||||||
clear();
|
clear();
|
||||||
|
_filename = fname;
|
||||||
|
_format = TextFileFormat.init;
|
||||||
try {
|
try {
|
||||||
LineStream lines = LineStream.create(f, fname);
|
LineStream lines = LineStream.create(f, fname);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -832,13 +857,17 @@ class EditableContent {
|
||||||
if (lines.errorCode != 0) {
|
if (lines.errorCode != 0) {
|
||||||
clear();
|
clear();
|
||||||
Log.e("Error ", lines.errorCode, " ", lines.errorMessage, " -- at line ", lines.errorLine, " position ", lines.errorPos);
|
Log.e("Error ", lines.errorCode, " ", lines.errorMessage, " -- at line ", lines.errorLine, " position ", lines.errorPos);
|
||||||
|
notifyContentReplaced();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// EOF
|
// EOF
|
||||||
|
_format = lines.textFormat;
|
||||||
|
notifyContentReplaced();
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("Exception while trying to read file ", fname, " ", e.toString);
|
Log.e("Exception while trying to read file ", fname, " ", e.toString);
|
||||||
clear();
|
clear();
|
||||||
|
notifyContentReplaced();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -855,6 +884,47 @@ class EditableContent {
|
||||||
return false;
|
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
|
/// base for all editor widgets
|
||||||
|
@ -1133,10 +1203,20 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
updateMaxLineWidth();
|
updateMaxLineWidth();
|
||||||
measureVisibleText();
|
measureVisibleText();
|
||||||
if (source is this) {
|
if (source is this) {
|
||||||
|
if (operation.action == EditAction.ReplaceContent) {
|
||||||
|
// loaded from file
|
||||||
_caretPos = rangeAfter.end;
|
_caretPos = rangeAfter.end;
|
||||||
_selectionRange.start = _caretPos;
|
_selectionRange.start = _caretPos;
|
||||||
_selectionRange.end = _caretPos;
|
_selectionRange.end = _caretPos;
|
||||||
ensureCaretVisible();
|
ensureCaretVisible();
|
||||||
|
correctCaretPos();
|
||||||
|
requestLayout();
|
||||||
|
} else {
|
||||||
|
_caretPos = rangeAfter.end;
|
||||||
|
_selectionRange.start = _caretPos;
|
||||||
|
_selectionRange.end = _caretPos;
|
||||||
|
ensureCaretVisible();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
correctCaretPos();
|
correctCaretPos();
|
||||||
// TODO: do something better (e.g. take into account ranges when correcting)
|
// TODO: do something better (e.g. take into account ranges when correcting)
|
||||||
|
|
Loading…
Reference in New Issue