scide + lang: Remove Document redirect
This merges the Document and ScIDEDocument classes. This should improve readability and maintainability, and simplifies the implementation. As part of this: - some duplicate methods are deprecated - the previous Document has been copied into the scel folder to avoid breaking EmacsDocument Signed-off-by: Scott Wilson <i@scottwilson.ca>
This commit is contained in:
parent
fe43d2f24a
commit
6af534a0fb
1 changed files with 587 additions and 0 deletions
587
sc/Document.sc
Normal file
587
sc/Document.sc
Normal file
|
@ -0,0 +1,587 @@
|
|||
// Since SC v3.2 dev, Document is an ABSTRACT class. Can't be instantiated directly.
|
||||
// Subclasses provide the editor-specific implementation, e.g. CocoaDocument for the standard Mac interface.
|
||||
// Subclasses also (in their SC code files) add a "implementationClass" method to Document to tell it to use them.
|
||||
|
||||
Document {
|
||||
|
||||
classvar <dir="", <allDocuments, >current;
|
||||
classvar <globalKeyDownAction, <globalKeyUpAction, <>initAction;
|
||||
|
||||
classvar <>autoRun = true;
|
||||
|
||||
classvar <>implementationClass;
|
||||
|
||||
//don't change the order of these vars:
|
||||
var <dataptr, <>keyDownAction, <>keyUpAction, <>mouseUpAction;
|
||||
var <>toFrontAction, <>endFrontAction, <>onClose, <>mouseDownAction;
|
||||
|
||||
var <stringColor;
|
||||
|
||||
var <envir, savedEnvir;
|
||||
var <editable;
|
||||
|
||||
*initClass{
|
||||
allDocuments = [];
|
||||
}
|
||||
|
||||
*startup {
|
||||
var num, doc;
|
||||
num = this.numberOfOpen;
|
||||
num.do { | i |
|
||||
doc = this.newFromIndex(i);
|
||||
};
|
||||
}
|
||||
|
||||
*open { | path, selectionStart=0, selectionLength=0, envir |
|
||||
var doc = Document.implementationClass.prBasicNew.initFromPath(path, selectionStart, selectionLength);
|
||||
if (envir.notNil and: { doc.notNil }) { doc.envir_(envir) };
|
||||
^doc
|
||||
}
|
||||
|
||||
*new { | title="Untitled", string="", makeListener=false, envir |
|
||||
var doc = Document.implementationClass.new(title, string, makeListener);
|
||||
if (envir.notNil and: { doc.notNil }) { doc.envir_(envir) };
|
||||
^doc
|
||||
}
|
||||
|
||||
*dir_ { | path |
|
||||
path = path.standardizePath;
|
||||
if(path == "") { dir = path } {
|
||||
if(pathMatch(path).isEmpty) { ("there is no such path:" + path).postln } {
|
||||
dir = path ++ "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*standardizePath { | p |
|
||||
var pathName;
|
||||
pathName = PathName(p.standardizePath);
|
||||
^if(pathName.isRelativePath) {
|
||||
dir ++ pathName.fullPath
|
||||
} {
|
||||
pathName.fullPath
|
||||
}
|
||||
}
|
||||
*abrevPath { | path |
|
||||
if(path.size < dir.size) { ^path };
|
||||
if(path.copyRange(0,dir.size - 1) == dir) {
|
||||
^path.copyRange(dir.size, path.size - 1)
|
||||
};
|
||||
^path
|
||||
}
|
||||
|
||||
*openDocuments {
|
||||
^allDocuments
|
||||
}
|
||||
|
||||
*hasEditedDocuments {
|
||||
allDocuments.do { | doc, i |
|
||||
if(doc.isEdited) {
|
||||
^true;
|
||||
}
|
||||
}
|
||||
^false
|
||||
}
|
||||
|
||||
*closeAll { | leavePostWindowOpen = true |
|
||||
allDocuments.do { | doc, i |
|
||||
if(leavePostWindowOpen.not) {
|
||||
doc.close;
|
||||
} {
|
||||
if(doc.isListener.not) {
|
||||
doc.close;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*closeAllUnedited { | leavePostWindowOpen = true |
|
||||
var listenerWindow;
|
||||
allDocuments.do({ | doc, i |
|
||||
if(doc.isListener,{
|
||||
listenerWindow = doc;
|
||||
},{
|
||||
if(doc.isEdited.not, {
|
||||
doc.close;
|
||||
});
|
||||
})
|
||||
});
|
||||
if(leavePostWindowOpen.not, {
|
||||
listenerWindow.close;
|
||||
})
|
||||
}
|
||||
|
||||
*globalKeyDownAction_ {|action|
|
||||
globalKeyDownAction = action;
|
||||
this.implementationClass.prGlobalKeyDownAction_(action);
|
||||
}
|
||||
|
||||
*prGlobalKeyDownAction_ { }
|
||||
|
||||
*globalKeyUpAction_ {|action|
|
||||
globalKeyUpAction = action;
|
||||
this.implementationClass.prGlobalKeyUpAction_(action);
|
||||
}
|
||||
|
||||
*prGlobalKeyUpAction_ { }
|
||||
|
||||
*current {
|
||||
if ( thisProcess.platform.hasFeature( \emacs ), {
|
||||
^this.implementationClass.current;
|
||||
});
|
||||
^current;
|
||||
}
|
||||
|
||||
*listener {
|
||||
var index = this.prGetIndexOfListener;
|
||||
if (index.notNil) {
|
||||
^allDocuments[index];
|
||||
} {
|
||||
^nil
|
||||
}
|
||||
}
|
||||
|
||||
isListener {
|
||||
var index = this.class.prGetIndexOfListener;
|
||||
if (index.notNil) {
|
||||
^allDocuments.indexOf(this) == index
|
||||
} {
|
||||
^False
|
||||
}
|
||||
}
|
||||
|
||||
// document setup
|
||||
|
||||
path {
|
||||
^this.prGetFileName
|
||||
}
|
||||
path_ { |apath|
|
||||
this.prSetFileName(apath);
|
||||
}
|
||||
dir {
|
||||
var path = this.path;
|
||||
^path !? { path.dirname }
|
||||
}
|
||||
name {
|
||||
^this.title
|
||||
}
|
||||
|
||||
name_ { |aname|
|
||||
this.title_(aname)
|
||||
}
|
||||
|
||||
title {
|
||||
^this.prGetTitle
|
||||
}
|
||||
|
||||
title_ { | argName |
|
||||
this.prSetTitle(argName);
|
||||
}
|
||||
|
||||
bounds {
|
||||
^this.prGetBounds(Rect.new);
|
||||
}
|
||||
bounds_ { | argBounds |
|
||||
^this.prSetBounds(argBounds);
|
||||
}
|
||||
|
||||
// interaction:
|
||||
close {
|
||||
this.prclose
|
||||
}
|
||||
|
||||
front {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
unfocusedFront {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
alwaysOnTop_ { |boolean=true|
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
alwaysOnTop {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
selectLine { | line |
|
||||
this.prSelectLine(line);
|
||||
}
|
||||
|
||||
selectRange { | start=0, length=0 |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
editable_ { | abool=true |
|
||||
editable = abool;
|
||||
this.prIsEditable_(abool);
|
||||
}
|
||||
removeUndo {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
promptToSave_ { | bool |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
promptToSave {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
underlineSelection {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
// state info
|
||||
isEdited {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
isFront {
|
||||
^Document.current === this
|
||||
}
|
||||
|
||||
selectionStart {
|
||||
^this.selectedRangeLocation
|
||||
}
|
||||
|
||||
selectionSize {
|
||||
^this.selectedRangeSize
|
||||
}
|
||||
|
||||
string { | rangestart, rangesize = 1 |
|
||||
if(rangestart.isNil,{
|
||||
^this.text;
|
||||
});
|
||||
^this.rangeText(rangestart, rangesize);
|
||||
}
|
||||
|
||||
string_ { | string, rangestart = -1, rangesize = 1 |
|
||||
this.insertTextRange(string, rangestart, rangesize);
|
||||
}
|
||||
selectedString {
|
||||
^this.selectedText
|
||||
}
|
||||
|
||||
|
||||
font_ { | font, rangestart = -1, rangesize=0 |
|
||||
this.setFont(font, rangestart, rangesize)
|
||||
}
|
||||
|
||||
selectedString_ { | txt |
|
||||
this.prinsertText(txt)
|
||||
}
|
||||
|
||||
currentLine {
|
||||
^this.getSelectedLines(this.selectionStart, 0);
|
||||
}
|
||||
|
||||
getSelectedLines { | rangestart = -1, rangesize = 0 |
|
||||
var start, end, str, max;
|
||||
str = this.string;
|
||||
max = str.size;
|
||||
start = rangestart;
|
||||
end = start + rangesize;
|
||||
while {
|
||||
str[start] !== Char.nl and: { start >= 0 }
|
||||
} { start = start - 1 };
|
||||
while {
|
||||
str[end] !== Char.nl and: { end < max }
|
||||
} { end = end + 1 };
|
||||
^str.copyRange(start + 1, end);
|
||||
}
|
||||
|
||||
//actions:
|
||||
|
||||
didBecomeKey {
|
||||
this.class.current = this;
|
||||
this.saveCurrentEnvironment;
|
||||
toFrontAction.value(this);
|
||||
}
|
||||
|
||||
didResignKey {
|
||||
endFrontAction.value(this);
|
||||
this.restoreCurrentEnvironment;
|
||||
}
|
||||
|
||||
mouseUp{ | x, y, modifiers, buttonNumber, clickCount, clickPos |
|
||||
mouseUpAction.value(this, x, y, modifiers, buttonNumber, clickCount)
|
||||
}
|
||||
|
||||
keyDown { | character, modifiers, unicode, keycode |
|
||||
this.class.globalKeyDownAction.value(this,character, modifiers, unicode, keycode);
|
||||
keyDownAction.value(this,character, modifiers, unicode, keycode);
|
||||
}
|
||||
|
||||
keyUp { | character, modifiers, unicode, keycode |
|
||||
this.class.globalKeyUpAction.value(this,character, modifiers, unicode, keycode);
|
||||
keyUpAction.value(this,character, modifiers, unicode, keycode);
|
||||
}
|
||||
|
||||
== { | doc |
|
||||
^if(this.path.isNil or: { doc.path.isNil }) { doc === this } {
|
||||
this.path == doc.path
|
||||
}
|
||||
}
|
||||
|
||||
hash {
|
||||
^(this.path ? this).hash
|
||||
}
|
||||
|
||||
*defaultUsesAutoInOutdent_ {|bool|
|
||||
Document.implementationClass.prDefaultUsesAutoInOutdent_(bool)
|
||||
}
|
||||
|
||||
usesAutoInOutdent_ {|bool|
|
||||
this.prUsesAutoInOutdent_(bool)
|
||||
}
|
||||
|
||||
*prDefaultUsesAutoInOutdent_{|bool|
|
||||
this.subclassResponsibility(thisMethod);
|
||||
}
|
||||
|
||||
*prPostColor_{ |color|
|
||||
this.subclassResponsibility(thisMethod);
|
||||
}
|
||||
|
||||
prUsesAutoInOutdent_{|bool|
|
||||
^this.subclassResponsibility(thisMethod);
|
||||
}
|
||||
|
||||
|
||||
// private implementation
|
||||
|
||||
prIsEditable_{ | editable=true |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
prSetTitle { | argName |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
prGetTitle {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
prGetFileName {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
prSetFileName { | apath |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
prGetBounds { | argBounds |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
prSetBounds { | argBounds |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
text {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
selectedText {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
selectUnderlinedText { | clickPos |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
linkAtClickPos { | clickPos |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
rangeText { | rangestart=0, rangesize=1 |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
prclose {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
closed {
|
||||
onClose.value(this); // call user function
|
||||
this.restoreCurrentEnvironment;
|
||||
allDocuments.remove(this);
|
||||
dataptr = nil;
|
||||
}
|
||||
|
||||
prinsertText { | dataPtr, txt |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
insertTextRange { | string, rangestart, rangesize |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
prAdd {
|
||||
allDocuments = allDocuments.add(this);
|
||||
this.editable = true;
|
||||
if (autoRun) {
|
||||
if (this.rangeText(0,7) == "/*RUN*/")
|
||||
{
|
||||
this.text.interpret;
|
||||
}
|
||||
};
|
||||
current = this;
|
||||
initAction.value(this);
|
||||
|
||||
}
|
||||
|
||||
//this is called after recompiling the lib
|
||||
*prnumberOfOpen {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
*numberOfOpen {
|
||||
thisProcess.platform.when(\_NumberOfOpenTextWindows) {
|
||||
^this.prnumberOfOpen
|
||||
} { ^allDocuments.size };
|
||||
^0
|
||||
}
|
||||
|
||||
*newFromIndex { | idx |
|
||||
^super.new.initByIndex(idx)
|
||||
}
|
||||
initByIndex { | idx |
|
||||
//allDocuments = allDocuments.add(this);
|
||||
var doc;
|
||||
doc = this.prinitByIndex(idx);
|
||||
if(doc.isNil,{^nil});
|
||||
this.prAdd;
|
||||
}
|
||||
prinitByIndex { | idx |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
//this is called from the menu: open, new
|
||||
*prGetLast {
|
||||
^Document.implementationClass.prBasicNew.initLast
|
||||
}
|
||||
|
||||
initLast {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
prGetLastIndex {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
// private open
|
||||
initFromPath { | path, selectionStart, selectionLength |
|
||||
var stpath;
|
||||
// path = apath;
|
||||
stpath = this.class.standardizePath(path);
|
||||
this.propen(stpath, selectionStart, selectionLength);
|
||||
if(dataptr.isNil,{
|
||||
this.class.allDocuments.do{ |d|
|
||||
if(d.path == stpath.absolutePath){
|
||||
^d
|
||||
}
|
||||
};
|
||||
^nil
|
||||
});
|
||||
this.background_(Color.white);
|
||||
^this.prAdd;
|
||||
}
|
||||
propen { | path, selectionStart=0, selectionLength=0 |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
// private newTextWindow
|
||||
initByString{ | argTitle, str, makeListener |
|
||||
|
||||
this.prinitByString(argTitle, str, makeListener);
|
||||
this.background_(Color.white);
|
||||
if(dataptr.isNil,{^nil});
|
||||
this.prAdd;
|
||||
this.title = argTitle;
|
||||
|
||||
}
|
||||
prinitByString { | title, str, makeListener |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
// other private
|
||||
// if -1 whole doc
|
||||
|
||||
prSetBackgroundColor { | color |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
prGetBackgroundColor { | color |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
prSetSelectedBackgroundColor { | color |
|
||||
^this.subclassResponsibility(thisMethod);
|
||||
}
|
||||
prGetSelectedBackgroundColor { | color |
|
||||
^this.subclassResponsibility(thisMethod);
|
||||
}
|
||||
selectedRangeLocation {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
selectedRangeSize {
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
prSelectLine { | line |
|
||||
^this.subclassResponsibility(thisMethod)
|
||||
}
|
||||
|
||||
*prGetIndexOfListener {
|
||||
if (this.implementationClass.isNil) {
|
||||
^nil
|
||||
};
|
||||
|
||||
if (this.implementationClass.respondsTo(\prGetIndexOfListener)) {
|
||||
^this.implementationClass.prGetIndexOfListener
|
||||
} {
|
||||
^nil
|
||||
}
|
||||
}
|
||||
|
||||
//---not yet implemented
|
||||
// ~/Documents
|
||||
// /Volumes
|
||||
// Music/Patches
|
||||
|
||||
//*reviewUnsavedDocumentsWithAlertTitle
|
||||
//*saveAllDocuments
|
||||
//*recentDocumentPaths
|
||||
//save
|
||||
//saveAs
|
||||
//print
|
||||
//
|
||||
//hasPath was loaded
|
||||
|
||||
|
||||
// Environment handling Document with its own envir must set and restore currentEnvironment on entry and exit.
|
||||
// Requires alteration of *open, *new, closed, didBecomeKey, and didResignKey
|
||||
|
||||
envir_ { | ev |
|
||||
envir = ev;
|
||||
if (this.class.current == this) {
|
||||
if(envir.isNil) {
|
||||
this.restoreCurrentEnvironment
|
||||
} {
|
||||
if (savedEnvir.isNil) {
|
||||
this.saveCurrentEnvironment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restoreCurrentEnvironment {
|
||||
if (savedEnvir.notNil) {
|
||||
currentEnvironment = savedEnvir;
|
||||
savedEnvir = nil;
|
||||
}
|
||||
}
|
||||
|
||||
saveCurrentEnvironment {
|
||||
if (envir.notNil) {
|
||||
savedEnvir = currentEnvironment;
|
||||
currentEnvironment = envir;
|
||||
}
|
||||
}
|
||||
|
||||
*prBasicNew {
|
||||
^super.new
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue