Skip to content

Commit 9d992d3

Browse files
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 71d458e commit 9d992d3

File tree

6 files changed

+269
-240
lines changed

6 files changed

+269
-240
lines changed

app/src/processing/app/Editor.java

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,13 @@ public EditorTab findTab(final SketchFile file) {
16511651
return tabs.get(findTabIndex(file));
16521652
}
16531653

1654+
/**
1655+
* Finds the index of the tab showing the given file. Matches the file against
1656+
* EditorTab.getSketchFile() using ==.
1657+
*
1658+
* @returns The index of the tab for the given file, or -1 if no such tab was
1659+
* found.
1660+
*/
16541661
public int findTabIndex(final SketchFile file) {
16551662
for (int i = 0; i < tabs.size(); ++i) {
16561663
if (tabs.get(i).getSketchFile() == file)
@@ -1659,6 +1666,21 @@ public int findTabIndex(final SketchFile file) {
16591666
return -1;
16601667
}
16611668

1669+
/**
1670+
* Finds the index of the tab showing the given file. Matches the file against
1671+
* EditorTab.getSketchFile().getFile() using equals.
1672+
*
1673+
* @returns The index of the tab for the given file, or -1 if no such tab was
1674+
* found.
1675+
*/
1676+
public int findTabIndex(final File file) {
1677+
for (int i = 0; i < tabs.size(); ++i) {
1678+
if (tabs.get(i).getSketchFile().getFile().equals(file))
1679+
return i;
1680+
}
1681+
return -1;
1682+
}
1683+
16621684
/**
16631685
* Create tabs for each of the current sketch's files, removing any existing
16641686
* tabs.
@@ -1880,23 +1902,6 @@ protected boolean checkModified() {
18801902
}
18811903
}
18821904

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

app/src/processing/app/EditorTab.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ public void activated() {
331331
/**
332332
* Reload the contents of our file.
333333
*/
334-
private void reload() {
334+
public void reload() {
335335
String text;
336336
try {
337337
text = file.load();

app/src/processing/app/SketchController.java

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

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

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

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

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

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

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

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

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

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

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

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

592440
// Make sure that it's not an untitled sketch
593441
setUntitled(false);
@@ -720,16 +568,24 @@ public boolean addFile(File sourceFile) {
720568
}
721569

722570
if (!isData) {
723-
SketchFile newFile = new SketchFile(destFile, false);
724-
571+
int tabIndex;
725572
if (replacement) {
726-
sketch.replaceFile(newFile);
727-
573+
tabIndex = editor.findTabIndex(destFile);
574+
editor.getTabs().get(tabIndex).reload();
728575
} else {
729-
ensureExistence();
730-
sketch.addFile(newFile);
576+
SketchFile sketchFile;
577+
try {
578+
sketchFile = sketch.addFile(destFile.getName());
579+
editor.addTab(sketchFile, null);
580+
} catch (IOException e) {
581+
// This does not pass on e, to prevent showing a backtrace for
582+
// "normal" errors.
583+
Base.showWarning(tr("Error"), e.getMessage(), null);
584+
return false;
585+
}
586+
tabIndex = editor.findTabIndex(sketchFile);
731587
}
732-
editor.selectTab(editor.findTabIndex(newFile));
588+
editor.selectTab(tabIndex);
733589
}
734590
return true;
735591
}

0 commit comments

Comments
 (0)