Skip to content

Commit 72f815b

Browse files
matthijskooijmanfacchinm
authored andcommitted
Refactor file adding and renaming, and save as handling
This commits replaces a significant part of the code handling these features. A lot of responsibilities are moved from SketchController to Sketch, though the code involved is rewritten mostly. Most of the handling now happens inside Sketch, including various checks against the new filename. Basically SketchController processes the user input to decide what needs to be done, and Sketch checks if it can be done and does it. If problems occur, an IOException is thrown, using a translated error message that is shown by SketchController as-is. This might not be the best way to transfer error messages (regular IOExceptions might contain less-friendly messages), so this might need further improvement later. In addition to moving around code and responsibilities, this code also changes behaviour in some places: - Because Sketch and SketchFile are now in control of renames and saves, they can update their internal state after a rename. This removes the need for reloading the entire sketch after a rename or save as and allows `Editor.handleOpenUnchecked()` to be removed. - When renaming the entire sketch, all files used to be saved before renaming, since the sketch would be re-opened after renaming. Since the re-opening no longer happens, there is no longer a need to save the sketch, so any unsaved changes remain unsaved in the editor after renaming the sketch. - When renaming or adding new files, duplicate filenames are detected. Initially, this happened case sensitively, but it was later changed to use case insensitive matching to prevent problems on Windows (where filenames cannot differ in just case). To prevent complexity, this did not distinguish between systems. In commit 5fbf962 (Sketch rename: allowig a case change rename if NOT on windows), the intention was to only do case insensitive checking on Windows, but it effectively disabled all checking on other systems, making the check not catch duplicate filenames at all. With this commit, all these checks are done using `File.equals()` instead of comparing strings, which is already aware of the case sensitivity of the platform and should act accordingly. - Some error messages were changed. - When adding a file, an empty file is not created directly, but only a SketchFile and EditorTab is added. When the sketch is saved, the file is created. - When importing a file that already exists (thus overwriting it), instead of replacing the SketchFile instance, this just lets the EditorTab reload its contents. This was broken since the introduction of EditorTab. The file would be replaced, but not this was not reflected in the editor, which is now fixed. This change allows `Sketch.replaceFile()` to be removed. - When importing a file that does not exist yet (thus adding it), a tab is now also added for it (in addition to a SketchFile). This was broken since the introduction of EditorTab, and would result in the file being added, but not shown in the editor. This commit adds a `Sketch.renameFileTo()` method, to rename a single file within the sketch. It would be better to integrate its contents into `Sketch.renameTo()`, but that does not have access to the `Sketch` instance it is contained in. This will be changed in a future commit.
1 parent 9705e1e commit 72f815b

File tree

6 files changed

+269
-240
lines changed

6 files changed

+269
-240
lines changed

app/src/processing/app/Editor.java

+22-17
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,13 @@ public EditorTab findTab(final SketchFile file) {
16421642
return tabs.get(findTabIndex(file));
16431643
}
16441644

