Skip to content

Commit 2fcd595

Browse files
Do not store file contents in SketchCode
Now that each file in the sketch has its own text area in the GUI, it is no longer needed to store the (possibly modified) contents of each file inside SketchCode. Keeping the contents in the text area is sufficient. Doing so allows removing the code that dealt with copying contents from the text area into the SketchCode instance at the right time, which was fragile and messy. However, when compiling a sketch, the current (modified) file contents still should be used. To allow this, the TextStorage interface is introduced. This is a simple interface implemented by EditorTab, that allows the SketchCode class to query the GUI for the current contents. By using an interface, there is no direct dependency on the GUI code. If no TextStorage instance is attached to a SketchCode, it will just assume that the contents are always unmodified and the contents from the file will be used during compilation. When not using the GUI (e.g. just compiling something from the commandline), there is no need to load the file contents from disk at all, the filenames just have to be passed to arduino-builder and the compiler. So, the SketchCode constructor no longer calls its `load()` function, leaving this to the GUI code to call when appropriate. This also modifies the `SketchCode.load()` function to return the loaded text, instead of storing it internally. To still support adding new files to a sketch (whose file does not exist on disk yet), the EditorTab constructor now allows an initial contents to be passed in, to be used instead of loading from disk. Only the empty string is passed for new files now, but this could also be used for the bare minimum contents of a new sketch later (which is now down by creating a .ino file in a temporary directory). Another side effect of this change is that all changes to the contents now happen through the text area, which keeps track of modifications already. This allows removing all manual calls to `Sketch.setModified()` (even more, the entire function is removed, making `Sketch.isModified()` always check the modification status of the contained files).
1 parent 9cb8ab2 commit 2fcd595

File tree

7 files changed

+135
-115
lines changed

7 files changed

+135
-115
lines changed

app/src/cc/arduino/packages/formatter/AStyle.java

