Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: xdg-open is now used to reveal a file Linux file system (#674) #711

Merged
merged 34 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
107ad14
Using dolphin for testing.
Oliver-Loeffler Sep 2, 2024
9c9a56b
Added first draft of linux specific file browser chooser.
Oliver-Loeffler Sep 2, 2024
f95f10c
Added logging.
Oliver-Loeffler Sep 2, 2024
0cf3ada
Reworked file browser detection.
Oliver-Loeffler Sep 2, 2024
9b76b20
Just reconfigured EditorPlatform to use xdg-open.
Oliver-Loeffler Sep 3, 2024
86a10ab
Restored order of imports.
Oliver-Loeffler Sep 4, 2024
9e520ca
Reworked reveal-x functionality in app so that there is a unified loo…
Oliver-Loeffler Sep 24, 2024
13e7ce4
Reworked the way files are revealed in explorer or other file system …
Oliver-Loeffler Sep 24, 2024
15ea41b
Added updated messages and error handling for remaining calls to Edit…
Oliver-Loeffler Sep 24, 2024
c48b682
Added proper error dialog for CSS Panel Controller in case revealing …
Oliver-Loeffler Sep 24, 2024
0aa9317
Removed empty lines.
Oliver-Loeffler Sep 24, 2024
5acbcab
Merge branch 'gluonhq:master' into issue-674
Oliver-Loeffler Sep 24, 2024
f6c889b
Checkstyle fix.
Oliver-Loeffler Sep 24, 2024
0f62394
Merge branch 'gluonhq:master' into issue-674
Oliver-Loeffler Sep 25, 2024
a1323dc
Merge branch 'gluonhq:master' into issue-674
Oliver-Loeffler Sep 27, 2024
be506d1
Merge branch 'gluonhq:master' into issue-674
Oliver-Loeffler Sep 27, 2024
330f45d
Merge branch 'gluonhq:master' into issue-674
Oliver-Loeffler Sep 29, 2024
f57c761
Removed accidently added illegal characters in properties file.
Oliver-Loeffler Sep 29, 2024
9dcb7f9
Removed accidently pasted text from license header.
Oliver-Loeffler Sep 29, 2024
3ec5ac3
Added basic test for external command execution.
Oliver-Loeffler Sep 29, 2024
bb1bfd0
Added requirement of xdg-utils for Linux to README.md
Oliver-Loeffler Sep 29, 2024
db5ad5d
Shell scripts for test now properly state 5sec wait time.
Oliver-Loeffler Sep 29, 2024
281ec05
Lowered log level as proposed in PR.
Oliver-Loeffler Sep 29, 2024
8773fae
Removed whitespace.
Oliver-Loeffler Sep 29, 2024
745a558
Removed whitespace
Oliver-Loeffler Sep 29, 2024
8b334bd
Removed whitespace
Oliver-Loeffler Sep 29, 2024
0693f81
Corrected README.md according to PR feedback.
Oliver-Loeffler Sep 29, 2024
9a79e77
ExecuteDaemon is now wrapped in a Task, so that it cannot block UI an…
Oliver-Loeffler Sep 29, 2024
007795d
Update README.md
Oliver-Loeffler Sep 30, 2024
2164068
Update EditorPlatform.java
Oliver-Loeffler Sep 30, 2024
bcd5cd0
Changed log level according to feedbck.
Oliver-Loeffler Sep 30, 2024
ca9f5c6
Removed JavaFX task. Refactoring of the execution method will be part…
Oliver-Loeffler Sep 30, 2024
0d512f6
Removed empty line.
Oliver-Loeffler Sep 30, 2024
b00da05
Applied formatter.
Oliver-Loeffler Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ See the [documentation](http://docs.gluonhq.com/scenebuilder/) about the new fea

For community support, go to [StackOverflow](https://stackoverflow.com/questions/tagged/scenebuilder).

### Requirements on Linux ###

On Linux systems, Scene Builder uses the `xdg-open` to command to reveal files in the system's default file system browser or to open URLs in the default web browser. Most modern Linux Desktop Environments already provide the [xdg-utils](https://freedesktop.org/wiki/Software/xdg-utils/) package. If it is missing, it can be installed using the respective Linux package management tool such as `yum`, `apt-get`, `dnf` or `pacman`. The `xdg-utils` package is usually available on KDE based systems, some Arch based systems may require manual installations.
Oliver-Loeffler marked this conversation as resolved.
Show resolved Hide resolved

## Issues and Contributions ##

Issues can be reported to the [Issue tracker](https://github.com/gluonhq/scenebuilder/issues/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.oracle.javafx.scenebuilder.kit.editor.EditorController.ControlAction;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController.EditAction;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.editor.FileBrowserRevealException;
import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.editor.panel.content.ContentPanelController;
import com.oracle.javafx.scenebuilder.kit.editor.panel.css.CssPanelController;
Expand Down Expand Up @@ -94,6 +95,8 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.InvalidationListener;
import javafx.beans.binding.Bindings;
Expand Down Expand Up @@ -184,6 +187,8 @@ public enum ActionStatus {
DONE
}

private static final Logger LOGGER = Logger.getLogger(DocumentWindowController.class.getName());

private final EditorController editorController = new EditorController();
private final MenuBarController menuBarController = new MenuBarController(this);
private final ContentPanelController contentPanelController = new ContentPanelController(editorController);
Expand Down Expand Up @@ -1467,14 +1472,13 @@ void onLibraryImportSelection(ActionEvent event) {
@FXML
void onLibraryRevealCustomFolder(ActionEvent event) {
String userLibraryPath = ((UserLibrary) getEditorController().getLibrary()).getPath();

// ensure that there is no mixup of forward and backward slashes.
File libraryPath = Paths.get(userLibraryPath).normalize().toFile();
try {
EditorPlatform.revealInFileBrowser(new File(userLibraryPath));
} catch(IOException x) {
final ErrorDialog errorDialog = new ErrorDialog(null);
errorDialog.setMessage(I18N.getString("alert.reveal.failure.message", getStage().getTitle()));
errorDialog.setDetails(I18N.getString("alert.reveal.failure.details"));
errorDialog.setDebugInfoWithThrowable(x);
errorDialog.showAndWait();
EditorPlatform.revealInFileBrowser(libraryPath);
} catch (Exception revealError) {
handleRevealFolderException(revealError, String.valueOf(libraryPath));
}
}

Expand Down Expand Up @@ -2185,26 +2189,33 @@ ActionStatus performCloseAction() {
return closeConfirmed ? ActionStatus.DONE : ActionStatus.CANCELLED;
}


private void performRevealAction() {
assert editorController.getFxomDocument() != null;
assert editorController.getFxomDocument().getLocation() != null;

final URL location = editorController.getFxomDocument().getLocation();


File fxmlFile = null;
try {
/*
* Using Path.normalize().toAbsolutePath() ensures that forward and backward slashes are not mixed
* and the path matches the platform requirements. It also ensures, that the file:/ prefix is
* removed from paths and users can directly use the path in their attempt to investigate the error.
*/
fxmlFile = Path.of(location.toURI()).normalize().toAbsolutePath().toFile();
} catch (URISyntaxException e) {
handleRevealResourceException(e, String.valueOf(location));
}

try {
final File fxmlFile = new File(location.toURI());
EditorPlatform.revealInFileBrowser(fxmlFile);
} catch(IOException | URISyntaxException x) {
final ErrorDialog errorDialog = new ErrorDialog(null);
errorDialog.setMessage(I18N.getString("alert.reveal.failure.message", getStage().getTitle()));
errorDialog.setDetails(I18N.getString("alert.reveal.failure.details"));
errorDialog.setDebugInfoWithThrowable(x);
errorDialog.showAndWait();
} catch (FileBrowserRevealException re) {
handleRevealFileException(re, String.valueOf(fxmlFile));
} catch (IOException x) {
handleRevealResourceException(x, String.valueOf(fxmlFile));
}
}


private void updateLoadFileTime() {

final URL fxmlURL = editorController.getFxmlLocation();
Expand Down Expand Up @@ -2275,17 +2286,64 @@ private void performHelp() {

private void openURL(String url) {
try {
LOGGER.log(Level.FINE, "Attempting to open URL: {0}", url);
EditorPlatform.open(url);
} catch (IOException ioe) {
handleErrorWhenOpeningURL(ioe, url);
LOGGER.log(Level.WARNING, "Error during attempt to open URL!", ioe);
handleRevealResourceException(ioe, url);
}
}

private void handleErrorWhenOpeningURL(IOException ioe, String url) {
final ErrorDialog errorDialog = new ErrorDialog(null);
errorDialog.setMessage(I18N.getString("alert.help.failure.message", url));
errorDialog.setDetails(I18N.getString("alert.messagebox.failure.details"));
errorDialog.setDebugInfoWithThrowable(ioe);
/**
* When an FXML file is to be revealed and an error occurs, the file and the error are displayed in this dialog.
* The error type and its details are displayed within the details window. The dialog itself only
* notifies that there was an error.
*
* @param fileRevealException {@link FileBrowserRevealException}
* @param locationToBeRevealed {@link String}
*/
private void handleRevealFileException(FileBrowserRevealException fileRevealException, String locationToBeRevealed) {
final ErrorDialog errorDialog = new ErrorDialog(this.getStage());
errorDialog.setTitle(I18N.getString("alert.error.file.reveal.title"));
errorDialog.setMessage(I18N.getString("alert.error.file.reveal.message"));
errorDialog.setDetails(I18N.getString("alert.error.file.reveal.details", locationToBeRevealed));
errorDialog.setDetailsTitle(I18N.getString("alert.error.file.reveal.details.title"));
errorDialog.setDebugInfoWithThrowable(fileRevealException);
errorDialog.showAndWait();
}

/**
*
Oliver-Loeffler marked this conversation as resolved.
Show resolved Hide resolved
* Resources may be related to files included with Scene Builder or with arbitrary files (e.g. CSS sheets)
* co-located to a given FXML file. In case of an error opening a resource, a custom dialog is shown.
*
* @param revealException {@link Exception}
* @param locationToBeRevealed {@link String}
*/
private void handleRevealResourceException(Exception revealException, String locationToBeRevealed) {
final ErrorDialog errorDialog = new ErrorDialog(this.getStage());
errorDialog.setTitle(I18N.getString("alert.error.resource.reveal.title"));
errorDialog.setMessage(I18N.getString("alert.error.resource.reveal.message"));
errorDialog.setDetails(I18N.getString("alert.error.resource.reveal.details", locationToBeRevealed));
errorDialog.setDetailsTitle(I18N.getString("alert.error.resource.reveal.details.title"));
errorDialog.setDebugInfoWithThrowable(revealException);
errorDialog.showAndWait();
}

/**
*
Oliver-Loeffler marked this conversation as resolved.
Show resolved Hide resolved
* In some cases the resource to be revealed or opened is a directory. Hence the UI dialog should reflect this.
*
* @param revealException {@link Exception}
* @param directoryToBeRevealed {@link String}
*/
private void handleRevealFolderException(Exception revealException, String directoryToBeRevealed) {
final ErrorDialog errorDialog = new ErrorDialog(this.getStage());
errorDialog.setTitle(I18N.getString("alert.error.directory.reveal.title"));
errorDialog.setMessage(I18N.getString("alert.error.directory.reveal.message"));
errorDialog.setDetails(I18N.getString("alert.error.directory.reveal.details", directoryToBeRevealed));
errorDialog.setDetailsTitle(I18N.getString("alert.error.directory.reveal.details.title"));
errorDialog.setDebugInfoWithThrowable(revealException);
errorDialog.showAndWait();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.oracle.javafx.scenebuilder.app.i18n.I18N;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform;
import com.oracle.javafx.scenebuilder.kit.editor.FileBrowserRevealException;
import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.ErrorDialog;
import javafx.stage.FileChooser;

Expand Down Expand Up @@ -97,10 +98,11 @@ public void performRevealResource() {
try {
EditorPlatform.revealInFileBrowser(resourceFile);
} catch (IOException ioe) {
final ErrorDialog errorDialog = new ErrorDialog(null);
errorDialog.setTitle(I18N.getString("error.file.reveal.title"));
errorDialog.setMessage(I18N.getString("error.file.reveal.message"));
errorDialog.setDetails(I18N.getString("error.filesystem.details"));
final ErrorDialog errorDialog = new ErrorDialog(documentWindowController.getStage());
errorDialog.setTitle(I18N.getString("alert.error.file.reveal.title"));
errorDialog.setMessage(I18N.getString("alert.error.file.reveal.message"));
errorDialog.setDetails(I18N.getString("alert.error.file.reveal.details", resourceFile));
errorDialog.setDetailsTitle(I18N.getString("alert.error.file.reveal.details.title"));
errorDialog.setDebugInfoWithThrowable(ioe);
errorDialog.showAndWait();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright (c) 2024, Gluon and/or its affiliates.
* Copyright (c) 2012, 2014, Oracle and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*
Expand Down Expand Up @@ -102,10 +103,11 @@ public void performOpenSceneStyleSheet(File toOpen) {
try {
EditorPlatform.open(toOpen.getPath());
} catch (IOException ioe) {
final ErrorDialog errorDialog = new ErrorDialog(null);
final ErrorDialog errorDialog = new ErrorDialog(documentWindowController.getStage());
errorDialog.setTitle(I18N.getString("error.file.open.title"));
errorDialog.setMessage(I18N.getString("error.file.open.message"));
errorDialog.setMessage(I18N.getString("error.file.open.message", toOpen));
errorDialog.setDetails(I18N.getString("error.filesystem.details"));
errorDialog.setDetailsTitle(I18N.getString("error.file.open.title"));
errorDialog.setDebugInfoWithThrowable(ioe);
errorDialog.showAndWait();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,6 @@ alert.messagebox.failure.message = Could not process open request
alert.messagebox.failure.details = An unexpected failure happened during open operation. Please report this failure to support.
alert.revert.question.message = Do you want to revert to the last saved version of ''{0}'' ?
alert.revert.question.details = Your current changes will be lost.
alert.reveal.failure.message = Could not reveal ''{0}''
alert.reveal.failure.details = Reveal operation has failed. Check logs of the operating system.
alert.copy.failure.message = Could not copy ''{0}''
alert.help.failure.message = Could not access to ''{0}''
alert.delete.fxid1of1.message = This component has an fx:id. Do you really want to delete it ?
Expand Down Expand Up @@ -470,11 +468,29 @@ file.filter.label.image = Image Document
file.filter.label.media = Media Document
file.filter.label.video = Video Document
error.file.open.title = File opening failed
error.file.open.details.title = File opening failed - Details
# {0} is a file path
error.file.open.message = File {0} cannot be opened
error.filesystem.details = Please check your file system.
error.file.reveal.title = File locating failed
error.file.reveal.message = File {0} cannot be found
error.filesystem.details = File: {0}

# File Reveal Error Dialog
alert.error.file.reveal.title = Failed to reveal file ...
Oliver-Loeffler marked this conversation as resolved.
Show resolved Hide resolved
alert.error.file.reveal.message = Cannot reveal file in file system viewer due to an error.
alert.error.file.reveal.details = ''{0}''
alert.error.file.reveal.details.title = File Reveal Error Details

# Directory Reveal Error Dialog
alert.error.directory.reveal.title = Failed to reveal folder ...
alert.error.directory.reveal.message = Cannot reveal folder in file system viewer due to an error.
alert.error.directory.reveal.details = ''{0}''
alert.error.directory.reveal.details.title = Folder Reveal Error Details

# Resource Reveal Error Dialog
alert.error.resource.reveal.title = Failed to reveal resource ...
alert.error.resource.reveal.message = Cannot reveal resource in file system viewer due to an error.
alert.error.resource.reveal.details = ''{0}''
alert.error.resource.reveal.details.title = Resource Reveal Error Details

#
log.start = JavaFX Scene Builder started
log.stop = JavaFX Scene Builder stopped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,6 @@ alert.messagebox.failure.message = 開く操作を処理できませんでした
alert.messagebox.failure.details = 開く操作中に予期しない問題が起きました。この問題をサポートに報告してください。
alert.revert.question.message = 最後に保存した''{0}''に戻しますか。
alert.revert.question.details = 現在の変更は失われます。
alert.reveal.failure.message = ''{0}''を表示できませんでした
alert.reveal.failure.details = 元に戻すのに失敗しました。オペレーティング・システムのログを確認してください。
alert.copy.failure.message = コピー
alert.help.failure.message = ''{0}''にアクセスできませんでした
alert.delete.fxid1of1.message = このコンポーネントにはfx:idがあります。このコンポーネントを削除しますか。
Expand Down Expand Up @@ -457,7 +455,7 @@ file.filter.label.fxml = FXMLドキュメント
file.filter.label.image = イメージ・ドキュメント
file.filter.label.media = メディア・ドキュメント
file.filter.label.video = ビデオ・ドキュメント
error.file.open.title = ファイルを開くのに失敗しました

# {0} is a file path
error.file.open.message = ファイル{0}を開けません
error.filesystem.details = ファイル・システムを確認してください。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,6 @@ alert.messagebox.failure.message = 无法处理打开的请求
alert.messagebox.failure.details = 打开操作期间发生意外故障。请向支持部门报告此故障。
alert.revert.question.message = 是否要恢复到上次保存的 ''{0}'' 版本?
alert.revert.question.details = 您当前的更改将丢失。
alert.reveal.failure.message = 无法显示 ''{0}''
alert.reveal.failure.details = 显示操作失败。检查操作系统的日志。
alert.copy.failure.message = 无法复制 ''{0}''
alert.help.failure.message = 无法访问 ''{0}''
alert.delete.fxid1of1.message = 此组件具有 fx:id,你真的要删除它吗?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2024, Gluon and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*
* This file is available and licensed under the following license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* - Neither the name of Oracle Corporation and Gluon nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.javafx.scenebuilder.kit.editor;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
* Cmd allows the execution of a given command line in a defined working directory. The execution is
* aborted after the timeout.
*/
final class Cmd {

/**
* Executes a given command line using the working directory and timeout.
*
* @param cmd The command line to be executed defined as a {@link List} of {@link String}
* @param wDir The working directory, where the process shall be executed within.
* @param timeoutSec Duration in in seconds after which the execution should be stopped.
* @return exit code of the command line as an Integer
*
* @throws IOException - if the command was not found or the program execution exceeds the given timeout duration.
* @throws InterruptedException - if the current thread is interrupted while waiting
*/
public final Integer exec(List<String> cmd, File wDir, long timeoutSec) throws IOException,
InterruptedException {
ProcessBuilder builder = new ProcessBuilder(cmd);
builder = builder.directory(wDir);
Process proc = builder.start();
boolean completed = proc.waitFor(timeoutSec, TimeUnit.SECONDS);
if (completed) {
return proc.exitValue();
}
throw new IOException("Process timed out after %s seconds!".formatted(timeoutSec));
}

}
Loading