1645+
/**
1646+
* Finds the index of the tab showing the given file. Matches the file against
1647+
* EditorTab.getSketchFile() using ==.
1648+
*
1649+
* @returns The index of the tab for the given file, or -1 if no such tab was
1650+
* found.
1651+
*/
16451652
public int findTabIndex(final SketchFile file) {
16461653
for (int i = 0; i < tabs.size(); ++i) {
16471654
if (tabs.get(i).getSketchFile() == file)
@@ -1650,6 +1657,21 @@ public int findTabIndex(final SketchFile file) {
16501657
return -1;
16511658
}
16521659

1660+
/**
1661+
* Finds the index of the tab showing the given file. Matches the file against
1662+
* EditorTab.getSketchFile().getFile() using equals.
1663+
*
1664+
* @returns The index of the tab for the given file, or -1 if no such tab was
1665+
* found.
1666+
*/
1667+
public int findTabIndex(final File file) {
1668+
for (int i = 0; i < tabs.size(); ++i) {
1669+
if (tabs.get(i).getSketchFile().getFile().equals(file))
1670+
return i;
1671+
}
1672+
return -1;
1673+
}
1674+
16531675
/**
16541676
* Create tabs for each of the current sketch's files, removing any existing
16551677
* tabs.
@@ -1872,23 +1894,6 @@ protected boolean checkModified() {
18721894
}
18731895
}
18741896

1875-
1876-
/**
1877-
* Open a sketch from a particular path, but don't check to save changes.
1878-
* Used by Sketch.saveAs() to re-open a sketch after the "Save As"
1879-
*/
1880-
protected void handleOpenUnchecked(File file, int codeIndex,
1881-
int selStart, int selStop, int scrollPos) {
1882-
handleOpenInternal(file);
1883-
// Replacing a document that may be untitled. If this is an actual
1884-
// untitled document, then editor.untitled will be set by Base.
1885-
untitled = false;
1886-
1887-
selectTab(codeIndex);
1888-
getCurrentTab().setSelection(selStart, selStop);
1889-
getCurrentTab().setScrollPosition(scrollPos);
1890-
}
1891-
18921897
/**
18931898
* Second stage of open, occurs after having checked to see if the
18941899
* modifications (if any) to the previous sketch need to be saved.

app/src/processing/app/EditorTab.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ public void activated() {
334334
/**
335335
* Reload the contents of our file.
336336
*/
337-
private void reload() {
337+
public void reload() {
338338
String text;
339339
try {
340340
text = file.load();

app/src/processing/app/SketchController.java

+57-201
Original file line numberDiff line numberDiff line change
@@ -133,213 +133,79 @@ public void handleRenameCode() {
133133
* where they diverge.
134134
*/
135135
protected void nameCode(String newName) {
136-
SketchFile current = editor.getCurrentTab().getSketchFile();
137-
int currentIndex = editor.getCurrentTabIndex();
138-
139136
// make sure the user didn't hide the sketch folder
140137
ensureExistence();
141138

142-
// Add the extension here, this simplifies some of the logic below.
143-
if (newName.indexOf('.') == -1) {
144-
newName += "." + Sketch.DEFAULT_SKETCH_EXTENSION;
145-
}
146-
147-
// if renaming to the same thing as before, just ignore.
148-
// also ignoring case here, because i don't want to write
149-
// a bunch of special stuff for each platform
150-
// (osx is case insensitive but preserving, windows insensitive,
151-
// *nix is sensitive and preserving.. argh)
152-
if (renamingCode) {
153-
if (newName.equalsIgnoreCase(current.getFileName())
154-
&& OSUtils.isWindows()) {
155-
// exit quietly for the 'rename' case.
156-
// if it's a 'new' then an error will occur down below
157-
return;
158-
}
159-
}
160-
161139
newName = newName.trim();
162140
if (newName.equals("")) return;
163141

164-
int dot = newName.indexOf('.');
165-
if (dot == 0) {
142+
if (newName.charAt(0) == '.') {
166143
Base.showWarning(tr("Problem with rename"),
167144
tr("The name cannot start with a period."), null);
168145
return;
169146
}
170147

171148
FileUtils.SplitFile split = FileUtils.splitFilename(newName);
149+
if (split.extension.equals(""))
150+
split.extension = Sketch.DEFAULT_SKETCH_EXTENSION;
151+
172152
if (!Sketch.EXTENSIONS.contains(split.extension)) {
173-
Base.showWarning(tr("Problem with rename"),
174-
I18n.format(tr("\".{0}\" is not a valid extension."),
175-
split.extension),
176-
null);
153+
String msg = I18n.format(tr("\".{0}\" is not a valid extension."),
154+
split.extension);
155+
Base.showWarning(tr("Problem with rename"), msg, null);
177156
return;
178157
}
179158

180-
// Don't let the user create the main tab as a .java file instead of .pde
181-
if (!split.extension.equals(Sketch.DEFAULT_SKETCH_EXTENSION)) {
182-
if (renamingCode) { // If creating a new tab, don't show this error
183-
if (current.isPrimary()) { // If this is the main tab, disallow
184-
Base.showWarning(tr("Problem with rename"),
185-
tr("The main file can't use an extension.\n" +
186-
"(It may be time for your to graduate to a\n" +
187-
"\"real\" programming environment)"), null);
188-
return;
189-
}
190-
}
191-
}
192-
193159
// Sanitize name
194-
String sanitaryName = BaseNoGui.sanitizeName(split.basename);
195-
newName = sanitaryName + "." + split.extension;
196-
197-
// In Arduino, we want to allow files with the same name but different
198-
// extensions, so compare the full names (including extensions). This
199-
// might cause problems: http://dev.processing.org/bugs/show_bug.cgi?id=543
200-
for (SketchFile file : sketch.getFiles()) {
201-
if (newName.equalsIgnoreCase(file.getFileName()) && OSUtils.isWindows()) {
202-
Base.showMessage(tr("Error"),
203-
I18n.format(
204-
tr("A file named \"{0}\" already exists in \"{1}\""),
205-
file.getFileName(),
206-
sketch.getFolder().getAbsolutePath()
207-
));
208-
return;
209-
}
210-
}
211-
212-
213-
File newFile = new File(sketch.getFolder(), newName);
214-
// if (newFile.exists()) { // yay! users will try anything
215-
// Base.showMessage("Error",
216-
// "A file named \"" + newFile + "\" already exists\n" +
217-
// "in \"" + folder.getAbsolutePath() + "\"");
218-
// return;
219-
// }
220-
221-
// File newFileHidden = new File(folder, newName + ".x");
222-
// if (newFileHidden.exists()) {
223-
// // don't let them get away with it if they try to create something
224-
// // with the same name as something hidden
225-
// Base.showMessage("No Way",
226-
// "A hidden tab with the same name already exists.\n" +
227-
// "Use \"Unhide\" to bring it back.");
228-
// return;
229-
// }
160+
split.basename = BaseNoGui.sanitizeName(split.basename);
161+
newName = split.join();
230162

231163
if (renamingCode) {
232-
if (current.isPrimary()) {
233-
// get the new folder name/location
234-
String folderName = newName.substring(0, newName.indexOf('.'));
235-
File newFolder = new File(sketch.getFolder().getParentFile(), folderName);
236-
if (newFolder.exists()) {
237-
Base.showWarning(tr("Cannot Rename"),
238-
I18n.format(
239-
tr("Sorry, a sketch (or folder) named " +
240-
"\"{0}\" already exists."),
241-
newName
242-
), null);
243-
return;
244-
}
245-
246-
// unfortunately this can't be a "save as" because that
247-
// only copies the sketch files and the data folder
248-
// however this *will* first save the sketch, then rename
249-
250-
// first get the contents of the editor text area
251-
if (current.isModified()) {
252-
try {
253-
// save this new SketchFile
254-
current.save();
255-
} catch (Exception e) {
256-
Base.showWarning(tr("Error"), tr("Could not rename the sketch. (0)"), e);
257-
return;
258-
}
259-
}
164+
SketchFile current = editor.getCurrentTab().getSketchFile();
260165

261-
if (!current.renameTo(newFile)) {
262-
Base.showWarning(tr("Error"),
263-
I18n.format(
264-
tr("Could not rename \"{0}\" to \"{1}\""),
265-
current.getFileName(),
266-
newFile.getName()
267-
), null);
166+
if (current.isPrimary()) {
167+
if (!split.extension.equals(Sketch.DEFAULT_SKETCH_EXTENSION)) {
168+
Base.showWarning(tr("Problem with rename"),
169+
tr("The main file cannot use an extension"), null);
268170
return;
269171
}
270172

271-
// save each of the other tabs because this is gonna be re-opened
173+
// Primary file, rename the entire sketch
174+
final File parent = sketch.getFolder().getParentFile();
175+
File newFolder = new File(parent, split.basename);
272176
try {
273-
for (SketchFile file : sketch.getFiles()) {
274-
file.save();
275-
}
276-
} catch (Exception e) {
277-
Base.showWarning(tr("Error"), tr("Could not rename the sketch. (1)"), e);
278-
return;
279-
}
280-
281-
// now rename the sketch folder and re-open
282-
boolean success = sketch.getFolder().renameTo(newFolder);
283-
if (!success) {
284-
Base.showWarning(tr("Error"), tr("Could not rename the sketch. (2)"), null);
177+
sketch.renameTo(newFolder);
178+
} catch (IOException e) {
179+
// This does not pass on e, to prevent showing a backtrace for
180+
// "normal" errors.
181+
Base.showWarning(tr("Error"), e.getMessage(), null);
285182
return;
286183
}
287-
// if successful, set base properties for the sketch
288-
289-
File newMainFile = new File(newFolder, newName + ".ino");
290184

291-
// having saved everything and renamed the folder and the main .pde,
292-
// use the editor to re-open the sketch to re-init state
293-
// (unfortunately this will kill positions for carets etc)
294-
editor.handleOpenUnchecked(newMainFile,
295-
currentIndex,
296-
editor.getCurrentTab().getSelectionStart(),
297-
editor.getCurrentTab().getSelectionStop(),
298-
editor.getCurrentTab().getScrollPosition());
299-
300-
// get the changes into the sketchbook menu
301-
// (re-enabled in 0115 to fix bug #332)
302185
editor.base.rebuildSketchbookMenus();
303-
304-
} else { // else if something besides code[0]
305-
if (!current.renameTo(newFile)) {
306-
Base.showWarning(tr("Error"),
307-
I18n.format(
308-
tr("Could not rename \"{0}\" to \"{1}\""),
309-
current.getFileName(),
310-
newFile.getName()
311-
), null);
186+
} else {
187+
// Non-primary file, rename just that file
188+
try {
189+
sketch.renameFileTo(current, newName);
190+
} catch (IOException e) {
191+
// This does not pass on e, to prevent showing a backtrace for
192+
// "normal" errors.
193+
Base.showWarning(tr("Error"), e.getMessage(), null);
312194
return;
313195
}
314196
}
315197

316198
} else { // creating a new file
199+
SketchFile file;
317200
try {
318-
if (!newFile.createNewFile()) {
319-
// Already checking for IOException, so make our own.
320-
throw new IOException(tr("createNewFile() returned false"));
321-
}
322-
} catch (IOException e) {
323-
Base.showWarning(tr("Error"),
324-
I18n.format(
325-
"Could not create the file \"{0}\" in \"{1}\"",
326-
newFile,
327-
sketch.getFolder().getAbsolutePath()
328-
), e);
329-
return;
330-
}
331-
ensureExistence();
332-
SketchFile file = new SketchFile(newFile, false);
333-
try {
201+
file = sketch.addFile(newName);
334202
editor.addTab(file, "");
335203
} catch (IOException e) {
336-
Base.showWarning(tr("Error"),
337-
I18n.format(
338-
"Failed to open tab for new file"
339-
), e);
204+
// This does not pass on e, to prevent showing a backtrace for
205+
// "normal" errors.
206+
Base.showWarning(tr("Error"), e.getMessage(), null);
340207
return;
341208
}
342-
sketch.addFile(file);
343209
editor.selectTab(editor.findTabIndex(file));
344210
}
345211

@@ -558,35 +424,17 @@ protected boolean saveAs() throws IOException {
558424
// in fact, you can't do this on windows because the file dialog
559425
// will instead put you inside the folder, but it happens on osx a lot.
560426

561-
// now make a fresh copy of the folder
562-
newFolder.mkdirs();
563-
564-
// save the other tabs to their new location
565-
for (SketchFile file : sketch.getFiles()) {
566-
if (file.isPrimary()) continue;
567-
File newFile = new File(newFolder, file.getFileName());
568-
file.saveAs(newFile);
569-
}
570-
571-
// re-copy the data folder (this may take a while.. add progress bar?)
572-
if (sketch.getDataFolder().exists()) {
573-
File newDataFolder = new File(newFolder, "data");
574-
FileUtils.copy(sketch.getDataFolder(), newDataFolder);
427+
try {
428+
sketch.saveAs(newFolder);
429+
} catch (IOException e) {
430+
// This does not pass on e, to prevent showing a backtrace for "normal"
431+
// errors.
432+
Base.showWarning(tr("Error"), e.getMessage(), null);
575433
}
576-
577-
// save the main tab with its new name
578-
File newFile = new File(newFolder, newName + ".ino");
579-
sketch.getFile(0).saveAs(newFile);
580-
581-
editor.handleOpenUnchecked(newFile,
582-
editor.getCurrentTabIndex(),
583-
editor.getCurrentTab().getSelectionStart(),
584-
editor.getCurrentTab().getSelectionStop(),
585-
editor.getCurrentTab().getScrollPosition());
586-
587434
// Name changed, rebuild the sketch menus
588435
//editor.sketchbook.rebuildMenusAsync();
589436
editor.base.rebuildSketchbookMenus();
437+
editor.header.rebuild();
590438

591439
// Make sure that it's not an untitled sketch
592440
setUntitled(false);
@@ -719,16 +567,24 @@ public boolean addFile(File sourceFile) {
719567
}
720568

721569
if (!isData) {
722-
SketchFile newFile = new SketchFile(destFile, false);
723-
570+
int tabIndex;
724571
if (replacement) {
725-
sketch.replaceFile(newFile);
726-
572+
tabIndex = editor.findTabIndex(destFile);
573+
editor.getTabs().get(tabIndex).reload();
727574
} else {
728-
ensureExistence();
729-
sketch.addFile(newFile);
575+
SketchFile sketchFile;
576+
try {
577+
sketchFile = sketch.addFile(destFile.getName());
578+
editor.addTab(sketchFile, null);
579+
} catch (IOException e) {
580+
// This does not pass on e, to prevent showing a backtrace for
581+
// "normal" errors.
582+
Base.showWarning(tr("Error"), e.getMessage(), null);
583+
return false;
584+
}
585+
tabIndex = editor.findTabIndex(sketchFile);
730586
}
731-
editor.selectTab(editor.findTabIndex(newFile));
587+
editor.selectTab(tabIndex);
732588
}
733589
return true;
734590
}

0 commit comments

Comments
 (0)