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 issue #9306: Move "Open only one instance of JabRef" preference option to somewhere else #10602

Merged
merged 13 commits into from
Oct 30, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv

### Changed

- We move the location of the 'Open only one instance of JabRef' preference option from "Network" to "General" .[#9306](https://github.com/JabRef/jabref/issues/9306)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed grammar and spaces in dc2b649.

- The two previews in the change resolver dialog now have their scrollbars synchronized [#9576](https://github.com/JabRef/jabref/issues/9576).
- We changed the setting of the keyword separator to accept a single character only. [#177](https://github.com/koppor/jabref/issues/177)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.control.TextField?>
Expand All @@ -13,7 +14,6 @@
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.icon.JabRefIconView?>
<?import javafx.scene.control.Hyperlink?>
<fx:root spacing="10.0" type="VBox"
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jabref.gui.preferences.general.GeneralTab">
Expand Down Expand Up @@ -70,6 +70,13 @@
<CheckBox fx:id="confirmDelete" text="%Show confirmation dialog when deleting entries"/>
<CheckBox fx:id="collectTelemetry" text="%Collect and share telemetry data to help improve JabRef"/>

<Label styleClass="sectionHeader" text="%Single instance" />
<HBox alignment="CENTER_LEFT" spacing="10.0">
<CheckBox fx:id="remoteServer" text="%Enforce single JabRef instance (and allow remote operations) using port" />
<TextField fx:id="remotePort" maxWidth="100.0" HBox.hgrow="ALWAYS" />
<Button fx:id="remoteHelp" prefWidth="20.0" />
</HBox>

<Label styleClass="sectionHeader" text="%Libraries"/>
<GridPane hgap="10.0" vgap="4.0">
<columnConstraints>
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/org/jabref/gui/preferences/general/GeneralTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
import org.jabref.logic.l10n.Language;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.util.FileUpdateMonitor;

import com.airhacks.afterburner.views.ViewLoader;
import com.tobiasdiez.easybind.EasyBind;
import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
import jakarta.inject.Inject;

public class GeneralTab extends AbstractPreferenceTabView<GeneralTabViewModel> implements PreferencesTab {

Expand All @@ -49,6 +52,11 @@ public class GeneralTab extends AbstractPreferenceTabView<GeneralTabViewModel> i
@FXML private Button autosaveLocalLibrariesHelp;
@FXML private CheckBox createBackup;
@FXML private TextField backupDirectory;
@FXML private CheckBox remoteServer;
@FXML private TextField remotePort;
@FXML private Button remoteHelp;
@Inject private FileUpdateMonitor fileUpdateMonitor;
Siedlerchr marked this conversation as resolved.
Show resolved Hide resolved
@Inject private BibEntryTypesManager entryTypesManager;

private final ControlsFxVisualizer validationVisualizer = new ControlsFxVisualizer();

Expand All @@ -74,7 +82,7 @@ public String getTabName() {
}

public void initialize() {
this.viewModel = new GeneralTabViewModel(dialogService, preferencesService);
this.viewModel = new GeneralTabViewModel(dialogService, preferencesService, fileUpdateMonitor, entryTypesManager);

new ViewModelListCellFactory<Language>()
.withText(Language::getDisplayName)
Expand Down Expand Up @@ -127,10 +135,17 @@ public void initialize() {
backupDirectory.textProperty().bindBidirectional(viewModel.backupDirectoryProperty());
backupDirectory.disableProperty().bind(viewModel.createBackupProperty().not());

actionFactory.configureIconButton(StandardActions.HELP, new HelpAction(HelpFile.REMOTE, dialogService, preferencesService.getFilePreferences()), remoteHelp);

Platform.runLater(() -> {
validationVisualizer.initVisualization(viewModel.remotePortValidationStatus(), remotePort);
validationVisualizer.initVisualization(viewModel.fontSizeValidationStatus(), fontSize);
validationVisualizer.initVisualization(viewModel.customPathToThemeValidationStatus(), customThemePath);
});

remoteServer.selectedProperty().bindBidirectional(viewModel.remoteServerProperty());
remotePort.textProperty().bindBidirectional(viewModel.remotePortProperty());
remotePort.disableProperty().bind(remoteServer.selectedProperty().not());
}

@FXML
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
Expand All @@ -19,18 +20,26 @@
import javafx.scene.control.SpinnerValueFactory;

import org.jabref.gui.DialogService;
import org.jabref.gui.Globals;
import org.jabref.gui.desktop.JabRefDesktop;
import org.jabref.gui.preferences.PreferenceTabViewModel;
import org.jabref.gui.remote.CLIMessageHandler;
import org.jabref.gui.theme.Theme;
import org.jabref.gui.theme.ThemeTypes;
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.l10n.Language;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.net.ssl.TrustStoreManager;
import org.jabref.logic.remote.RemotePreferences;
import org.jabref.logic.remote.RemoteUtil;
import org.jabref.logic.util.StandardFileType;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.strings.StringUtil;
import org.jabref.model.util.FileUpdateMonitor;
import org.jabref.preferences.FilePreferences;
import org.jabref.preferences.InternalPreferences;
import org.jabref.preferences.LibraryPreferences;
import org.jabref.preferences.PreferencesService;
import org.jabref.preferences.TelemetryPreferences;
Expand Down Expand Up @@ -81,19 +90,33 @@ public class GeneralTabViewModel implements PreferenceTabViewModel {
private final TelemetryPreferences telemetryPreferences;
private final LibraryPreferences libraryPreferences;
private final FilePreferences filePreferences;
private final RemotePreferences remotePreferences;

private final Validator fontSizeValidator;
private final Validator customPathToThemeValidator;

private final List<String> restartWarning = new ArrayList<>();

public GeneralTabViewModel(DialogService dialogService, PreferencesService preferences) {
private final BooleanProperty remoteServerProperty = new SimpleBooleanProperty();
private final StringProperty remotePortProperty = new SimpleStringProperty("");
private final Validator remotePortValidator;
private final InternalPreferences internalPreferences;
private final BooleanProperty versionCheckProperty = new SimpleBooleanProperty();
private final FileUpdateMonitor fileUpdateMonitor;
private final BibEntryTypesManager entryTypesManager;
private TrustStoreManager trustStoreManager;

public GeneralTabViewModel(DialogService dialogService, PreferencesService preferences, FileUpdateMonitor fileUpdateMonitor, BibEntryTypesManager entryTypesManager) {
Siedlerchr marked this conversation as resolved.
Show resolved Hide resolved
this.dialogService = dialogService;
this.preferences = preferences;
this.workspacePreferences = preferences.getWorkspacePreferences();
this.telemetryPreferences = preferences.getTelemetryPreferences();
this.libraryPreferences = preferences.getLibraryPreferences();
this.filePreferences = preferences.getFilePreferences();
this.remotePreferences = preferences.getRemotePreferences();
this.internalPreferences = preferences.getInternalPreferences();
this.fileUpdateMonitor = fileUpdateMonitor;
this.entryTypesManager = entryTypesManager;
this.trustStoreManager = trustStoreManager;

fontSizeValidator = new FunctionBasedValidator<>(
fontSizeProperty,
Expand All @@ -116,6 +139,25 @@ public GeneralTabViewModel(DialogService dialogService, PreferencesService prefe
Localization.lang("General"),
Localization.lang("Visual theme"),
Localization.lang("Please specify a css theme file."))));

remotePortValidator = new FunctionBasedValidator<>(
remotePortProperty,
input -> {
try {
int portNumber = Integer.parseInt(remotePortProperty().getValue());
return RemoteUtil.isUserPort(portNumber);
} catch (NumberFormatException ex) {
return false;
}
},
ValidationMessage.error(String.format("%s > %s %n %n %s",
Localization.lang("Network"),
Localization.lang("Remote operation"),
Localization.lang("You must enter an integer value in the interval 1025-65535"))));
}

public ValidationStatus remotePortValidationStatus() {
return remotePortValidator.getValidationStatus();
}

@Override
Expand All @@ -125,8 +167,10 @@ public void setValues() {
// The light theme is in fact the absence of any theme modifying 'base.css'. Another embedded theme like
// 'dark.css', stored in the classpath, can be introduced in {@link org.jabref.gui.theme.Theme}.
switch (workspacePreferences.getTheme().getType()) {
case DEFAULT -> selectedThemeProperty.setValue(ThemeTypes.LIGHT);
case EMBEDDED -> selectedThemeProperty.setValue(ThemeTypes.DARK);
case DEFAULT ->
selectedThemeProperty.setValue(ThemeTypes.LIGHT);
case EMBEDDED ->
selectedThemeProperty.setValue(ThemeTypes.DARK);
case CUSTOM -> {
selectedThemeProperty.setValue(ThemeTypes.CUSTOM);
customPathToThemeProperty.setValue(workspacePreferences.getTheme().getName());
Expand All @@ -151,6 +195,9 @@ public void setValues() {

createBackupProperty.setValue(filePreferences.shouldCreateBackup());
backupDirectoryProperty.setValue(filePreferences.getBackupDirectory().toString());

remoteServerProperty.setValue(remotePreferences.useRemoteServer());
remotePortProperty.setValue(String.valueOf(remotePreferences.getPort()));
}

@Override
Expand Down Expand Up @@ -185,6 +232,38 @@ public void storeSettings() {

filePreferences.createBackupProperty().setValue(createBackupProperty.getValue());
filePreferences.backupDirectoryProperty().setValue(Path.of(backupDirectoryProperty.getValue()));

getPortAsInt(remotePortProperty.getValue()).ifPresent(newPort -> {
if (remotePreferences.isDifferentPort(newPort)) {
remotePreferences.setPort(newPort);
}
});

internalPreferences.setVersionCheckEnabled(versionCheckProperty.getValue());

getPortAsInt(remotePortProperty.getValue()).ifPresent(newPort -> {
if (remotePreferences.isDifferentPort(newPort)) {
remotePreferences.setPort(newPort);
}
});

if (remoteServerProperty.getValue()) {
remotePreferences.setUseRemoteServer(true);
Globals.REMOTE_LISTENER.openAndStart(new CLIMessageHandler(preferences, fileUpdateMonitor, entryTypesManager), remotePreferences.getPort());
} else {
remotePreferences.setUseRemoteServer(false);
Globals.REMOTE_LISTENER.stop();
}
trustStoreManager.flush();

if (remoteServerProperty.getValue()) {
remotePreferences.setUseRemoteServer(true);
Globals.REMOTE_LISTENER.openAndStart(new CLIMessageHandler(preferences, fileUpdateMonitor, entryTypesManager), remotePreferences.getPort());
} else {
remotePreferences.setUseRemoteServer(false);
Globals.REMOTE_LISTENER.stop();
}
trustStoreManager.flush();
}

public ValidationStatus fontSizeValidationStatus() {
Expand All @@ -199,6 +278,10 @@ public ValidationStatus customPathToThemeValidationStatus() {
public boolean validateSettings() {
CompositeValidator validator = new CompositeValidator();

if (remoteServerProperty.getValue()) {
validator.addValidators(remotePortValidator);
}

if (fontOverrideProperty.getValue()) {
validator.addValidators(fontSizeValidator);
}
Expand Down Expand Up @@ -310,6 +393,14 @@ public void backupFileDirBrowse() {
.ifPresent(dir -> backupDirectoryProperty.setValue(dir.toString()));
}

public BooleanProperty remoteServerProperty() {
return remoteServerProperty;
}

public StringProperty remotePortProperty() {
return remotePortProperty;
}

public void openBrowser() {
String url = "https://themes.jabref.org";
try {
Expand All @@ -318,4 +409,12 @@ public void openBrowser() {
dialogService.showErrorDialogAndWait(Localization.lang("Could not open website."), e);
}
}

private Optional<Integer> getPortAsInt(String value) {
try {
return Optional.of(Integer.parseInt(value));
} catch (NumberFormatException ex) {
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.icon.JabRefIconView?>
Expand All @@ -19,13 +18,6 @@
<Label styleClass="titleHeader" text="%Network" />
<CheckBox fx:id="versionCheck" text="%Check for latest version online"/>
<Label text="%If you encounter an issue or a bug, please check the latest version, whether the issue is still present." wrapText="true"/>
<Label styleClass="sectionHeader" text="%Remote operation" />
<Label fx:id="remoteLabel" text="%This feature lets new files be opened or imported into an already running instance of JabRef instead of opening a new instance. For instance, this is useful when you open a file in JabRef from your web browser. Note that this will prevent you from running more than one instance of JabRef at a time." textOverrun="CLIP" wrapText="true" />
<HBox alignment="CENTER_LEFT" spacing="10.0">
<CheckBox fx:id="remoteServer" text="%Listen for remote operation on port" />
<TextField fx:id="remotePort" maxWidth="100.0" HBox.hgrow="ALWAYS" />
<Button fx:id="remoteHelp" prefWidth="20.0" />
</HBox>

<Label styleClass="sectionHeader" text="%Proxy configuration" />
<GridPane hgap="10.0" vgap="10.0">
Expand Down
27 changes: 1 addition & 26 deletions src/main/java/org/jabref/gui/preferences/network/NetworkTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,20 @@
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseEvent;

import org.jabref.gui.Globals;
import org.jabref.gui.actions.ActionFactory;
import org.jabref.gui.actions.StandardActions;
import org.jabref.gui.help.HelpAction;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.preferences.AbstractPreferenceTabView;
import org.jabref.gui.preferences.PreferencesTab;
import org.jabref.gui.util.IconValidationDecorator;
import org.jabref.gui.util.ValueTableCellFactory;
import org.jabref.logic.help.HelpFile;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.util.FileUpdateMonitor;

import com.airhacks.afterburner.views.ViewLoader;
import com.tobiasdiez.easybind.EasyBind;
import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
import jakarta.inject.Inject;
import org.controlsfx.control.textfield.CustomPasswordField;

public class NetworkTab extends AbstractPreferenceTabView<NetworkTabViewModel> implements PreferencesTab {
@FXML private Label remoteLabel;
@FXML private CheckBox versionCheck;
@FXML private CheckBox remoteServer;
@FXML private TextField remotePort;
@FXML private Button remoteHelp;

@FXML private CheckBox proxyUse;
@FXML private Label proxyHostnameLabel;
@FXML private TextField proxyHostname;
Expand All @@ -67,9 +54,6 @@ public class NetworkTab extends AbstractPreferenceTabView<NetworkTabViewModel> i
@FXML private TableColumn<CustomCertificateViewModel, String> certVersion;
@FXML private TableColumn<CustomCertificateViewModel, String> actionsColumn;

@Inject private FileUpdateMonitor fileUpdateMonitor;
@Inject private BibEntryTypesManager entryTypesManager;
DiamondMyK marked this conversation as resolved.
Show resolved Hide resolved

private String proxyPasswordText = "";
private int proxyPasswordCaretPosition = 0;

Expand All @@ -87,15 +71,10 @@ public String getTabName() {
}

public void initialize() {
this.viewModel = new NetworkTabViewModel(dialogService, preferencesService, fileUpdateMonitor, entryTypesManager);
this.viewModel = new NetworkTabViewModel(dialogService, preferencesService);

versionCheck.selectedProperty().bindBidirectional(viewModel.versionCheckProperty());

remoteLabel.setVisible(preferencesService.getWorkspacePreferences().shouldShowAdvancedHints());
remoteServer.selectedProperty().bindBidirectional(viewModel.remoteServerProperty());
remotePort.textProperty().bindBidirectional(viewModel.remotePortProperty());
remotePort.disableProperty().bind(remoteServer.selectedProperty().not());

proxyUse.selectedProperty().bindBidirectional(viewModel.proxyUseProperty());
proxyHostnameLabel.disableProperty().bind(proxyUse.selectedProperty().not());
proxyHostname.textProperty().bindBidirectional(viewModel.proxyHostnameProperty());
Expand Down Expand Up @@ -129,12 +108,8 @@ public void initialize() {
proxyPassword.getRight().addEventFilter(MouseEvent.MOUSE_RELEASED, this::proxyPasswordMask);
proxyPassword.getRight().addEventFilter(MouseEvent.MOUSE_EXITED, this::proxyPasswordMask);

ActionFactory actionFactory = new ActionFactory(Globals.getKeyPrefs());
actionFactory.configureIconButton(StandardActions.HELP, new HelpAction(HelpFile.REMOTE, dialogService, preferencesService.getFilePreferences()), remoteHelp);

validationVisualizer.setDecoration(new IconValidationDecorator());
Platform.runLater(() -> {
validationVisualizer.initVisualization(viewModel.remotePortValidationStatus(), remotePort);
validationVisualizer.initVisualization(viewModel.proxyHostnameValidationStatus(), proxyHostname);
validationVisualizer.initVisualization(viewModel.proxyPortValidationStatus(), proxyPort);
validationVisualizer.initVisualization(viewModel.proxyUsernameValidationStatus(), proxyUsername);
Expand Down
Loading
Loading