diff --git a/src/main/java/com/sparrowwallet/sparrow/AppServices.java b/src/main/java/com/sparrowwallet/sparrow/AppServices.java index bbe56d67..f37fa157 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppServices.java @@ -9,6 +9,7 @@ import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.uri.BitcoinURI; import com.sparrowwallet.drongo.wallet.KeystoreSource; import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.sparrow.control.TextUtils; import com.sparrowwallet.sparrow.control.TrayManager; import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.io.Config; @@ -27,10 +28,7 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Dialog; -import javafx.scene.control.DialogPane; +import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.text.Font; import javafx.stage.Screen; @@ -38,6 +36,7 @@ import javafx.stage.Stage; import javafx.stage.Window; import javafx.util.Duration; import org.berndpruenster.netlayer.tor.Tor; +import org.controlsfx.control.HyperlinkLabel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +48,8 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class AppServices { @@ -514,6 +515,23 @@ public class AppServices { alert.getDialogPane().getScene().getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm()); alert.setTitle(title); alert.setHeaderText(title); + + Pattern linkPattern = Pattern.compile("\\[(http.+)]"); + Matcher matcher = linkPattern.matcher(content); + if(matcher.find()) { + String link = matcher.group(1); + HyperlinkLabel hyperlinkLabel = new HyperlinkLabel(content); + hyperlinkLabel.setMaxWidth(Double.MAX_VALUE); + hyperlinkLabel.setMaxHeight(Double.MAX_VALUE); + hyperlinkLabel.getStyleClass().add("content"); + Label label = new Label(); + hyperlinkLabel.setPrefWidth(Math.max(360, TextUtils.computeTextWidth(label.getFont(), link, 0.0D) + 50)); + hyperlinkLabel.setOnAction(event -> { + get().getApplication().getHostServices().showDocument(link); + }); + alert.getDialogPane().setContent(hyperlinkLabel); + } + moveToActiveWindowScreen(alert); return alert.showAndWait(); } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java index 79d7a56e..8bd1b1c9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java @@ -113,7 +113,19 @@ public class QRScanDialog extends Dialog { webcamService.resultProperty().addListener(new QRResultListener()); webcamService.setOnFailed(failedEvent -> { - Platform.runLater(() -> setResult(new Result(failedEvent.getSource().getException()))); + Throwable exception = failedEvent.getSource().getException(); + + Throwable nested = exception; + while(nested.getCause() != null) { + nested = nested.getCause(); + } + if(org.controlsfx.tools.Platform.getCurrent() == org.controlsfx.tools.Platform.WINDOWS && + nested.getMessage().startsWith("Library 'OpenIMAJGrabber' was not loaded successfully from file")) { + exception = new WebcamDependencyException("Your system is missing a dependency required for the webcam. Follow the link below for more details.\n\n[https://sparrowwallet.com/docs/faq.html#your-system-is-missing-a-dependency-for-the-webcam]", exception); + } + + final Throwable result = exception; + Platform.runLater(() -> setResult(new Result(result))); }); webcamService.start(); webcamResolutionProperty.addListener((observable, oldValue, newResolution) -> { @@ -715,6 +727,24 @@ public class QRScanDialog extends Dialog { } } + public static class WebcamDependencyException extends ScanException { + public WebcamDependencyException() { + super(); + } + + public WebcamDependencyException(String message) { + super(message); + } + + public WebcamDependencyException(Throwable cause) { + super(cause); + } + + public WebcamDependencyException(String message, Throwable cause) { + super(message, cause); + } + } + public static class ScanDelayCalculator implements WebcamUpdater.DelayCalculator { public long calculateDelay(long snapshotDuration, double deviceFps) { return Math.max(SCAN_PERIOD_MILLIS - snapshotDuration, 0L); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java index 999bb842..aeb5e8de 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TextAreaDialog.java @@ -12,10 +12,14 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import org.controlsfx.glyphfont.Glyph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Optional; public class TextAreaDialog extends Dialog { + private static final Logger log = LoggerFactory.getLogger(TextAreaDialog.class); + private final TextArea textArea; private final String defaultValue; @@ -99,6 +103,7 @@ public class TextAreaDialog extends Dialog { } else if(result.outputDescriptor != null) { textArea.setText(result.outputDescriptor.toString(true)); } else if(result.exception != null) { + log.error("Error scanning QR", result.exception); AppServices.showErrorDialog("Error scanning QR", result.exception.getMessage()); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java index 5f60e206..477ef264 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java @@ -30,13 +30,19 @@ import javafx.scene.control.*; import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationSupport; import org.controlsfx.validation.Validator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.net.URL; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.*; +import static com.sparrowwallet.sparrow.AppServices.showErrorDialog; + public class PaymentController extends WalletFormController implements Initializable { + private static final Logger log = LoggerFactory.getLogger(PaymentController.class); + private SendController sendController; private ValidationSupport validationSupport; @@ -327,6 +333,9 @@ public class PaymentController extends WalletFormController implements Initializ QRScanDialog.Result result = optionalResult.get(); if(result.uri != null) { updateFromURI(result.uri); + } else if(result.exception != null) { + log.error("Error scanning QR", result.exception); + showErrorDialog("Error scanning QR", result.exception.getMessage()); } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java index 334e54cc..213ce07a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java @@ -289,6 +289,7 @@ public class SettingsController extends WalletFormController implements Initiali } else if(result.payload != null && !result.payload.isEmpty()) { setDescriptorText(result.payload); } else if(result.exception != null) { + log.error("Error scanning QR", result.exception); AppServices.showErrorDialog("Error scanning QR", result.exception.getMessage()); } }