diff --git a/com.io7m.cardant_gui.ui/src/main/images/images.xcf b/com.io7m.cardant_gui.ui/src/main/images/images.xcf index a058afc..1cfcaf3 100644 Binary files a/com.io7m.cardant_gui.ui/src/main/images/images.xcf and b/com.io7m.cardant_gui.ui/src/main/images/images.xcf differ diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/CAGApplication.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/CAGApplication.java index 0f2a30c..3e145ca 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/CAGApplication.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/CAGApplication.java @@ -18,7 +18,6 @@ package com.io7m.cardant_gui.ui; import com.io7m.cardant.client.preferences.api.CAPreferencesServiceType; -import com.io7m.cardant_gui.ui.internal.CAGAttachmentAddDialogs; import com.io7m.cardant_gui.ui.internal.CAGCSS; import com.io7m.cardant_gui.ui.internal.CAGClientService; import com.io7m.cardant_gui.ui.internal.CAGClientServiceType; @@ -27,6 +26,8 @@ import com.io7m.cardant_gui.ui.internal.CAGFileChoosers; import com.io7m.cardant_gui.ui.internal.CAGFileChoosersType; import com.io7m.cardant_gui.ui.internal.CAGFileViewDialogs; +import com.io7m.cardant_gui.ui.internal.CAGItemAttachmentAddDialogs; +import com.io7m.cardant_gui.ui.internal.CAGLocationAttachmentAddDialogs; import com.io7m.cardant_gui.ui.internal.CAGMainAuditSearchView; import com.io7m.cardant_gui.ui.internal.CAGMainAuditTableView; import com.io7m.cardant_gui.ui.internal.CAGMainFileListView; @@ -34,6 +35,9 @@ import com.io7m.cardant_gui.ui.internal.CAGMainItemDetailsView; import com.io7m.cardant_gui.ui.internal.CAGMainItemSearchView; import com.io7m.cardant_gui.ui.internal.CAGMainItemTableView; +import com.io7m.cardant_gui.ui.internal.CAGMainLocationDetailsView; +import com.io7m.cardant_gui.ui.internal.CAGMainLocationSearchView; +import com.io7m.cardant_gui.ui.internal.CAGMainLocationTableView; import com.io7m.cardant_gui.ui.internal.CAGMainTypePackageDetailsView; import com.io7m.cardant_gui.ui.internal.CAGMainTypePackageSearchView; import com.io7m.cardant_gui.ui.internal.CAGMainTypePackageTableView; @@ -100,8 +104,13 @@ public void start( new CAGFileViewDialogs(services)); services.register( - CAGAttachmentAddDialogs.class, - new CAGAttachmentAddDialogs(services) + CAGItemAttachmentAddDialogs.class, + new CAGItemAttachmentAddDialogs(services) + ); + + services.register( + CAGLocationAttachmentAddDialogs.class, + new CAGLocationAttachmentAddDialogs(services) ); services.register(CAPreferencesServiceType.class, this.preferences); @@ -169,6 +178,18 @@ public void start( Map.entry( CAGMainTypePackageTableView.class, () -> new CAGMainTypePackageTableView(services) + ), + Map.entry( + CAGMainLocationDetailsView.class, + () -> new CAGMainLocationDetailsView(services) + ), + Map.entry( + CAGMainLocationSearchView.class, + () -> new CAGMainLocationSearchView(services) + ), + Map.entry( + CAGMainLocationTableView.class, + () -> new CAGMainLocationTableView(services) ) ); diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAuditEventTypeMatchConverter.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAuditEventTypeMatchConverter.java index fd124fb..19e71ab 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAuditEventTypeMatchConverter.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAuditEventTypeMatchConverter.java @@ -21,9 +21,9 @@ import java.util.Objects; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_AUDITSEARCH_TYPE_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_AUDITSEARCH_TYPE_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_AUDITSEARCH_TYPE_NOTEQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_NOTEQUALTO; /** * A string converter. @@ -56,13 +56,13 @@ public String toString( return switch (k) { case ANY -> { - yield this.strings.format(CARDANT_AUDITSEARCH_TYPE_ANY); + yield this.strings.format(CARDANT_EXACT_ANY); } case EQUAL_TO -> { - yield this.strings.format(CARDANT_AUDITSEARCH_TYPE_EQUALTO); + yield this.strings.format(CARDANT_EXACT_EQUALTO); } case NOT_EQUAL_TO -> { - yield this.strings.format(CARDANT_AUDITSEARCH_TYPE_NOTEQUALTO); + yield this.strings.format(CARDANT_EXACT_NOTEQUALTO); } }; } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGController.java index 5753b9e..4af5116 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGController.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGController.java @@ -27,6 +27,8 @@ import com.io7m.cardant.model.CAItemID; import com.io7m.cardant.model.CAItemSearchParameters; import com.io7m.cardant.model.CAItemSummary; +import com.io7m.cardant.model.CALocationID; +import com.io7m.cardant.model.CALocationSummary; import com.io7m.cardant.model.CAMetadataType; import com.io7m.cardant.model.type_package.CATypePackageIdentifier; import com.io7m.cardant.model.type_package.CATypePackageSearchParameters; @@ -36,6 +38,9 @@ import com.io7m.cardant.protocol.inventory.CAICommandItemAttachmentAdd; import com.io7m.cardant.protocol.inventory.CAICommandItemGet; import com.io7m.cardant.protocol.inventory.CAICommandItemSearchBegin; +import com.io7m.cardant.protocol.inventory.CAICommandLocationAttachmentAdd; +import com.io7m.cardant.protocol.inventory.CAICommandLocationGet; +import com.io7m.cardant.protocol.inventory.CAICommandLocationList; import com.io7m.cardant.protocol.inventory.CAICommandTypePackageGetText; import com.io7m.cardant.protocol.inventory.CAICommandTypePackageInstall; import com.io7m.cardant.protocol.inventory.CAICommandTypePackageSearchBegin; @@ -46,6 +51,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.SortedList; +import javafx.scene.control.TreeItem; import javafx.scene.image.Image; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,7 +70,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -80,25 +88,37 @@ public final class CAGController implements CAGControllerType private static final Logger LOG = LoggerFactory.getLogger(CAGController.class); - private final ObservableList items; - private final SortedList itemsSorted; - private final ObservableList itemsRead; - private final SimpleObjectProperty itemPages; + private static final CALocationID ROOT_LOCATION = + CALocationID.of("00000000-0000-0000-0000-000000000000"); + + private static final CALocationSummary ROOT_LOCATION_SUMMARY = + new CALocationSummary(ROOT_LOCATION, Optional.empty(), "Everywhere"); + private final CAGClientServiceType clientService; - private final SimpleObjectProperty itemSelected; - private final ObservableList itemSelectedMeta; - private final SortedList itemSelectedMetaSorted; private final ObservableList itemSelectedAttachments; + private final ObservableList auditEvents; private final ObservableList files; + private final ObservableList items; + private final ObservableList itemsRead; + private final ObservableList itemSelectedMeta; + private final ObservableList typePackages; + private final SimpleObjectProperty auditEventPages; private final SimpleObjectProperty filePages; + private final SimpleObjectProperty itemPages; + private final SimpleObjectProperty typePackagePages; private final SimpleObjectProperty transferStatus; - private final SimpleObjectProperty auditEventPages; - private final ObservableList auditEvents; + private final SimpleObjectProperty itemSelected; + private final SimpleStringProperty typePackageTextSelected; private final SortedList auditEventsSorted; - private final SimpleObjectProperty typePackagePages; - private final ObservableList typePackages; + private final SortedList itemsSorted; + private final SortedList itemSelectedMetaSorted; private final SortedList typePackagesSorted; - private final SimpleStringProperty typePackageTextSelected; + private final ObservableList locationSelectedMeta; + private final SortedList locationSelectedMetaSorted; + private final ObservableList locationSelectedAttachments; + private final SimpleObjectProperty locationPages; + private final SimpleObjectProperty locationSelected; + private final SimpleObjectProperty> locationTree; private CAGController( final CAGClientServiceType inClientService) @@ -122,7 +142,6 @@ private CAGController( FXCollections.observableArrayList(); this.itemSelectedMetaSorted = new SortedList<>(this.itemSelectedMeta); - this.itemSelectedAttachments = FXCollections.observableArrayList(); @@ -150,6 +169,21 @@ private CAGController( this.typePackageTextSelected = new SimpleStringProperty(); + + this.locationSelectedMeta = + FXCollections.observableArrayList(); + this.locationSelectedMetaSorted = + new SortedList<>(this.locationSelectedMeta); + this.locationSelectedAttachments = + FXCollections.observableArrayList(); + + this.locationPages = + new SimpleObjectProperty<>(new CAGPageRange(0L, 0L)); + this.locationSelected = + new SimpleObjectProperty<>(); + + this.locationTree = + new SimpleObjectProperty<>(); } /** @@ -596,4 +630,148 @@ public String toString() Integer.valueOf(this.hashCode()) ); } + + @Override + public ObservableValue> locationTree() + { + return this.locationTree; + } + + @Override + public void locationSearchBegin() + { + final var future = + this.clientService.execute(new CAICommandLocationList()); + + future.thenAccept(response -> { + Platform.runLater(() -> { + final var data = + response.data(); + final var summaries = + data.locations(); + + LOG.debug("Received {} locations", summaries.size()); + + final var treeItems = + new HashMap>(summaries.size()); + final var newRoot = + new TreeItem<>(ROOT_LOCATION_SUMMARY); + + for (final var location : summaries.values()) { + final var item = new TreeItem<>(location); + treeItems.put(location.id(), item); + } + + for (final var location : summaries.values()) { + final var locationItem = + treeItems.get(location.id()); + final var parent = + location.parent(); + + if (parent.isEmpty()) { + newRoot.getChildren().add(locationItem); + continue; + } + + final var parentId = + parent.get(); + final var parentItem = + treeItems.get(parentId); + + if (parentItem == null) { + LOG.warn("Location {} provided a nonexistent parent {}", location.id(), parentId); + continue; + } + + parentItem.getChildren().add(locationItem); + } + + this.locationTree.set(newRoot); + }); + }); + } + + @Override + public void locationGet( + final CALocationID id) + { + if (Objects.equals(id, ROOT_LOCATION)) { + this.locationSelectNothing(); + return; + } + + final var future = + this.clientService.execute(new CAICommandLocationGet(id)); + + future.thenAccept(response -> { + Platform.runLater(() -> { + final var location = response.data(); + + this.locationSelected.set(location.summary()); + + this.locationSelectedMeta.setAll( + location.metadata() + .values() + .stream() + .toList() + ); + + this.locationSelectedAttachments.setAll( + location.attachments() + .values() + .stream() + .sorted(Comparator.comparing(o -> o.key().fileID())) + .collect(Collectors.toList()) + ); + }); + }); + } + + @Override + public SortedList locationSelectedMetadata() + { + return this.locationSelectedMetaSorted; + } + + @Override + public void locationSelectNothing() + { + this.locationSelected.set(null); + } + + @Override + public ObservableList locationSelectedAttachments() + { + return this.locationSelectedAttachments; + } + + @Override + public void locationAttachmentAdd( + final CAICommandLocationAttachmentAdd command) + { + + } + + @Override + public ObservableValue locationPages() + { + return this.locationPages; + } + + @Override + public ObservableValue locationSelected() + { + return this.locationSelected; + } + + @Override + public void locationRemove() + { + final var location = this.locationSelected.get(); + if (location == null) { + return; + } + + throw new IllegalStateException("Unimplemented code!"); + } } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerAuditType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerAuditType.java new file mode 100644 index 0000000..9c13edc --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerAuditType.java @@ -0,0 +1,60 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CAAuditEvent; +import com.io7m.cardant.model.CAAuditSearchParameters; +import com.io7m.repetoir.core.RPServiceType; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +/** + * Audit methods for the controller. + */ + +public interface CAGControllerAuditType + extends RPServiceType +{ + /** + * Start searching for audit records. + * + * @param searchParameters The search parameters + */ + + void auditSearchBegin( + CAAuditSearchParameters searchParameters); + + /** + * @return The audit records for the current search query + */ + + ObservableList auditEventsView(); + + /** + * @return The audit records for the current search query + */ + + SortedList auditEventsViewSorted(); + + /** + * @return The page range for the current audit event search query + */ + + ObservableValue auditEventsPages(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFilesType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFilesType.java new file mode 100644 index 0000000..a199327 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFilesType.java @@ -0,0 +1,91 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CAFileID; +import com.io7m.cardant.model.CAFileSearchParameters; +import com.io7m.cardant.model.CAFileType; +import com.io7m.repetoir.core.RPServiceType; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; + +import java.nio.file.Path; + +/** + * File methods for the controller. + */ + +public interface CAGControllerFilesType + extends RPServiceType +{ + /** + * Start searching for files. + * + * @param searchParameters The parameters + */ + + void fileSearchBegin( + CAFileSearchParameters searchParameters); + + /** + * @return The files for the current search query + */ + + ObservableList filesView(); + + /** + * Upload a file. + * + * @param fileID The file ID + * @param file The file + * @param contentType The content type + * @param description The description + */ + + void fileUpload( + CAFileID fileID, + Path file, + String contentType, + String description + ); + + /** + * Download a file. + * + * @param fileID The file ID + * @param file The output file + * @param fileTmp The output temporary file + * @param size The expected size + * @param hashAlgorithm The hash algorithm + * @param hashValue The expected hash value + */ + + void fileDownload( + CAFileID fileID, + Path file, + Path fileTmp, + long size, + String hashAlgorithm, + String hashValue); + + /** + * @return The page range for the current file search query + */ + + ObservableValue filePages(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerItemsType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerItemsType.java new file mode 100644 index 0000000..5574bbe --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerItemsType.java @@ -0,0 +1,105 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CAAttachment; +import com.io7m.cardant.model.CAItemID; +import com.io7m.cardant.model.CAItemSearchParameters; +import com.io7m.cardant.model.CAItemSummary; +import com.io7m.cardant.model.CAMetadataType; +import com.io7m.cardant.protocol.inventory.CAICommandItemAttachmentAdd; +import com.io7m.repetoir.core.RPServiceType; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +/** + * Item methods for the controller. + */ + +public interface CAGControllerItemsType + extends RPServiceType +{ + /** + * @return The items for the current search query + */ + + ObservableList itemsView(); + + /** + * @return The items for the current search query + */ + + SortedList itemsViewSorted(); + + /** + * Start searching for items. + * + * @param searchParameters The search parameters + */ + + void itemSearchBegin( + CAItemSearchParameters searchParameters); + + /** + * Fetch an item. + * + * @param id The item ID + */ + + void itemGet(CAItemID id); + + /** + * @return The metadata for the selected item + */ + + SortedList itemSelectedMetadata(); + + /** + * Clear the current item selection. + */ + + void itemSelectNothing(); + + /** + * @return The attachments for the selected item + */ + + ObservableList itemSelectedAttachments(); + + /** + * Add an attachment. + * + * @param command The command + */ + + void itemAttachmentAdd( + CAICommandItemAttachmentAdd command); + + /** + * @return The page range for the current item search query + */ + + ObservableValue itemPages(); + + /** + * @return The currently selected item + */ + + ObservableValue itemSelected(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerLocationsType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerLocationsType.java new file mode 100644 index 0000000..12126e9 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerLocationsType.java @@ -0,0 +1,102 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CAAttachment; +import com.io7m.cardant.model.CALocationID; +import com.io7m.cardant.model.CALocationSummary; +import com.io7m.cardant.model.CAMetadataType; +import com.io7m.cardant.protocol.inventory.CAICommandLocationAttachmentAdd; +import com.io7m.repetoir.core.RPServiceType; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; +import javafx.scene.control.TreeItem; + +/** + * Location methods for the controller. + */ + +public interface CAGControllerLocationsType + extends RPServiceType +{ + /** + * @return The location tree + */ + + ObservableValue> locationTree(); + + /** + * Start searching for locations. + */ + + void locationSearchBegin(); + + /** + * Fetch an location. + * + * @param id The location ID + */ + + void locationGet(CALocationID id); + + /** + * @return The metadata for the selected location + */ + + SortedList locationSelectedMetadata(); + + /** + * Clear the current location selection. + */ + + void locationSelectNothing(); + + /** + * @return The attachments for the selected location + */ + + ObservableList locationSelectedAttachments(); + + /** + * Add an attachment. + * + * @param command The command + */ + + void locationAttachmentAdd( + CAICommandLocationAttachmentAdd command); + + /** + * @return The page range for the current location search query + */ + + ObservableValue locationPages(); + + /** + * @return The currently selected location + */ + + ObservableValue locationSelected(); + + /** + * Remove the selected location. + */ + + void locationRemove(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerType.java index 659b816..b582a72 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerType.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerType.java @@ -17,24 +17,8 @@ package com.io7m.cardant_gui.ui.internal; -import com.io7m.cardant.model.CAAttachment; -import com.io7m.cardant.model.CAAuditEvent; -import com.io7m.cardant.model.CAAuditSearchParameters; import com.io7m.cardant.model.CAFileID; -import com.io7m.cardant.model.CAFileSearchParameters; -import com.io7m.cardant.model.CAFileType; -import com.io7m.cardant.model.CAItemID; -import com.io7m.cardant.model.CAItemSearchParameters; -import com.io7m.cardant.model.CAItemSummary; -import com.io7m.cardant.model.CAMetadataType; -import com.io7m.cardant.model.type_package.CATypePackageIdentifier; -import com.io7m.cardant.model.type_package.CATypePackageSearchParameters; -import com.io7m.cardant.model.type_package.CATypePackageSummary; -import com.io7m.cardant.protocol.inventory.CAICommandItemAttachmentAdd; -import com.io7m.repetoir.core.RPServiceType; import javafx.beans.value.ObservableValue; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; import javafx.scene.image.Image; import java.nio.file.Path; @@ -45,117 +29,18 @@ */ public interface CAGControllerType - extends RPServiceType + extends CAGControllerAuditType, + CAGControllerFilesType, + CAGControllerItemsType, + CAGControllerLocationsType, + CAGControllerTypePackagesType { - /** - * @return The items for the current search query - */ - - ObservableList itemsView(); - - /** - * @return The items for the current search query - */ - - SortedList itemsViewSorted(); - - /** - * Start searching for items. - * - * @param searchParameters The search parameters - */ - - void itemSearchBegin( - CAItemSearchParameters searchParameters); - - /** - * Fetch an item. - * - * @param id The item ID - */ - - void itemGet(CAItemID id); - - /** - * @return The metadata for the selected item - */ - - SortedList itemSelectedMetadata(); - - /** - * Clear the current item selection. - */ - - void itemSelectNothing(); - - /** - * @return The attachments for the selected item - */ - - ObservableList itemSelectedAttachments(); - - /** - * Start searching for files. - * - * @param searchParameters The parameters - */ - - void fileSearchBegin( - CAFileSearchParameters searchParameters); - - /** - * @return The files for the current search query - */ - - ObservableList filesView(); - /** * @return The current transfer status */ ObservableValue transferStatus(); - /** - * Upload a file. - * - * @param fileID The file ID - * @param file The file - * @param contentType The content type - * @param description The description - */ - - void fileUpload( - CAFileID fileID, - Path file, - String contentType, - String description - ); - - /** - * Download a file. - * - * @param fileID The file ID - * @param file The output file - * @param fileTmp The output temporary file - * @param size The expected size - * @param hashAlgorithm The hash algorithm - * @param hashValue The expected hash value - */ - - void fileDownload( - CAFileID fileID, - Path file, - Path fileTmp, - long size, - String hashAlgorithm, - String hashValue); - - /** - * @return The page range for the current file search query - */ - - ObservableValue filePages(); - /** * Get an image. * @@ -180,121 +65,4 @@ CompletableFuture imageGet( String hashValue, int width, int height); - - /** - * Add an attachment. - * - * @param command The command - */ - - void itemAttachmentAdd( - CAICommandItemAttachmentAdd command); - - /** - * Start searching for audit records. - * - * @param searchParameters The search parameters - */ - - void auditSearchBegin( - CAAuditSearchParameters searchParameters); - - /** - * @return The audit records for the current search query - */ - - ObservableList auditEventsView(); - - /** - * @return The audit records for the current search query - */ - - SortedList auditEventsViewSorted(); - - /** - * @return The page range for the current audit event search query - */ - - ObservableValue auditEventsPages(); - - /** - * Start searching for type packages. - * - * @param searchParameters The search parameters - */ - - void typePackageSearchBegin( - CATypePackageSearchParameters searchParameters); - - /** - * @return The type packages for the current search query - */ - - ObservableList typePackagesView(); - - /** - * @return The type packages for the current search query - */ - - SortedList typePackagesViewSorted(); - - /** - * @return The page range for the current type package search query - */ - - ObservableValue typePackagesPages(); - - /** - * Install a type package from the given file. - * - * @param file The file - */ - - void typePackageInstall(Path file); - - /** - * A page range. - * - * @param pageIndex The page index (indexed from 1) - * @param pageCount The page count - */ - - record CAGPageRange( - long pageIndex, - long pageCount) - { - - } - - /** - * @return The page range for the current item search query - */ - - ObservableValue itemPages(); - - /** - * @return The currently selected item - */ - - ObservableValue itemSelected(); - - /** - * Fetch a type package. - * - * @param id The type package ID - */ - - void typePackageGet(CATypePackageIdentifier id); - - /** - * @return The text of the currently selected type package - */ - - ObservableValue typePackageTextSelected(); - - /** - * Clear the current type package selection. - */ - - void typePackageSelectNothing(); } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerTypePackagesType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerTypePackagesType.java new file mode 100644 index 0000000..4e3ff05 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerTypePackagesType.java @@ -0,0 +1,91 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.type_package.CATypePackageIdentifier; +import com.io7m.cardant.model.type_package.CATypePackageSearchParameters; +import com.io7m.cardant.model.type_package.CATypePackageSummary; +import com.io7m.repetoir.core.RPServiceType; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import java.nio.file.Path; + +/** + * Type package methods for the controller. + */ + +public interface CAGControllerTypePackagesType + extends RPServiceType +{ + /** + * Start searching for type packages. + * + * @param searchParameters The search parameters + */ + + void typePackageSearchBegin( + CATypePackageSearchParameters searchParameters); + + /** + * @return The type packages for the current search query + */ + + ObservableList typePackagesView(); + + /** + * @return The type packages for the current search query + */ + + SortedList typePackagesViewSorted(); + + /** + * @return The page range for the current type package search query + */ + + ObservableValue typePackagesPages(); + + /** + * Install a type package from the given file. + * + * @param file The file + */ + + void typePackageInstall(Path file); + + /** + * Fetch a type package. + * + * @param id The type package ID + */ + + void typePackageGet(CATypePackageIdentifier id); + + /** + * @return The text of the currently selected type package + */ + + ObservableValue typePackageTextSelected(); + + /** + * Clear the current type package selection. + */ + + void typePackageSelectNothing(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDescriptionMatchConverter.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDescriptionMatchConverter.java index c70a009..2e64397 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDescriptionMatchConverter.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDescriptionMatchConverter.java @@ -21,11 +21,11 @@ import java.util.Objects; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_DESCRIPTION_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_DESCRIPTION_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_DESCRIPTION_NOTEQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_DESCRIPTION_NOTSIMILARTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_DESCRIPTION_SIMILARTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_NOTEQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_NOTSIMILARTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_SIMILARTO; /** * A string converter. @@ -58,19 +58,19 @@ public String toString( return switch (k) { case ANY -> { - yield this.strings.format(CARDANT_FILESEARCH_DESCRIPTION_ANY); + yield this.strings.format(CARDANT_FUZZY_ANY); } case EQUAL_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_DESCRIPTION_EQUALTO); + yield this.strings.format(CARDANT_FUZZY_EQUALTO); } case NOT_EQUAL_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_DESCRIPTION_NOTEQUALTO); + yield this.strings.format(CARDANT_FUZZY_NOTEQUALTO); } case SIMILAR_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_DESCRIPTION_SIMILARTO); + yield this.strings.format(CARDANT_FUZZY_SIMILARTO); } case NOT_SIMILAR_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_DESCRIPTION_NOTSIMILARTO); + yield this.strings.format(CARDANT_FUZZY_NOTSIMILARTO); } }; } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAttachmentAddDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogs.java similarity index 79% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAttachmentAddDialogs.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogs.java index 6d6cdb6..f6bef54 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAttachmentAddDialogs.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogs.java @@ -25,8 +25,8 @@ * An attachment addition dialog. */ -public final class CAGAttachmentAddDialogs - extends CAGDialogFactoryAbstract +public final class CAGItemAttachmentAddDialogs + extends CAGDialogFactoryAbstract { /** * An attachment addition dialog. @@ -34,12 +34,12 @@ public final class CAGAttachmentAddDialogs * @param services The service directory */ - public CAGAttachmentAddDialogs( + public CAGItemAttachmentAddDialogs( final RPServiceDirectoryType services) { super( - CAGAttachmentAddView.class, - "/com/io7m/cardant_gui/ui/internal/attachmentAdd.fxml", + CAGItemAttachmentAddView.class, + "/com/io7m/cardant_gui/ui/internal/itemAttachmentAdd.fxml", services, Modality.NONE ); @@ -53,11 +53,11 @@ protected String createStageTitle( } @Override - protected CAGAttachmentAddView createController( + protected CAGItemAttachmentAddView createController( final Void arguments, final Stage stage) { - return new CAGAttachmentAddView(stage, this.services()); + return new CAGItemAttachmentAddView(stage, this.services()); } @Override @@ -70,7 +70,7 @@ public String description() public String toString() { return String.format( - "[CAGAttachmentAddDialogs 0x%08x]", + "[CAGItemAttachmentAddDialogs 0x%08x]", Integer.valueOf(this.hashCode()) ); } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAttachmentAddView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddView.java similarity index 98% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAttachmentAddView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddView.java index 0c3c87d..1498b92 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGAttachmentAddView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddView.java @@ -41,7 +41,7 @@ * The attachment addition view. */ -public final class CAGAttachmentAddView +public final class CAGItemAttachmentAddView implements CAGViewType { private final Stage stage; @@ -62,7 +62,7 @@ public final class CAGAttachmentAddView * @param services The services */ - public CAGAttachmentAddView( + public CAGItemAttachmentAddView( final Stage inStage, final RPServiceDirectoryType services) { diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemNameMatchConverter.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemNameMatchConverter.java index b6eda4f..3f70c95 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemNameMatchConverter.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemNameMatchConverter.java @@ -21,11 +21,11 @@ import java.util.Objects; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_NAME_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_NAME_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_NAME_NOTEQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_NAME_NOTSIMILARTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_NAME_SIMILARTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_NOTEQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_NOTSIMILARTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_SIMILARTO; /** * A string converter. @@ -58,19 +58,19 @@ public String toString( return switch (k) { case ANY -> { - yield this.strings.format(CARDANT_ITEMSEARCH_NAME_ANY); + yield this.strings.format(CARDANT_FUZZY_ANY); } case EQUAL_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_NAME_EQUALTO); + yield this.strings.format(CARDANT_FUZZY_EQUALTO); } case NOT_EQUAL_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_NAME_NOTEQUALTO); + yield this.strings.format(CARDANT_FUZZY_NOTEQUALTO); } case SIMILAR_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_NAME_SIMILARTO); + yield this.strings.format(CARDANT_FUZZY_SIMILARTO); } case NOT_SIMILAR_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_NAME_NOTSIMILARTO); + yield this.strings.format(CARDANT_FUZZY_NOTSIMILARTO); } }; } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSerialMatchConverter.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSerialMatchConverter.java index b691ffe..5d6db6f 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSerialMatchConverter.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSerialMatchConverter.java @@ -21,9 +21,9 @@ import java.util.Objects; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_SERIAL_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_SERIAL_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_SERIAL_NOTEQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_NOTEQUALTO; /** * A string converter. @@ -56,13 +56,13 @@ public String toString( return switch (k) { case ANY -> { - yield this.strings.format(CARDANT_ITEMSEARCH_SERIAL_ANY); + yield this.strings.format(CARDANT_EXACT_ANY); } case EQUAL_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_SERIAL_EQUALTO); + yield this.strings.format(CARDANT_EXACT_EQUALTO); } case NOT_EQUAL_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_SERIAL_NOTEQUALTO); + yield this.strings.format(CARDANT_EXACT_NOTEQUALTO); } }; } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemTypeMatchConverter.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemTypeMatchConverter.java index 54d127d..371739a 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemTypeMatchConverter.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemTypeMatchConverter.java @@ -21,12 +21,12 @@ import java.util.Objects; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_TYPES_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_TYPES_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_TYPES_NOTEQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_TYPES_OVERLAPPING; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_TYPES_SUBSETOF; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_TYPES_SUPERSETOF; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SETCOMPARISON_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SETCOMPARISON_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SETCOMPARISON_NOTEQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SETCOMPARISON_OVERLAPPING; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SETCOMPARISON_SUBSETOF; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SETCOMPARISON_SUPERSETOF; /** * A string converter. @@ -59,22 +59,22 @@ public String toString( return switch (k) { case ANY -> { - yield this.strings.format(CARDANT_ITEMSEARCH_TYPES_ANY); + yield this.strings.format(CARDANT_SETCOMPARISON_ANY); } case EQUAL_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_TYPES_EQUALTO); + yield this.strings.format(CARDANT_SETCOMPARISON_EQUALTO); } case NOT_EQUAL_TO -> { - yield this.strings.format(CARDANT_ITEMSEARCH_TYPES_NOTEQUALTO); + yield this.strings.format(CARDANT_SETCOMPARISON_NOTEQUALTO); } case SUPERSET_OF -> { - yield this.strings.format(CARDANT_ITEMSEARCH_TYPES_SUPERSETOF); + yield this.strings.format(CARDANT_SETCOMPARISON_SUPERSETOF); } case SUBSET_OF -> { - yield this.strings.format(CARDANT_ITEMSEARCH_TYPES_SUBSETOF); + yield this.strings.format(CARDANT_SETCOMPARISON_SUBSETOF); } case OVERLAPPING -> { - yield this.strings.format(CARDANT_ITEMSEARCH_TYPES_OVERLAPPING); + yield this.strings.format(CARDANT_SETCOMPARISON_OVERLAPPING); } }; } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddDialogs.java new file mode 100644 index 0000000..cc965eb --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddDialogs.java @@ -0,0 +1,77 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.repetoir.core.RPServiceDirectoryType; +import javafx.stage.Modality; +import javafx.stage.Stage; + +/** + * An attachment addition dialog. + */ + +public final class CAGLocationAttachmentAddDialogs + extends CAGDialogFactoryAbstract +{ + /** + * An attachment addition dialog. + * + * @param services The service directory + */ + + public CAGLocationAttachmentAddDialogs( + final RPServiceDirectoryType services) + { + super( + CAGLocationAttachmentAddView.class, + "/com/io7m/cardant_gui/ui/internal/locationAttachmentAdd.fxml", + services, + Modality.NONE + ); + } + + @Override + protected String createStageTitle( + final Void arguments) + { + return this.strings().format(CAGStringConstants.CARDANT_ATTACHMENTADD_TITLE); + } + + @Override + protected CAGLocationAttachmentAddView createController( + final Void arguments, + final Stage stage) + { + return new CAGLocationAttachmentAddView(stage, this.services()); + } + + @Override + public String description() + { + return "Attachment add dialogs."; + } + + @Override + public String toString() + { + return String.format( + "[CAGLocationAttachmentAddDialogs 0x%08x]", + Integer.valueOf(this.hashCode()) + ); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddView.java new file mode 100644 index 0000000..28eaf5f --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddView.java @@ -0,0 +1,217 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CAFileID; +import com.io7m.cardant.model.CAFileType; +import com.io7m.cardant.model.CALocationID; +import com.io7m.cardant.model.CALocationSummary; +import com.io7m.cardant.protocol.inventory.CAICommandLocationAttachmentAdd; +import com.io7m.repetoir.core.RPServiceDirectoryType; +import javafx.collections.ListChangeListener; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ListView; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.TextField; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.UUID; + +/** + * The attachment addition view. + */ + +public final class CAGLocationAttachmentAddView + implements CAGViewType +{ + private final Stage stage; + private final CAGStringsType strings; + private final CAGControllerType controller; + + @FXML private Button addButton; + @FXML private Label resultsLabel; + @FXML private TextField locationField; + @FXML private TextField fileField; + @FXML private TextField relationField; + @FXML private ListView files; + + /** + * The attachment addition view. + * + * @param inStage The stage + * @param services The services + */ + + public CAGLocationAttachmentAddView( + final Stage inStage, + final RPServiceDirectoryType services) + { + this.stage = + Objects.requireNonNull(inStage, "stage"); + this.strings = + services.requireService(CAGStringsType.class); + this.controller = + services.requireService(CAGControllerType.class); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.addButton.setDisable(true); + + this.files.setCellFactory( + new CAGFileCellFactory(this.strings)); + this.files.setItems( + this.controller.filesView()); + this.files.getSelectionModel() + .setSelectionMode(SelectionMode.SINGLE); + this.files.getSelectionModel() + .getSelectedItems() + .addListener((ListChangeListener) this::onFileSelectionChanged); + + this.controller.filesView() + .addListener(this::onFilesViewChanged); + + this.controller.locationSelected() + .addListener((observable, oldValue, newValue) -> { + this.onLocationSelectionChanged(newValue); + }); + + this.locationField.textProperty() + .addListener(observable -> { + this.validate(); + }); + + this.fileField.textProperty() + .addListener(observable -> { + this.validate(); + }); + + this.relationField.textProperty() + .addListener(observable -> { + this.validate(); + }); + } + + private void validate() + { + this.addButton.setDisable(true); + + try { + this.createCommand(); + this.addButton.setDisable(false); + } catch (final Exception e) { + // Ignored + this.addButton.setDisable(true); + } + } + + private CAICommandLocationAttachmentAdd createCommand() + { + final var relationText = + this.relationField.getText().trim(); + final var locationIdText = + this.locationField.getText().trim(); + final var fileIdText = + this.fileField.getText().trim(); + + if (relationText.isEmpty()) { + throw new IllegalArgumentException(); + } + + return new CAICommandLocationAttachmentAdd( + new CALocationID(UUID.fromString(locationIdText)), + new CAFileID(UUID.fromString(fileIdText)), + relationText + ); + } + + private void onLocationSelectionChanged( + final CALocationSummary location) + { + if (location == null) { + this.locationField.setText(""); + return; + } + + this.locationField.setText(location.id().displayId()); + } + + private void onFilesViewChanged( + final ListChangeListener.Change c) + { + final var size = c.getList().size(); + if (size > 0) { + final var range = + this.controller.filePages().getValue(); + + this.resultsLabel.setText( + this.strings.format( + CAGStringConstants.CARDANT_FILESEARCH_PAGEOF, + Long.valueOf(range.pageIndex()), + Long.valueOf(range.pageCount()) + ) + ); + } else { + this.resultsLabel.setText(""); + } + } + + private void onFileSelectionChanged( + final ListChangeListener.Change c) + { + final var selected = c.getList(); + if (selected.isEmpty()) { + return; + } + + this.fileField.setText(selected.get(0).id().id().toString()); + } + + @FXML + private void onAddSelected() + { + this.controller.locationAttachmentAdd(this.createCommand()); + this.stage.close(); + } + + @FXML + private void onCancelSelected() + { + this.stage.close(); + } + + @FXML + private void onPageNextSelected() + { + + } + + @FXML + private void onPagePreviousSelected() + { + + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCell.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCell.java new file mode 100644 index 0000000..aed0f09 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCell.java @@ -0,0 +1,87 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CALocationSummary; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.TreeCell; + +import java.util.Objects; + +/** + * A tree cell. + */ + +public final class CAGLocationCell + extends TreeCell +{ + private final CAGStringsType strings; + private final Parent root; + private final CAGLocationCellController controller; + + /** + * Construct a cell. + * + * @param inStrings The string resources + * @throws Exception On errors + */ + + public CAGLocationCell( + final CAGStringsType inStrings) + throws Exception + { + this.strings = + Objects.requireNonNull(inStrings, "strings"); + + final FXMLLoader loader = + new FXMLLoader( + CAGLocationCell.class.getResource( + "/com/io7m/cardant_gui/ui/internal/locationCell.fxml") + ); + + loader.setResources(this.strings.resources()); + loader.setControllerFactory(param -> { + return new CAGLocationCellController(); + }); + this.root = loader.load(); + this.controller = loader.getController(); + } + + @Override + protected void updateItem( + final CALocationSummary item, + final boolean empty) + { + super.updateItem(item, empty); + + this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + + if (empty || item == null) { + this.setGraphic(null); + this.setText(null); + this.controller.unsetItem(); + return; + } + + this.controller.setItem(item); + this.setGraphic(this.root); + this.setText(null); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCellController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCellController.java new file mode 100644 index 0000000..eb12be1 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCellController.java @@ -0,0 +1,64 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CALocationSummary; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.control.Label; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * A tree cell controller. + */ + +public final class CAGLocationCellController implements Initializable +{ + @FXML private Parent root; + @FXML private Label name; + + /** + * Construct a cell controller. + */ + + public CAGLocationCellController() + { + } + + void unsetItem() + { + this.name.setText(""); + } + + void setItem( + final CALocationSummary item) + { + this.name.setText(item.name()); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCellFactory.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCellFactory.java new file mode 100644 index 0000000..cfa0dac --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationCellFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CALocationSummary; +import javafx.scene.control.TreeCell; +import javafx.scene.control.TreeView; +import javafx.util.Callback; + +import java.util.Objects; + +/** + * A cell factory. + */ + +public final class CAGLocationCellFactory + implements Callback, TreeCell> +{ + private final CAGStringsType strings; + + /** + * A cell factory. + * + * @param inStrings The string resources + */ + + public CAGLocationCellFactory( + final CAGStringsType inStrings) + { + this.strings = + Objects.requireNonNull(inStrings, "strings"); + } + + @Override + public TreeCell call( + final TreeView param) + { + try { + return new CAGLocationCell(this.strings); + } catch (final Exception e) { + throw new IllegalStateException(e); + } + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemDetailsView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemDetailsView.java index 96223b9..da88eb1 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemDetailsView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemDetailsView.java @@ -57,8 +57,8 @@ public final class CAGMainItemDetailsView private final CAGControllerType controller; private final CAGStringsType strings; - private final CAGAttachmentAddDialogs attachmentAddDialogs; - private CAGViewAndStage attachmentAddDialog; + private final CAGItemAttachmentAddDialogs attachmentAddDialogs; + private CAGViewAndStage attachmentAddDialog; @FXML private TabPane mainItemDetails; @FXML private TextField idField; @@ -86,7 +86,7 @@ public CAGMainItemDetailsView( this.strings = services.requireService(CAGStringsType.class); this.attachmentAddDialogs = - services.requireService(CAGAttachmentAddDialogs.class); + services.requireService(CAGItemAttachmentAddDialogs.class); } @Override diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationDetailsView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationDetailsView.java new file mode 100644 index 0000000..3b562a5 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationDetailsView.java @@ -0,0 +1,291 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CAAttachment; +import com.io7m.cardant.model.CAAttachmentRelations; +import com.io7m.cardant.model.CALocationSummary; +import com.io7m.cardant.model.CAMetadataType; +import com.io7m.lanark.core.RDottedName; +import com.io7m.repetoir.core.RPServiceDirectoryType; +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.collections.ListChangeListener; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.TabPane; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.image.ImageView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * The main location details view. + */ + +public final class CAGMainLocationDetailsView + implements CAGViewType +{ + private static final Logger LOG = + LoggerFactory.getLogger(CAGMainLocationDetailsView.class); + + private final CAGControllerType controller; + private final CAGStringsType strings; + private final CAGLocationAttachmentAddDialogs attachmentAddDialogs; + private CAGViewAndStage attachmentAddDialog; + + @FXML private TabPane mainItemDetails; + @FXML private TextField idField; + @FXML private TextField nameField; + @FXML private ImageView thumbnail; + @FXML private ProgressBar thumbnailLoading; + @FXML private TableView meta; + @FXML private ListView attachments; + @FXML private Button metaAdd; + @FXML private Button metaRemove; + @FXML private Button attachmentAdd; + @FXML private Button attachmentRemove; + + /** + * The main location details view. + * + * @param services The service directory + */ + + public CAGMainLocationDetailsView( + final RPServiceDirectoryType services) + { + this.controller = + services.requireService(CAGControllerType.class); + this.strings = + services.requireService(CAGStringsType.class); + this.attachmentAddDialogs = + services.requireService(CAGLocationAttachmentAddDialogs.class); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.mainItemDetails.setDisable(true); + + this.thumbnail.setVisible(false); + this.thumbnailLoading.setVisible(false); + + final var tableColumns = + this.meta.getColumns(); + + final var tablePkgColumn = + (TableColumn) tableColumns.get(0); + final var tableTypeColumn = + (TableColumn) tableColumns.get(1); + final var tableFieldColumn = + (TableColumn) tableColumns.get(2); + final var tableValueColumn = + (TableColumn) tableColumns.get(3); + + tablePkgColumn.setSortable(true); + tablePkgColumn.setReorderable(false); + tablePkgColumn.setCellValueFactory( + param -> { + return new SimpleObjectProperty<>( + param.getValue().name().typeName().packageName() + ); + }); + + tableTypeColumn.setSortable(true); + tableTypeColumn.setReorderable(false); + tableTypeColumn.setCellValueFactory( + param -> { + return new SimpleObjectProperty<>( + param.getValue().name().typeName().typeName() + ); + }); + + tableFieldColumn.setSortable(true); + tableFieldColumn.setReorderable(false); + tableFieldColumn.setCellValueFactory( + param -> { + return new SimpleObjectProperty<>( + param.getValue().name().fieldName() + ); + }); + + tableValueColumn.setSortable(true); + tableValueColumn.setReorderable(false); + tableValueColumn.setCellValueFactory( + param -> { + return new SimpleStringProperty( + param.getValue().valueString() + ); + }); + + this.controller.locationSelectedMetadata() + .comparatorProperty() + .bind(this.meta.comparatorProperty()); + + this.meta.setItems(this.controller.locationSelectedMetadata()); + + this.attachments.setCellFactory( + new CAGItemAttachmentCellFactory(this.strings) + ); + this.attachments.setItems( + this.controller.locationSelectedAttachments() + ); + + this.controller.locationSelected() + .addListener((observable, oldValue, newValue) -> { + this.locationSelectionChanged(newValue); + }); + + this.controller.locationSelectedAttachments() + .addListener((ListChangeListener) c -> { + this.loadThumbnail(); + }); + } + + private void locationSelectionChanged( + final CALocationSummary newValue) + { + if (newValue == null) { + this.mainItemDetails.setDisable(true); + this.idField.setText(""); + this.nameField.setText(""); + this.clearThumbnail(); + return; + } + + this.mainItemDetails.setDisable(false); + this.idField.setText(newValue.id().toString()); + this.nameField.setText(newValue.name()); + this.clearThumbnail(); + } + + private void clearThumbnail() + { + this.thumbnailLoading.setVisible(false); + this.thumbnail.setVisible(false); + this.thumbnail.setImage(null); + } + + private void loadThumbnail() + { + this.attachments.getItems() + .stream() + .filter(a -> Objects.equals(a.relation(), CAAttachmentRelations.image())) + .findFirst() + .ifPresent(this::loadThumbnailFromAttachment); + } + + private void loadThumbnailFromAttachment( + final CAAttachment attachment) + { + LOG.debug("Loading thumbnail: {}", attachment.file().id()); + + Platform.runLater(() -> { + this.thumbnailLoading.setVisible(true); + this.thumbnail.setVisible(false); + }); + + try { + final var attachmentFile = + attachment.file(); + final var fileID = + attachmentFile.id(); + final var file = + Files.createTempFile("cardant-gui", ".jpg"); + final var fileTmp = + Files.createTempFile("cardant-gui", ".jpg"); + final var size = + attachmentFile.size(); + final var hashAlgo = + attachmentFile.hashAlgorithm(); + final var hashValue = + attachmentFile.hashValue(); + + this.controller.imageGet( + fileID, + file, + fileTmp, + size, + hashAlgo, + hashValue, + (int) this.thumbnail.getFitWidth(), + (int) this.thumbnail.getFitHeight()) + .thenAccept(image -> { + Platform.runLater(() -> { + this.thumbnailLoading.setVisible(false); + this.thumbnail.setImage(image); + this.thumbnail.setVisible(true); + }); + }); + } catch (final IOException e) { + LOG.debug("Loading thumbnail: ", e); + Platform.runLater(this::clearThumbnail); + } + } + + @FXML + private void onMetaAddSelected() + { + + } + + @FXML + private void onMetaRemoveSelected() + { + + } + + @FXML + private void onAttachmentAddSelected() + throws IOException + { + if (this.attachmentAddDialog == null) { + this.attachmentAddDialog = + this.attachmentAddDialogs.createDialog(null); + } + + this.attachmentAddDialog.stage() + .show(); + } + + @FXML + private void onAttachmentRemoveSelected() + { + + } + + @FXML + private void onItemNameSetSelected() + { + + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationSearchView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationSearchView.java new file mode 100644 index 0000000..286ba94 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationSearchView.java @@ -0,0 +1,342 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CAMetadataElementMatchType; +import com.io7m.cardant.model.CATypeRecordIdentifier; +import com.io7m.cardant.model.comparisons.CAComparisonFuzzyType; +import com.io7m.cardant.model.comparisons.CAComparisonSetType; +import com.io7m.cardant.parsers.CAMetadataMatchExpressions; +import com.io7m.cardant.strings.CAStrings; +import com.io7m.jsx.prettyprint.JSXPrettyPrinterCodeStyle; +import com.io7m.repetoir.core.RPServiceDirectoryType; +import com.io7m.seltzer.api.SStructuredErrorType; +import javafx.collections.FXCollections; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.control.TreeView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.StringWriter; +import java.net.URL; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_CANCEL; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SEARCH_CLEAR; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SEARCH_CONFIRMCLEAR; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_SEARCH_CONFIRMCLEARTITLE; +import static javafx.scene.control.Alert.AlertType.CONFIRMATION; +import static javafx.scene.control.ButtonBar.ButtonData.CANCEL_CLOSE; +import static javafx.scene.control.ButtonBar.ButtonData.OK_DONE; + +/** + * The main location search view. + */ + +public final class CAGMainLocationSearchView + implements CAGViewType +{ + private static final Logger LOG = + LoggerFactory.getLogger(CAGMainLocationSearchView.class); + + private final CAGStringsType strings; + private final CAMetadataMatchExpressions expressions; + private final CAGControllerType controller; + + @FXML private ChoiceBox locationNameMatch; + @FXML private ChoiceBox locationTypeMatch; + @FXML private Button locationTypeAdd; + @FXML private Button locationTypeRemove; + @FXML private TextField locationName; + @FXML private TreeView locationMetadataMatch; + @FXML private TextArea locationMetadataCompiled; + @FXML private ListView locationTypes; + + private CAGMetaMatchTree metaTree; + + /** + * The main location search view. + * + * @param services The service directory + */ + + public CAGMainLocationSearchView( + final RPServiceDirectoryType services) + { + this.strings = + services.requireService(CAGStringsType.class); + this.expressions = + new CAMetadataMatchExpressions(CAStrings.create(Locale.getDefault())); + this.controller = + services.requireService(CAGControllerType.class); + } + + private void clearParameters() + { + this.locationNameMatch.getSelectionModel() + .select(CAGItemNameMatchKind.ANY); + this.locationName.setText(""); + + this.locationTypeMatch.getSelectionModel() + .select(CAGItemTypeMatchKind.ANY); + this.locationTypes.getItems() + .clear(); + + this.metaTree.clear(); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.locationTypeRemove.setDisable(true); + this.locationTypeAdd.setDisable(true); + this.locationName.setDisable(true); + + this.locationTypeMatch.setItems( + FXCollections.observableArrayList(CAGItemTypeMatchKind.values())); + this.locationTypeMatch.setConverter( + new CAGItemTypeMatchConverter(this.strings)); + this.locationTypeMatch.getSelectionModel() + .select(CAGItemTypeMatchKind.ANY); + this.locationTypeMatch.getSelectionModel() + .selectedItemProperty() + .addListener((observable, oldValue, newValue) -> { + this.onTypeMatchChanged(newValue); + }); + + this.locationNameMatch.setItems( + FXCollections.observableArrayList(CAGItemNameMatchKind.values())); + this.locationNameMatch.setConverter( + new CAGItemNameMatchConverter(this.strings)); + this.locationNameMatch.getSelectionModel() + .select(CAGItemNameMatchKind.ANY); + this.locationNameMatch.getSelectionModel() + .selectedItemProperty() + .addListener((observable, oldValue, newValue) -> { + this.onNameMatchChanged(newValue); + }); + + this.metaTree = + new CAGMetaMatchTree(this.strings, this.locationMetadataMatch); + + this.metaTree.sequence().addListener( + (observable, oldValue, newValue) -> { + this.recompileMetadataMatch(); + }); + } + + private void onTypeMatchChanged( + final CAGItemTypeMatchKind k) + { + switch (k) { + case ANY -> { + this.locationTypeRemove.setDisable(true); + this.locationTypeAdd.setDisable(true); + } + case EQUAL_TO, NOT_EQUAL_TO, SUPERSET_OF, SUBSET_OF, OVERLAPPING -> { + this.locationTypeRemove.setDisable(false); + this.locationTypeAdd.setDisable(false); + } + } + } + + private void onNameMatchChanged( + final CAGItemNameMatchKind k) + { + switch (k) { + case ANY -> { + this.locationName.setDisable(true); + } + case EQUAL_TO, NOT_EQUAL_TO, SIMILAR_TO, NOT_SIMILAR_TO -> { + this.locationName.setDisable(false); + } + } + } + + private void recompileMetadataMatch() + { + try { + final var expression = + this.metaTree.compile(); + final var sexpr = + this.expressions.metadataMatchSerialize(expression); + + try (var writer = new StringWriter()) { + final var pretty = + JSXPrettyPrinterCodeStyle.newPrinterWithWidthIndent( + writer, + 60, + 2 + ); + + pretty.print(sexpr); + writer.flush(); + this.locationMetadataCompiled.setText(writer.toString()); + } + } catch (final Exception e) { + final var text = new StringBuilder(); + text.append(e.getMessage()); + text.append("\n"); + + final ArrayList> entries; + if (e instanceof final SStructuredErrorType s) { + entries = new ArrayList<>(s.attributes().entrySet()); + } else { + entries = new ArrayList<>(); + } + entries.sort(Map.Entry.comparingByKey()); + + for (final var entry : entries) { + text.append(" "); + text.append(entry.getKey()); + text.append(": "); + text.append(entry.getValue()); + text.append("\n"); + } + + this.locationMetadataCompiled.setText(text.toString()); + LOG.error("Compile expression: {}: ", text, e); + } + } + + @FXML + private void onLocationTypeAdd() + { + + } + + @FXML + private void onLocationTypeRemove() + { + + } + + @FXML + private void onSearchSelected() + { + + } + + @FXML + private void onSearchClearSelected() + { + final var confirmMessage = + this.strings.format(CARDANT_SEARCH_CONFIRMCLEAR); + final var clearButtonMessage = + this.strings.format(CARDANT_SEARCH_CLEAR); + + final var confirm = + new ButtonType(clearButtonMessage, OK_DONE); + final var cancel = + new ButtonType(this.strings.format(CARDANT_CANCEL), CANCEL_CLOSE); + + final var dialog = + new Alert(CONFIRMATION, confirmMessage); + + CAGCSS.setCSS(dialog.getDialogPane()); + + dialog.setHeaderText( + this.strings.format(CARDANT_SEARCH_CONFIRMCLEARTITLE)); + + final var dialogButtons = + dialog.getButtonTypes(); + + dialogButtons.clear(); + dialogButtons.add(cancel); + dialogButtons.add(confirm); + + final var r = dialog.showAndWait(); + if (r.isEmpty()) { + return; + } + + if (r.get().equals(confirm)) { + this.clearParameters(); + } + } + + private CAMetadataElementMatchType metadataMatch() + { + return this.metaTree.compile(); + } + + private CAComparisonSetType typeMatch() + { + return switch (this.locationTypeMatch.getValue()) { + case ANY -> { + yield new CAComparisonSetType.Anything<>(); + } + case EQUAL_TO -> { + throw new IllegalStateException(); + } + case NOT_EQUAL_TO -> { + throw new IllegalStateException(); + } + case SUPERSET_OF -> { + throw new IllegalStateException(); + } + case SUBSET_OF -> { + throw new IllegalStateException(); + } + case OVERLAPPING -> { + throw new IllegalStateException(); + } + }; + } + + private CAComparisonFuzzyType nameMatch() + { + return switch (this.locationNameMatch.getValue()) { + case ANY -> { + yield new CAComparisonFuzzyType.Anything<>(); + } + case EQUAL_TO -> { + yield new CAComparisonFuzzyType.IsEqualTo<>( + this.locationName.getText().trim() + ); + } + case NOT_EQUAL_TO -> { + yield new CAComparisonFuzzyType.IsNotEqualTo<>( + this.locationName.getText().trim() + ); + } + case SIMILAR_TO -> { + yield new CAComparisonFuzzyType.IsSimilarTo<>( + this.locationName.getText().trim() + ); + } + case NOT_SIMILAR_TO -> { + yield new CAComparisonFuzzyType.IsNotSimilarTo<>( + this.locationName.getText().trim() + ); + } + }; + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationTableView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationTableView.java new file mode 100644 index 0000000..e84ad97 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationTableView.java @@ -0,0 +1,182 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +import com.io7m.cardant.model.CALocationSummary; +import com.io7m.repetoir.core.RPServiceDirectoryType; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_CANCEL; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_LOCATIONS_REMOVECONFIRM; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_LOCATIONS_REMOVECONFIRMTITLE; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_REMOVE; +import static javafx.scene.control.Alert.AlertType.CONFIRMATION; +import static javafx.scene.control.ButtonBar.ButtonData.CANCEL_CLOSE; +import static javafx.scene.control.ButtonBar.ButtonData.OK_DONE; + +/** + * The table of locations. + */ + +public final class CAGMainLocationTableView + implements CAGViewType +{ + private final CAGControllerType controller; + private final CAGStringsType strings; + private final CAGClientServiceType clients; + + @FXML private TreeView mainLocationTree; + @FXML private Button locationAdd; + @FXML private Button locationRemove; + + /** + * The table of locations. + * + * @param inServices The service directory + */ + + public CAGMainLocationTableView( + final RPServiceDirectoryType inServices) + { + Objects.requireNonNull(inServices, "services"); + + this.controller = + inServices.requireService(CAGControllerType.class); + this.strings = + inServices.requireService(CAGStringsType.class); + this.clients = + inServices.requireService(CAGClientServiceType.class); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.locationRemove.setDisable(true); + + this.mainLocationTree.setCellFactory( + new CAGLocationCellFactory(this.strings) + ); + + this.clients.status() + .subscribe((oldStatus, newStatus) -> { + Platform.runLater(() -> { + this.onClientStatusChanged(oldStatus, newStatus); + }); + }); + + this.mainLocationTree.setRoot( + this.controller.locationTree() + .getValue() + ); + + this.controller.locationTree() + .addListener((observable, oldValue, newValue) -> { + this.mainLocationTree.setRoot(newValue); + }); + + this.mainLocationTree.getSelectionModel() + .selectedItemProperty() + .addListener((observable, oldValue, newValue) -> { + this.onLocationSelectionChanged(newValue); + }); + } + + private void onLocationSelectionChanged( + final TreeItem newValue) + { + this.locationRemove.setDisable(true); + + if (newValue == null) { + this.controller.locationSelectNothing(); + return; + } + + this.locationRemove.setDisable(false); + this.controller.locationGet(newValue.getValue().id()); + } + + private void onClientStatusChanged( + final CAGClientStatus oldStatus, + final CAGClientStatus newStatus) + { + switch (newStatus) { + case NOT_CONNECTED, CONNECTING -> { + // Nothing + } + case CONNECTED -> { + this.controller.locationSearchBegin(); + } + } + } + + @FXML + private void onLocationAddSelected() + { + + } + + @FXML + private void onLocationRemoveSelected() + { + final var confirmMessage = + this.strings.format(CARDANT_LOCATIONS_REMOVECONFIRM); + final var clearButtonMessage = + this.strings.format(CARDANT_REMOVE); + + final var confirm = + new ButtonType(clearButtonMessage, OK_DONE); + final var cancel = + new ButtonType(this.strings.format(CARDANT_CANCEL), CANCEL_CLOSE); + + final var dialog = + new Alert(CONFIRMATION, confirmMessage); + + CAGCSS.setCSS(dialog.getDialogPane()); + + dialog.setHeaderText( + this.strings.format(CARDANT_LOCATIONS_REMOVECONFIRMTITLE)); + + final var dialogButtons = + dialog.getButtonTypes(); + + dialogButtons.clear(); + dialogButtons.add(cancel); + dialogButtons.add(confirm); + + final var r = dialog.showAndWait(); + if (r.isEmpty()) { + return; + } + + if (r.get().equals(confirm)) { + this.controller.locationRemove(); + } + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMediaTypeMatchConverter.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMediaTypeMatchConverter.java index b1dd1eb..d8c7c6a 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMediaTypeMatchConverter.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMediaTypeMatchConverter.java @@ -21,11 +21,11 @@ import java.util.Objects; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_MEDIATYPE_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_MEDIATYPE_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_MEDIATYPE_NOTEQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_MEDIATYPE_NOTSIMILARTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_MEDIATYPE_SIMILARTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_NOTEQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_NOTSIMILARTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FUZZY_SIMILARTO; /** * A string converter. @@ -58,19 +58,19 @@ public String toString( return switch (k) { case ANY -> { - yield this.strings.format(CARDANT_FILESEARCH_MEDIATYPE_ANY); + yield this.strings.format(CARDANT_FUZZY_ANY); } case EQUAL_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_MEDIATYPE_EQUALTO); + yield this.strings.format(CARDANT_FUZZY_EQUALTO); } case NOT_EQUAL_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_MEDIATYPE_NOTEQUALTO); + yield this.strings.format(CARDANT_FUZZY_NOTEQUALTO); } case SIMILAR_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_MEDIATYPE_SIMILARTO); + yield this.strings.format(CARDANT_FUZZY_SIMILARTO); } case NOT_SIMILAR_TO -> { - yield this.strings.format(CARDANT_FILESEARCH_MEDIATYPE_NOTSIMILARTO); + yield this.strings.format(CARDANT_FUZZY_NOTSIMILARTO); } }; } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchCellController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchCellController.java index 88a9f0f..422f669 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchCellController.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchCellController.java @@ -62,11 +62,11 @@ import java.util.Objects; import java.util.ResourceBundle; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_NOTEQUALTO; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_AND; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_COMPARISON_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_COMPARISON_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_COMPARISON_NOTEQUALTO; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_OR; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_SPECIFIC; @@ -292,11 +292,11 @@ private void setItemStringComparison( this.stringComparisonFieldOp.setText( switch (c) { case final StringAnything a -> - s.format(CARDANT_ITEMSEARCH_METADATA_COMPARISON_ANY); + s.format(CARDANT_EXACT_ANY); case final StringEqualTo e -> - s.format(CARDANT_ITEMSEARCH_METADATA_COMPARISON_EQUALTO); + s.format(CARDANT_EXACT_EQUALTO); case final StringNotEqualTo n -> - s.format(CARDANT_ITEMSEARCH_METADATA_COMPARISON_NOTEQUALTO); + s.format(CARDANT_EXACT_NOTEQUALTO); } ); diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchTree.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchTree.java index f41ff36..6e8d7d5 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchTree.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMetaMatchTree.java @@ -72,12 +72,12 @@ import static com.io7m.cardant_gui.ui.internal.CAGMetaMatchNodeType.Element.ANYTHING; import static com.io7m.cardant_gui.ui.internal.CAGMetaMatchNodeType.Element.MATCH; import static com.io7m.cardant_gui.ui.internal.CAGMetaMatchNodeType.Element.OR; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_ANY; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_EQUALTO; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_EXACT_NOTEQUALTO; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_AND; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_ANY; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_CHANGETYPE; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_COMPARISON_ANY; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_COMPARISON_EQUALTO; -import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_COMPARISON_NOTEQUALTO; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_OR; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_SPECIFIC; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMSEARCH_METADATA_VALUE_ANY; @@ -278,19 +278,19 @@ private Menu makeMetadataComparisonMenu() items.add( this.menuItem( - CARDANT_ITEMSEARCH_METADATA_COMPARISON_EQUALTO, + CARDANT_EXACT_EQUALTO, this::onMetadataChangeComparisonToEqualTo ) ); items.add( this.menuItem( - CARDANT_ITEMSEARCH_METADATA_COMPARISON_NOTEQUALTO, + CARDANT_EXACT_NOTEQUALTO, this::onMetadataChangeComparisonToNotEqualTo ) ); items.add( this.menuItem( - CARDANT_ITEMSEARCH_METADATA_COMPARISON_ANY, + CARDANT_EXACT_ANY, this::onMetadataChangeComparisonToAny ) ); diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGPageRange.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGPageRange.java new file mode 100644 index 0000000..741d2c3 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGPageRange.java @@ -0,0 +1,32 @@ +/* + * Copyright © 2024 Mark Raynsford https://www.io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +package com.io7m.cardant_gui.ui.internal; + +/** + * A page range. + * + * @param pageIndex The page index (indexed from 1) + * @param pageCount The page count + */ + +public record CAGPageRange( + long pageIndex, + long pageCount) +{ + +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGViewType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGViewType.java index 14dfbcd..25c8348 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGViewType.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGViewType.java @@ -24,8 +24,9 @@ */ public sealed interface CAGViewType - extends Initializable permits CAGAttachmentAddView, - CAGFileCreateView, + extends Initializable permits CAGFileCreateView, + CAGItemAttachmentAddView, + CAGLocationAttachmentAddView, CAGLoginView, CAGMainAuditSearchView, CAGMainAuditTableView, @@ -34,6 +35,9 @@ public sealed interface CAGViewType CAGMainItemDetailsView, CAGMainItemSearchView, CAGMainItemTableView, + CAGMainLocationDetailsView, + CAGMainLocationSearchView, + CAGMainLocationTableView, CAGMainTypePackageDetailsView, CAGMainTypePackageSearchView, CAGMainTypePackageTableView, diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/Strings.properties b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/Strings.properties index b31641d..f4ac67c 100644 --- a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/Strings.properties +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/Strings.properties @@ -14,206 +14,146 @@ # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -cardant.title=Cardant -cardant.file.connect=Connect... -cardant.file.exit=Exit -cardant.cancel=Cancel -cardant.login.bookmark.createMain=Please enter a name for the bookmark. -cardant.login.bookmark.createTitle=Create Bookmark -cardant.login.bookmarks=Bookmarks -cardant.login.connect=Connect -cardant.login.host=Host -cardant.login.https=HTTPS -cardant.login.password=Password -cardant.login.port=Port -cardant.login.title=Connect to server... -cardant.login.tooltip.createBookmark=Create a new bookmark. -cardant.login.tooltip.deleteBookmark=Delete the selected bookmark. -cardant.login.tooltip.fieldNotValid=This field is not valid! -cardant.login.tooltip.host=The host name to which to cardant.login. -cardant.login.tooltip.https=Check this box if HTTPS should be used when connecting to the server. -cardant.login.tooltip.password=The password that will be used when connecting. -cardant.login.tooltip.port=The port to which to login. This will be automatically inferred if left empty. -cardant.login.tooltip.user=The username that will be used when connecting. -cardant.login.username=Username -cardant.login=Login -cardant.items=Items -cardant.audit=Audit -cardant.files=Files -cardant.typePackages=Type Packages -cardant.locations=Locations -cardant.ui.boot=Cardant initialized. -cardant.login.connecting=Connecting to {0}... -cardant.login.connected=Connected to {0}. - -cardant.tooltip.searchNext=Go to the next page of search results. -cardant.tooltip.searchPrevious=Go to the previous page of search results. - -cardant.search.clear=Clear -cardant.search.confirmClearTitle=Confirm -cardant.search.confirmClear=Are you sure you want to clear the search parameters? - -cardant.attachmentAdd.title=Add attachment -cardant.attachmentAdd.file=File cardant.attachmentAdd.add=Add +cardant.attachmentAdd.file=File cardant.attachmentAdd.item=Item cardant.attachmentAdd.relation=Relation - -cardant.itemDetails.attributes=Attributes -cardant.itemDetails.attachments=Attachments -cardant.itemDetails.metadata=Metadata - -cardant.itemDetails.name=Name -cardant.itemDetails.id=ID - -cardant.itemDetails.tooltip.metaAdd=Add a new metadata item. -cardant.itemDetails.tooltip.metaRemove=Remove the selected metadata items. - -cardant.itemDetails.tooltip.attachmentAdd=Add a new attachment. -cardant.itemDetails.tooltip.attachmentRemove=Remove the selected attachments. - -cardant.itemDetails.meta.package=Package -cardant.itemDetails.meta.type=Type -cardant.itemDetails.meta.field=Field -cardant.itemDetails.meta.value=Value - +cardant.attachmentAdd.title=Add attachment +cardant.attachments=Attachments +cardant.attributes=Attributes +cardant.audit=Audit +cardant.auditSearch.data=Data +cardant.auditSearch.id=ID +cardant.auditSearch.key=Key +cardant.auditSearch.owner=Owner +cardant.auditSearch.pageOf=Page {0} of {1} +cardant.auditSearch.time=Time +cardant.auditSearch.timeRange.lower=Lower bound... +cardant.auditSearch.timeRange.upper=Upper bound... +cardant.auditSearch.timeRange=Time Range +cardant.auditSearch.type=Type +cardant.auditSearch.value=Value +cardant.cancel=Cancel +cardant.description=Description +cardant.exact.any=Is anything. +cardant.exact.equalTo=Is equal to: +cardant.exact.notEqualTo=Is not equal to: +cardant.field=Field +cardant.file.connect=Connect... +cardant.file.exit=Exit +cardant.fileCreate.description=Description +cardant.fileCreate.openFile=Open a file... +cardant.fileCreate.select=Select... +cardant.fileCreate.upload=Upload +cardant.fileSearch.pageOf=Page {0} of {1} +cardant.fileSearch.sizeRange=Size Range +cardant.files.transfer.downloading=Downloading... +cardant.files.transfer.idle=No transfers are currently in progress. +cardant.files.transfer.uploading=Uploading... +cardant.files=Files +cardant.fuzzy.any=Is anything. +cardant.fuzzy.equalTo=Is equal to: +cardant.fuzzy.notEqualTo=Is not equal to: +cardant.fuzzy.notSimilarTo=Is not similar to: +cardant.fuzzy.similarTo=Is similar to: +cardant.hashAlgorithm=Hash Algorithm +cardant.hashValue=Hash Value +cardant.id=ID cardant.itemDetails.nameSet=Set... - -cardant.itemDetails.attachment.relation=Relation -cardant.itemDetails.attachment.id=ID -cardant.itemDetails.attachment.description=Description -cardant.itemDetails.attachment.mediaType=Media Type -cardant.itemDetails.attachment.size=Size -cardant.itemDetails.attachment.hashAlgorithm=Hash Algorithm -cardant.itemDetails.attachment.hashValue=Hash Value - -cardant.items.tooltip.add=Add a new item. -cardant.items.tooltip.remove=Remove item(s). -cardant.items.tooltip.search=Execute an item search based on the above parameters. -cardant.items.tooltip.searchClear=Reset the item search parameters. - -cardant.itemSearch.searchClear=Clear -cardant.itemSearch.search=Search -cardant.itemSearch.basicParameters=Basic Parameters -cardant.itemSearch.pageOf=Page {0} of {1} - -cardant.itemSearch.itemId=ID -cardant.itemSearch.itemName=Name cardant.itemSearch.itemMetadata=Metadata Matching Expression -cardant.itemSearch.tooltip.itemMetadata=Items must have at least one metadata element matching the given expression. - -cardant.itemSearch.location=Location +cardant.itemSearch.itemName=Name cardant.itemSearch.location.any=Is anywhere. -cardant.itemSearch.location.exact=Is equal to: cardant.itemSearch.location.descendants=Is a descendant of: +cardant.itemSearch.location.exact=Is equal to: cardant.itemSearch.location.select=Select... - -cardant.itemSearch.name=Name -cardant.itemSearch.name.any=Is anything. -cardant.itemSearch.name.equalTo=Is equal to: -cardant.itemSearch.name.notEqualTo=Is not equal to: -cardant.itemSearch.name.similarTo=Is similar to: -cardant.itemSearch.name.notSimilarTo=Is not similar to: - -cardant.itemSearch.serial=Serial -cardant.itemSearch.serial.any=Is anything. -cardant.itemSearch.serial.equalTo=Is equal to: -cardant.itemSearch.serial.notEqualTo=Is not equal to: - -cardant.itemSearch.types=Types -cardant.itemSearch.types.any=Are anything. -cardant.itemSearch.types.equalTo=Are equal to: -cardant.itemSearch.types.notEqualTo=Are not equal to: -cardant.itemSearch.types.supersetOf=Are a superset of: -cardant.itemSearch.types.subsetOf=Are a subset of: -cardant.itemSearch.types.overlapping=Are overlapping: - -cardant.itemSearch.metadata.or=Or +cardant.itemSearch.location=Location cardant.itemSearch.metadata.and=And cardant.itemSearch.metadata.any=Any cardant.itemSearch.metadata.changeType=Change type to... -cardant.itemSearch.metadata.wrapIn=Wrap in... +cardant.itemSearch.metadata.or=Or cardant.itemSearch.metadata.specific=Match - -cardant.itemSearch.metadata.comparison.equalTo=Equal to -cardant.itemSearch.metadata.comparison.notEqualTo=Not equal to -cardant.itemSearch.metadata.comparison.any=Any - +cardant.itemSearch.metadata.value.any=Any value cardant.itemSearch.metadata.value.exactText=With exact text -cardant.itemSearch.metadata.value.searchText=Matching text search query cardant.itemSearch.metadata.value.integralWithin=Integral value within range +cardant.itemSearch.metadata.value.moneyCurrency=Monetary value with currency +cardant.itemSearch.metadata.value.moneyWithin=Monetary value within range cardant.itemSearch.metadata.value.realWithin=Real value within range +cardant.itemSearch.metadata.value.searchText=Matching text search query cardant.itemSearch.metadata.value.timeWithin=Time value within range -cardant.itemSearch.metadata.value.moneyWithin=Monetary value within range -cardant.itemSearch.metadata.value.moneyCurrency=Monetary value with currency -cardant.itemSearch.metadata.value.any=Any value - -cardant.fileSearch.description=Description -cardant.fileSearch.description.any=Is anything. -cardant.fileSearch.description.equalTo=Is equal to: -cardant.fileSearch.description.notEqualTo=Is not equal to: -cardant.fileSearch.description.similarTo=Is similar to: -cardant.fileSearch.description.notSimilarTo=Is not similar to: - -cardant.fileSearch.mediaType=Media Type -cardant.fileSearch.mediaType.any=Is anything. -cardant.fileSearch.mediaType.equalTo=Is equal to: -cardant.fileSearch.mediaType.notEqualTo=Is not equal to: -cardant.fileSearch.mediaType.similarTo=Is similar to: -cardant.fileSearch.mediaType.notSimilarTo=Is not similar to: - -cardant.fileSearch.sizeRange=Size Range -cardant.fileSearch.pageOf=Page {0} of {1} - -cardant.fileCreate.localFile=File -cardant.fileCreate.select=Select... -cardant.fileCreate.description=Description -cardant.fileCreate.mediaType=Media Type -cardant.fileCreate.size=Size -cardant.fileCreate.hashValue=Hash -cardant.fileCreate.upload=Upload - -cardant.fileCreate.openFile=Open a file... - -cardant.files.tooltip.add=Add a new file. -cardant.files.tooltip.remove=Remove file(s). -cardant.files.tooltip.download=Download a file. - -cardant.files.tooltip.search=Execute a file search based on the above parameters. -cardant.files.tooltip.searchClear=Reset the file search parameters. - -cardant.files.transfer.uploading=Uploading... -cardant.files.transfer.downloading=Downloading... -cardant.files.transfer.idle=No transfers are currently in progress. - -cardant.files.file.id=ID -cardant.files.file.description=Description -cardant.files.file.mediaType=Media Type -cardant.files.file.size=Size -cardant.files.file.hashAlgorithm=Hash Algorithm -cardant.files.file.hashValue=Hash Value - -cardant.auditSearch.owner=Owner -cardant.auditSearch.type=Type -cardant.auditSearch.timeRange=Time Range -cardant.auditSearch.timeRange.lower=Lower bound... -cardant.auditSearch.timeRange.upper=Upper bound... - -cardant.auditSearch.id=ID -cardant.auditSearch.time=Time -cardant.auditSearch.data=Data -cardant.auditSearch.key=Key -cardant.auditSearch.value=Value -cardant.auditSearch.pageOf=Page {0} of {1} - -cardant.auditSearch.type.any=Is anything. -cardant.auditSearch.type.equalTo=Is equal to: -cardant.auditSearch.type.notEqualTo=Is not equal to: - -cardant.tpSearch.description=Description - -cardant.tpSearch.name=Name -cardant.tpSearch.version=Version - -cardant.typePackages.tooltip.packageAdd=Add a new type package. -cardant.typePackages.tooltip.packageRemove=Remove the selected type packages. +cardant.itemSearch.metadata.wrapIn=Wrap in... +cardant.itemSearch.pageOf=Page {0} of {1} +cardant.itemSearch.search=Search +cardant.itemSearch.searchClear=Clear +cardant.items=Items +cardant.localFile=File +cardant.location=Location +cardant.locationDetails.nameSet=Set... +cardant.locations.removeConfirm=Are you sure you want to remove the given location? This operation cannot be undone. +cardant.locations.removeConfirmTitle=Confirm removal. +cardant.locations=Locations +cardant.login.bookmark.createMain=Please enter a name for the bookmark. +cardant.login.bookmark.createTitle=Create Bookmark +cardant.login.bookmarks=Bookmarks +cardant.login.connect=Connect +cardant.login.connected=Connected to {0}. +cardant.login.connecting=Connecting to {0}... +cardant.login.host=Host +cardant.login.https=HTTPS +cardant.login.password=Password +cardant.login.port=Port +cardant.login.title=Connect to server... +cardant.login.username=Username +cardant.login=Login +cardant.mediaType=Media Type +cardant.metadata=Metadata +cardant.name=Name +cardant.package=Package +cardant.relation=Relation +cardant.remove=Remove +cardant.search.clear=Clear +cardant.search.confirmClear=Are you sure you want to clear the search parameters? +cardant.search.confirmClearTitle=Confirm +cardant.search=Search +cardant.searchBasicParameters=Basic Parameters +cardant.serial=Serial +cardant.setComparison.any=Are anything. +cardant.setComparison.equalTo=Are equal to: +cardant.setComparison.notEqualTo=Are not equal to: +cardant.setComparison.overlapping=Are overlapping: +cardant.setComparison.subsetOf=Are a subset of: +cardant.setComparison.supersetOf=Are a superset of: +cardant.size=Size +cardant.title=Cardant +cardant.tooltip.attachmentAdd=Add a new attachment. +cardant.tooltip.attachmentRemove=Remove the selected attachments. +cardant.tooltip.files.add=Add a new file. +cardant.tooltip.files.download=Download a file. +cardant.tooltip.files.remove=Remove file(s). +cardant.tooltip.files.search=Execute a file search based on the above parameters. +cardant.tooltip.items.add=Add a new item. +cardant.tooltip.items.remove=Remove item(s). +cardant.tooltip.locations.add=Add a new location. +cardant.tooltip.locations.remove=Remove location(s). +cardant.tooltip.login.createBookmark=Create a new bookmark. +cardant.tooltip.login.deleteBookmark=Delete the selected bookmark. +cardant.tooltip.login.fieldNotValid=This field is not valid! +cardant.tooltip.login.host=The host name to which to cardant.login. +cardant.tooltip.login.https=Check this box if HTTPS should be used when connecting to the server. +cardant.tooltip.login.password=The password that will be used when connecting. +cardant.tooltip.login.port=The port to which to login. This will be automatically inferred if left empty. +cardant.tooltip.login.user=The username that will be used when connecting. +cardant.tooltip.metaAdd=Add a new metadata item. +cardant.tooltip.metaRemove=Remove the selected metadata items. +cardant.tooltip.metadata=The returned objects must have at least one metadata element matching the given expression. +cardant.tooltip.search=Execute a search based on the above parameters. +cardant.tooltip.searchClear=Reset the search parameters. +cardant.tooltip.searchNext=Go to the next page of search results. +cardant.tooltip.searchPrevious=Go to the previous page of search results. +cardant.tooltip.typePackages.packageAdd=Add a new type package. +cardant.tooltip.typePackages.packageRemove=Remove the selected type packages. +cardant.type=Type +cardant.typePackages=Type Packages +cardant.types=Types +cardant.ui.boot=Cardant initialized. +cardant.value=Value +cardant.version=Version diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/auditSearch.fxml b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/auditSearch.fxml index 3cd0818..540bc57 100644 --- a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/auditSearch.fxml +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/auditSearch.fxml @@ -25,7 +25,7 @@ - + @@ -77,7 +77,7 @@ @@ -90,7 +90,7 @@ - + diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/file.fxml b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/file.fxml index 9e561dc..b7caef2 100644 --- a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/file.fxml +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/file.fxml @@ -32,12 +32,12 @@ - - + @@ -142,7 +142,7 @@ - + diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/itemSearch.fxml b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/itemSearch.fxml index bd5c6c6..961a6ba 100644 --- a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/itemSearch.fxml +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/itemSearch.fxml @@ -27,7 +27,7 @@ - + @@ -65,14 +65,14 @@ - @@ -81,7 +81,7 @@ - + @@ -129,7 +129,7 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locationTable.fxml b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locationTable.fxml new file mode 100644 index 0000000..ba28304 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locationTable.fxml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locations.fxml b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locations.fxml index 01f9f1f..cd61619 100644 --- a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locations.fxml +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locations.fxml @@ -16,7 +16,7 @@