-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ public void run() {
9494
textArea.getUndoManager().beginInternalAtomicEdit();
9595
editor.removeAllLineHighlights();
9696
editor.getCurrentTab().setText(formattedText);
97-
editor.getSketch().setModified(true);
9897
textArea.getUndoManager().endInternalAtomicEdit();
9998

10099
if (line != -1 && lineOffset != -1) {

app/src/cc/arduino/view/findreplace/FindReplace.java

-2
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,6 @@ private void replace() {
392392
if (find(false, false, searchAllFilesBox.isSelected(), -1)) {
393393
foundAtLeastOne = true;
394394
editor.getCurrentTab().setSelectedText(replaceField.getText());
395-
editor.getSketch().setModified(true); // TODO is this necessary?
396395
}
397396

398397
if (!foundAtLeastOne) {
@@ -430,7 +429,6 @@ private void replaceAll() {
430429
if (find(false, false, searchAllFilesBox.isSelected(), -1)) {
431430
foundAtLeastOne = true;
432431
editor.getCurrentTab().setSelectedText(replaceField.getText());
433-
editor.getSketch().setModified(true); // TODO is this necessary?
434432
} else {
435433
break;
436434
}

app/src/processing/app/Editor.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,6 @@ public void actionPerformed(ActionEvent e) {
13421342
pasteItem.addActionListener(new ActionListener() {
13431343
public void actionPerformed(ActionEvent e) {
13441344
getCurrentTab().handlePaste();
1345-
sketch.setModified(true);
13461345
}
13471346
});
13481347
menu.add(pasteItem);
@@ -1492,7 +1491,6 @@ public UndoAction() {
14921491
public void actionPerformed(ActionEvent e) {
14931492
try {
14941493
getCurrentTab().handleRedo();
1495-
sketch.setModified(true);
14961494
} catch (CannotUndoException ex) {
14971495
//System.out.println("Unable to undo: " + ex);
14981496
//ex.printStackTrace();
@@ -1526,7 +1524,6 @@ public RedoAction() {
15261524
public void actionPerformed(ActionEvent e) {
15271525
try {
15281526
getCurrentTab().handleRedo();
1529-
sketch.setModified(true);
15301527
} catch (CannotRedoException ex) {
15311528
//System.out.println("Unable to redo: " + ex);
15321529
//ex.printStackTrace();
@@ -1631,7 +1628,7 @@ public void sketchLoaded(Sketch sketch) {
16311628
tabs.ensureCapacity(sketch.getCodeCount());
16321629
for (SketchCode code : sketch.getCodes()) {
16331630
try {
1634-
addTab(code);
1631+
addTab(code, null);
16351632
} catch(IOException e) {
16361633
// TODO: Improve / move error handling
16371634
System.err.println(e);
@@ -1647,8 +1644,18 @@ protected void setCode(final SketchCodeDocument codeDoc) {
16471644
selectTab(findTabIndex(codeDoc.getCode()));
16481645
}
16491646

1650-
protected void addTab(SketchCode code) throws IOException {
1651-
EditorTab tab = new EditorTab(this, code);
1647+
/**
1648+
* Add a new tab.
1649+
*
1650+
* @param code
1651+
* The file to show in the tab.
1652+
* @param contents
1653+
* The contents to show in the tab, or null to load the
1654+
* contents from the given file.
1655+
* @throws IOException
1656+
*/
1657+
protected void addTab(SketchCode code, String contents) throws IOException {
1658+
EditorTab tab = new EditorTab(this, code, contents);
16521659
tabs.add(tab);
16531660
}
16541661

app/src/processing/app/EditorTab.java

+59-10
Original file line numberDiff line numberDiff line change
@@ -63,37 +63,63 @@
6363
/**
6464
* Single tab, editing a single file, in the main window.
6565
*/
66-
public class EditorTab extends JPanel {
66+
public class EditorTab extends JPanel implements SketchCode.TextStorage {
6767
protected Editor editor;
6868
protected SketchTextArea textarea;
6969
protected RTextScrollPane scrollPane;
7070
protected SketchCode code;
71+
protected boolean modified;
7172

72-
public EditorTab(Editor editor, SketchCode code) throws IOException {
73+
/**
74+
* Create a new EditorTab
75+
*
76+
* @param editor
77+
* The Editor this tab runs in
78+
* @param code
79+
* The file to display in this tab
80+
* @param contents
81+
* Initial contents to display in this tab. Can be used when
82+
* editing a file that doesn't exist yet. If null is passed,
83+
* code.load() is called and displayed instead.
84+
* @throws IOException
85+
*/
86+
public EditorTab(Editor editor, SketchCode code, String contents)
87+
throws IOException {
7388
super(new BorderLayout());
89+
90+
// Load initial contents contents from file if nothing was specified.
91+
if (contents == null) {
92+
contents = code.load();
93+
modified = false;
94+
} else {
95+
modified = true;
96+
}
97+
7498
this.editor = editor;
7599
this.code = code;
76-
this.textarea = createTextArea();
100+
RSyntaxDocument document = createDocument(contents);
101+
this.textarea = createTextArea(document);
77102
this.scrollPane = createScrollPane(this.textarea);
78103
applyPreferences();
79104
add(this.scrollPane, BorderLayout.CENTER);
80-
105+
81106
UndoManager undo = new LastUndoableEditAwareUndoManager(this.textarea, this.editor);
82107
((RSyntaxDocument)textarea.getDocument()).addUndoableEditListener(undo);
108+
code.setStorage(this);
83109
}
84110

85-
private RSyntaxDocument createDocument() {
111+
private RSyntaxDocument createDocument(String contents) {
86112
RSyntaxDocument document = new RSyntaxDocument(new ArduinoTokenMakerFactory(editor.base.getPdeKeywords()), RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS);
87113
document.putProperty(PlainDocument.tabSizeAttribute, PreferencesData.getInteger("editor.tabs.size"));
88-
114+
89115
// insert the program text into the document object
90116
try {
91-
document.insertString(0, code.getProgram(), null);
117+
document.insertString(0, contents, null);
92118
} catch (BadLocationException bl) {
93119
bl.printStackTrace();
94120
}
95121
document.addDocumentListener(new DocumentTextChangeListener(
96-
() -> code.setModified(true)));
122+
() -> setModified(true)));
97123
return document;
98124
}
99125

@@ -112,8 +138,8 @@ private RTextScrollPane createScrollPane(SketchTextArea textArea) throws IOExcep
112138
return scrollPane;
113139
}
114140

115-
private SketchTextArea createTextArea() throws IOException {
116-
RSyntaxDocument document = createDocument();
141+
private SketchTextArea createTextArea(RSyntaxDocument document)
142+
throws IOException {
117143
final SketchTextArea textArea = new SketchTextArea(document, editor.base.getPdeKeywords());
118144
textArea.setName("editor");
119145
textArea.setFocusTraversalKeysEnabled(false);
@@ -316,6 +342,29 @@ public void setText(String what) {
316342
textarea.setText(what);
317343
}
318344

345+
/**
346+
* Is the text modified since the last save / load?
347+
*/
348+
public boolean isModified() {
349+
return modified;
350+
}
351+
352+
/**
353+
* Clear modified status. Should only be called by SketchCode through
354+
* the TextStorage interface.
355+
*/
356+
public void clearModified() {
357+
setModified(false);
358+
}
359+
360+
private void setModified(boolean value) {
361+
if (value != modified) {
362+
modified = value;
363+
// TODO: Improve decoupling
364+
editor.getSketch().calcModified();
365+
}
366+
}
367+
319368
public String getSelectedText() {
320369
return textarea.getSelectedText();
321370
}

app/src/processing/app/Sketch.java

+10-60
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@
5858
public class Sketch {
5959
private final Editor editor;
6060

61-
/** true if any of the files have been modified. */
62-
private boolean modified;
63-
6461
private SketchCodeDocument current;
6562
private int currentIndex;
6663

@@ -320,7 +317,6 @@ protected void nameCode(String newName) {
320317

321318
// first get the contents of the editor text area
322319
if (current.getCode().isModified()) {
323-
current.getCode().setProgram(editor.getCurrentTab().getText());
324320
try {
325321
// save this new SketchCode
326322
current.getCode().save();
@@ -403,7 +399,7 @@ protected void nameCode(String newName) {
403399
ensureExistence();
404400
SketchCode code = (new SketchCodeDocument(this, newFile)).getCode();
405401
try {
406-
editor.addTab(code);
402+
editor.addTab(code, "");
407403
} catch (IOException e) {
408404
Base.showWarning(tr("Error"),
409405
I18n.format(
@@ -510,39 +506,28 @@ public void handleNextCode() {
510506
setCurrentCode((currentIndex + 1) % data.getCodeCount());
511507
}
512508

513-
514509
/**
515-
* Sets the modified value for the code in the frontmost tab.
510+
* Called whenever the modification status of one of the tabs changes. TODO:
511+
* Move this code into Editor and improve decoupling from EditorTab
516512
*/
517-
public void setModified(boolean state) {
518-
//System.out.println("setting modified to " + state);
519-
//new Exception().printStackTrace();
520-
current.getCode().setModified(state);
521-
calcModified();
522-
}
523-
524-
525-
private void calcModified() {
526-
modified = false;
527-
for (SketchCode code : data.getCodes()) {
528-
if (code.isModified()) {
529-
modified = true;
530-
break;
531-
}
532-
}
513+
public void calcModified() {
533514
editor.header.repaint();
534515

535516
if (OSUtils.isMacOS()) {
536517
// http://developer.apple.com/qa/qa2001/qa1146.html
537-
Object modifiedParam = modified ? Boolean.TRUE : Boolean.FALSE;
518+
Object modifiedParam = isModified() ? Boolean.TRUE : Boolean.FALSE;
538519
editor.getRootPane().putClientProperty("windowModified", modifiedParam);
539520
editor.getRootPane().putClientProperty("Window.documentModified", modifiedParam);
540521
}
541522
}
542523

543524

544525
public boolean isModified() {
545-
return modified;
526+
for (SketchCode code : data.getCodes()) {
527+
if (code.isModified())
528+
return true;
529+
}
530+
return false;
546531
}
547532

548533

@@ -553,14 +538,6 @@ public boolean save() throws IOException {
553538
// make sure the user didn't hide the sketch folder
554539
ensureExistence();
555540

556-
// first get the contents of the editor text area
557-
if (current.getCode().isModified()) {
558-
current.getCode().setProgram(editor.getCurrentTab().getText());
559-
}
560-
561-
// don't do anything if not actually modified
562-
//if (!modified) return false;
563-
564541
if (isReadOnly(BaseNoGui.librariesIndexer.getInstalledLibraries(), BaseNoGui.getExamplesPath())) {
565542
Base.showMessage(tr("Sketch is read-only"),
566543
tr("Some files are marked \"read-only\", so you'll\n" +
@@ -606,7 +583,6 @@ public boolean save() throws IOException {
606583
}
607584

608585
data.save();
609-
calcModified();
610586
return true;
611587
}
612588

@@ -708,12 +684,6 @@ protected boolean saveAs() throws IOException {
708684
// now make a fresh copy of the folder
709685
newFolder.mkdirs();
710686

711-
// grab the contents of the current tab before saving
712-
// first get the contents of the editor text area
713-
if (current.getCode().isModified()) {
714-
current.getCode().setProgram(editor.getCurrentTab().getText());
715-
}
716-
717687
// save the other tabs to their new location
718688
for (SketchCode code : data.getCodes()) {
719689
if (data.indexOfCode(code) == 0) continue;
@@ -913,18 +883,6 @@ public boolean addFile(File sourceFile) {
913883
data.sortCode();
914884
}
915885
setCurrentCode(filename);
916-
editor.header.repaint();
917-
if (editor.untitled) { // TODO probably not necessary? problematic?
918-
// Mark the new code as modified so that the sketch is saved
919-
current.getCode().setModified(true);
920-
}
921-
922-
} else {
923-
if (editor.untitled) { // TODO probably not necessary? problematic?
924-
// If a file has been added, mark the main code as modified so
925-
// that the sketch is properly saved.
926-
data.getCode(0).setModified(true);
927-
}
928886
}
929887
return true;
930888
}
@@ -966,7 +924,6 @@ private void importLibrary(File jarPath) throws IOException {
966924
buffer.append(editor.getCurrentTab().getText());
967925
editor.getCurrentTab().setText(buffer.toString());
968926
editor.getCurrentTab().setSelection(0, 0); // scroll to start
969-
setModified(true);
970927
}
971928

972929

@@ -988,10 +945,6 @@ private void setCurrentCode(int which, boolean forceUpdate) {
988945
return;
989946
}
990947

991-
// get the text currently being edited
992-
if (current != null)
993-
current.getCode().setProgram(editor.getCurrentTab().getText());
994-
995948
current = (SketchCodeDocument) data.getCode(which).getMetadata();
996949
currentIndex = which;
997950
editor.setCode(current);
@@ -1050,8 +1003,6 @@ public void prepare() throws IOException {
10501003
// make sure the user didn't hide the sketch folder
10511004
ensureExistence();
10521005

1053-
current.getCode().setProgram(editor.getCurrentTab().getText());
1054-
10551006
// TODO record history here
10561007
//current.history.record(program, SketchHistory.RUN);
10571008

@@ -1215,7 +1166,6 @@ private void ensureExistence() {
12151166
"but anything besides the code will be lost."), null);
12161167
try {
12171168
data.getFolder().mkdirs();
1218-
modified = true;
12191169

12201170
for (SketchCode code : data.getCodes()) {
12211171
code.save(); // this will force a save

app/src/processing/app/tools/FixEncoding.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ public void run() {
6767
try {
6868
for (int i = 0; i < sketch.getCodeCount(); i++) {
6969
SketchCode code = sketch.getCode(i);
70-
code.setProgram(loadWithLocalEncoding(code.getFile()));
71-
code.setModified(true); // yes, because we want them to save this
72-
editor.findTab(code).setText(code.getProgram());
70+
editor.findTab(code).setText(loadWithLocalEncoding(code.getFile()));
7371
}
7472
} catch (IOException e) {
7573
String msg =

0 commit comments

Comments
 (0)