Skip to content

Commit ca57335

Browse files
matthijskooijmanfacchinm
authored andcommitted
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 422c111 commit ca57335

File tree

7 files changed

+139
-117
lines changed

7 files changed

+139
-117
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
@@ -1317,7 +1317,6 @@ public void actionPerformed(ActionEvent e) {
13171317
pasteItem.addActionListener(new ActionListener() {
13181318
public void actionPerformed(ActionEvent e) {
13191319
getCurrentTab().handlePaste();
1320-
sketch.setModified(true);
13211320
}
13221321
});
13231322
menu.add(pasteItem);
@@ -1482,7 +1481,6 @@ public UndoAction() {
14821481
public void actionPerformed(ActionEvent e) {
14831482
try {
14841483
getCurrentTab().handleUndo();
1485-
sketch.setModified(true);
14861484
} catch (CannotUndoException ex) {
14871485
//System.out.println("Unable to undo: " + ex);
14881486
//ex.printStackTrace();
@@ -1516,7 +1514,6 @@ public RedoAction() {
15161514
public void actionPerformed(ActionEvent e) {
15171515
try {
15181516
getCurrentTab().handleRedo();
1519-
sketch.setModified(true);
15201517
} catch (CannotRedoException ex) {
15211518
//System.out.println("Unable to redo: " + ex);
15221519
//ex.printStackTrace();
@@ -1622,7 +1619,7 @@ public void sketchLoaded(Sketch sketch) {
16221619
tabs.ensureCapacity(sketch.getCodeCount());
16231620
for (SketchCode code : sketch.getCodes()) {
16241621
try {
1625-
addTab(code);
1622+
addTab(code, null);
16261623
} catch(IOException e) {
16271624
// TODO: Improve / move error handling
16281625
System.err.println(e);
@@ -1638,8 +1635,18 @@ protected void setCode(final SketchCodeDocument codeDoc) {
16381635
selectTab(findTabIndex(codeDoc.getCode()));
16391636
}
16401637

1641-
protected void addTab(SketchCode code) throws IOException {
1642-
EditorTab tab = new EditorTab(this, code);
1638+
/**
1639+
* Add a new tab.
1640+
*
1641+
* @param code
1642+
* The file to show in the tab.
1643+
* @param contents
1644+
* The contents to show in the tab, or null to load the
1645+
* contents from the given file.
1646+
* @throws IOException
1647+
*/
1648+
protected void addTab(SketchCode code, String contents) throws IOException {
1649+
EditorTab tab = new EditorTab(this, code, contents);
16431650
tabs.add(tab);
16441651
}
16451652

app/src/processing/app/EditorTab.java

+63-12
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
5252
import org.fife.ui.rtextarea.Gutter;
5353
import org.fife.ui.rtextarea.RTextScrollPane;
54+
import org.fife.ui.rtextarea.RUndoManager;
5455

5556
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
5657
import processing.app.helpers.DocumentTextChangeListener;
@@ -63,37 +64,64 @@
6364
/**
6465
* Single tab, editing a single file, in the main window.
6566
*/
66-
public class EditorTab extends JPanel {
67+
public class EditorTab extends JPanel implements SketchCode.TextStorage {
6768
protected Editor editor;
6869
protected SketchTextArea textarea;
6970
protected RTextScrollPane scrollPane;
7071
protected SketchCode code;
72+
protected boolean modified;
7173

72-
public EditorTab(Editor editor, SketchCode code) throws IOException {
74+
/**
75+
* Create a new EditorTab
76+
*
77+
* @param editor
78+
* The Editor this tab runs in
79+
* @param code
80+
* The file to display in this tab
81+
* @param contents
82+
* Initial contents to display in this tab. Can be used when
83+
* editing a file that doesn't exist yet. If null is passed,
84+
* code.load() is called and displayed instead.
85+
* @throws IOException
86+
*/
87+
public EditorTab(Editor editor, SketchCode code, String contents)
88+
throws IOException {
7389
super(new BorderLayout());
90+
91+
// Load initial contents contents from file if nothing was specified.
92+
if (contents == null) {
93+
contents = code.load();
94+
modified = false;
95+
} else {
96+
modified = true;
97+
}
98+
7499
this.editor = editor;
75100
this.code = code;
76-
this.textarea = createTextArea();
101+
RSyntaxDocument document = createDocument(contents);
102+
this.textarea = createTextArea(document);
77103
this.scrollPane = createScrollPane(this.textarea);
78104
applyPreferences();
79105
add(this.scrollPane, BorderLayout.CENTER);
80-
81-
UndoManager undo = new LastUndoableEditAwareUndoManager(this.textarea, this.editor);
82-
((RSyntaxDocument)textarea.getDocument()).addUndoableEditListener(undo);
106+
107+
RUndoManager undo = new LastUndoableEditAwareUndoManager(this.textarea, this.editor);
108+
document.addUndoableEditListener(undo);
109+
textarea.setUndoManager(undo);
110+
code.setStorage(this);
83111
}
84112

85-
private RSyntaxDocument createDocument() {
113+
private RSyntaxDocument createDocument(String contents) {
86114
RSyntaxDocument document = new RSyntaxDocument(new ArduinoTokenMakerFactory(editor.base.getPdeKeywords()), RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS);
87115
document.putProperty(PlainDocument.tabSizeAttribute, PreferencesData.getInteger("editor.tabs.size"));
88-
116+
89117
// insert the program text into the document object
90118
try {
91-
document.insertString(0, code.getProgram(), null);
119+
document.insertString(0, contents, null);
92120
} catch (BadLocationException bl) {
93121
bl.printStackTrace();
94122
}
95123
document.addDocumentListener(new DocumentTextChangeListener(
96-
() -> code.setModified(true)));
124+
() -> setModified(true)));
97125
return document;
98126
}
99127

@@ -112,8 +140,8 @@ private RTextScrollPane createScrollPane(SketchTextArea textArea) throws IOExcep
112140
return scrollPane;
113141
}
114142

115-
private SketchTextArea createTextArea() throws IOException {
116-
RSyntaxDocument document = createDocument();
143+
private SketchTextArea createTextArea(RSyntaxDocument document)
144+
throws IOException {
117145
final SketchTextArea textArea = new SketchTextArea(document, editor.base.getPdeKeywords());
118146
textArea.setName("editor");
119147
textArea.setFocusTraversalKeysEnabled(false);
@@ -318,6 +346,29 @@ public void setText(String what) {
318346
textarea.setText(what);
319347
}
320348

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

app/src/processing/app/Sketch.java

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

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

@@ -319,7 +316,6 @@ protected void nameCode(String newName) {
319316

320317
// first get the contents of the editor text area
321318
if (current.getCode().isModified()) {
322-
current.getCode().setProgram(editor.getCurrentTab().getText());
323319
try {
324320
// save this new SketchCode
325321
current.getCode().save();
@@ -402,7 +398,7 @@ protected void nameCode(String newName) {
402398
ensureExistence();
403399
SketchCode code = (new SketchCodeDocument(this, newFile)).getCode();
404400
try {
405-
editor.addTab(code);
401+
editor.addTab(code, "");
406402
} catch (IOException e) {
407403
Base.showWarning(tr("Error"),
408404
I18n.format(
@@ -509,39 +505,28 @@ public void handleNextCode() {
509505
setCurrentCode((currentIndex + 1) % data.getCodeCount());
510506
}
511507

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

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

542523

543524
public boolean isModified() {
544-
return modified;
525+
for (SketchCode code : data.getCodes()) {
526+
if (code.isModified())
527+
return true;
528+
}
529+
return false;
545530
}
546531

547532

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

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

607584
data.save();
608-
calcModified();
609585
return true;
610586
}
611587

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

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

971928

@@ -987,10 +944,6 @@ private void setCurrentCode(int which, boolean forceUpdate) {
987944
return;
988945
}
989946

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

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

@@ -1223,7 +1174,6 @@ private void ensureExistence() {
12231174
"but anything besides the code will be lost."), null);
12241175
try {
12251176
data.getFolder().mkdirs();
1226-
modified = true;
12271177

12281178
for (SketchCode code : data.getCodes()) {
12291179
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)