diff --git a/com.io7m.cardant_gui.tests/src/main/java/com/io7m/cardant_gui/tests/CAGMainItemSearchViewControllerTest.java b/com.io7m.cardant_gui.tests/src/main/java/com/io7m/cardant_gui/tests/CAGMainItemSearchViewControllerTest.java deleted file mode 100644 index db59a5e..0000000 --- a/com.io7m.cardant_gui.tests/src/main/java/com/io7m/cardant_gui/tests/CAGMainItemSearchViewControllerTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.tests; - -import com.io7m.cardant_gui.ui.internal.CAGClientService; -import com.io7m.cardant_gui.ui.internal.CAGController; -import com.io7m.cardant_gui.ui.internal.CAGControllerType; -import com.io7m.cardant_gui.ui.internal.CAGMainItemSearchView; -import com.io7m.cardant_gui.ui.internal.CAGStatusService; -import com.io7m.cardant_gui.ui.internal.CAGStrings; -import com.io7m.cardant_gui.ui.internal.CAGStringsType; -import com.io7m.repetoir.core.RPServiceDirectory; -import com.io7m.xoanon.commander.api.XCCommanderType; -import com.io7m.xoanon.commander.api.XCRobotType; -import com.io7m.xoanon.extension.XoExtension; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.util.Locale; -import java.util.Objects; - -@ExtendWith(XoExtension.class) -public final class CAGMainItemSearchViewControllerTest -{ - private CAGStrings strings; - private RPServiceDirectory services; - private Parent pane; - private CAGMainItemSearchView view; - private CAGStatusService status; - private CAGClientService client; - private CAGControllerType controller; - - @BeforeEach - public void setup( - final XCCommanderType commander, - final XCRobotType robot) - throws Exception - { - this.strings = new CAGStrings(Locale.getDefault()); - this.status = new CAGStatusService(); - this.client = new CAGClientService(this.status, this.strings); - this.controller = CAGController.create(this.client); - - this.services = new RPServiceDirectory(); - this.services.register(CAGStringsType.class, this.strings); - this.services.register(CAGControllerType.class, this.controller); - - final var loader = - new FXMLLoader( - CAGMainItemSearchView.class.getResource( - "/com/io7m/cardant_gui/ui/internal/itemSearch.fxml"), - this.strings.resources() - ); - - loader.setControllerFactory( - clazz -> { - if (Objects.equals(clazz, CAGMainItemSearchView.class)) { - return new CAGMainItemSearchView(this.services); - } - throw new IllegalStateException( - "Unrecognized controller class: %s".formatted(clazz) - ); - } - ); - - this.pane = loader.load(); - this.view = loader.getController(); - } - - @Test - public void testInit( - final XCCommanderType commander, - final XCRobotType robot) - throws Exception - { - robot.slowMotionEnable(); - - final var stage = - commander.stageNewAndWait(s -> { - s.setWidth(800.0); - s.setHeight(600.0); - s.setScene(new Scene(this.pane)); - s.show(); - }); - } -} diff --git a/com.io7m.cardant_gui.tests/src/main/java/com/io7m/cardant_gui/tests/CAGMetaMatchTreeTest.java b/com.io7m.cardant_gui.tests/src/main/java/com/io7m/cardant_gui/tests/CAGMetaMatchTreeTest.java index f469eba..31b4004 100644 --- a/com.io7m.cardant_gui.tests/src/main/java/com/io7m/cardant_gui/tests/CAGMetaMatchTreeTest.java +++ b/com.io7m.cardant_gui.tests/src/main/java/com/io7m/cardant_gui/tests/CAGMetaMatchTreeTest.java @@ -31,7 +31,7 @@ import com.io7m.cardant.model.comparisons.CAComparisonExactType.Anything; import com.io7m.cardant.model.comparisons.CAComparisonExactType.IsEqualTo; import com.io7m.cardant.model.comparisons.CAComparisonExactType.IsNotEqualTo; -import com.io7m.cardant_gui.ui.internal.CAGMainItemSearchView; +import com.io7m.cardant_gui.ui.internal.CAGItemSearchView; import com.io7m.cardant_gui.ui.internal.CAGMetaMatchNodeType; import com.io7m.cardant_gui.ui.internal.CAGMetaMatchTree; import com.io7m.cardant_gui.ui.internal.CAGStrings; @@ -57,7 +57,7 @@ public final class CAGMetaMatchTreeTest private CAGStrings strings; private RPServiceDirectory services; private Parent pane; - private CAGMainItemSearchView controller; + private CAGItemSearchView controller; private TreeView treeView; private CAGMetaMatchTree metaTree; diff --git a/com.io7m.cardant_gui.ui/pom.xml b/com.io7m.cardant_gui.ui/pom.xml index 3d2f6b5..b95b33b 100644 --- a/com.io7m.cardant_gui.ui/pom.xml +++ b/com.io7m.cardant_gui.ui/pom.xml @@ -32,6 +32,11 @@ javafx-graphics + + com.io7m.jaffirm + com.io7m.jaffirm.core + + com.io7m.seltzer com.io7m.seltzer.api 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 ae77ed9..c28d53c 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/images/images16.xcf b/com.io7m.cardant_gui.ui/src/main/images/images16.xcf index e08aa3a..0b97a75 100644 Binary files a/com.io7m.cardant_gui.ui/src/main/images/images16.xcf and b/com.io7m.cardant_gui.ui/src/main/images/images16.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 e5cace2..2a1c96c 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 @@ -22,29 +22,39 @@ import com.io7m.cardant_gui.ui.internal.CAGClientServiceType; import com.io7m.cardant_gui.ui.internal.CAGController; import com.io7m.cardant_gui.ui.internal.CAGControllerType; +import com.io7m.cardant_gui.ui.internal.CAGEventService; +import com.io7m.cardant_gui.ui.internal.CAGEventServiceType; import com.io7m.cardant_gui.ui.internal.CAGFileChoosers; import com.io7m.cardant_gui.ui.internal.CAGFileChoosersType; +import com.io7m.cardant_gui.ui.internal.CAGFileListView; +import com.io7m.cardant_gui.ui.internal.CAGFileSearchView; +import com.io7m.cardant_gui.ui.internal.CAGFileSelectDialogs; +import com.io7m.cardant_gui.ui.internal.CAGFileTransferController; +import com.io7m.cardant_gui.ui.internal.CAGFileTransferControllerType; import com.io7m.cardant_gui.ui.internal.CAGFileViewDialogs; import com.io7m.cardant_gui.ui.internal.CAGItemAttachmentAddDialogs; +import com.io7m.cardant_gui.ui.internal.CAGItemDetailsView; +import com.io7m.cardant_gui.ui.internal.CAGItemSearchView; +import com.io7m.cardant_gui.ui.internal.CAGItemSelectDialogs; +import com.io7m.cardant_gui.ui.internal.CAGItemTableView; import com.io7m.cardant_gui.ui.internal.CAGLocationAttachmentAddDialogs; +import com.io7m.cardant_gui.ui.internal.CAGLocationDetailsView; import com.io7m.cardant_gui.ui.internal.CAGLocationReparentDialogs; +import com.io7m.cardant_gui.ui.internal.CAGLocationSelectDialogs; +import com.io7m.cardant_gui.ui.internal.CAGLocationTreeView; import com.io7m.cardant_gui.ui.internal.CAGMainAuditSearchView; import com.io7m.cardant_gui.ui.internal.CAGMainAuditTableView; -import com.io7m.cardant_gui.ui.internal.CAGMainFileListView; -import com.io7m.cardant_gui.ui.internal.CAGMainFileSearchView; -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.CAGMainStockSearchView; -import com.io7m.cardant_gui.ui.internal.CAGMainStockTableView; +import com.io7m.cardant_gui.ui.internal.CAGMainFilesView; +import com.io7m.cardant_gui.ui.internal.CAGMainItemsView; +import com.io7m.cardant_gui.ui.internal.CAGMainLocationsView; +import com.io7m.cardant_gui.ui.internal.CAGMainStockView; import com.io7m.cardant_gui.ui.internal.CAGMainTypePackageDetailsView; import com.io7m.cardant_gui.ui.internal.CAGMainTypePackageSearchView; import com.io7m.cardant_gui.ui.internal.CAGMainTypePackageTableView; import com.io7m.cardant_gui.ui.internal.CAGMainView; import com.io7m.cardant_gui.ui.internal.CAGStatusService; +import com.io7m.cardant_gui.ui.internal.CAGStockSearchView; +import com.io7m.cardant_gui.ui.internal.CAGStockTableView; import com.io7m.cardant_gui.ui.internal.CAGStringConstants; import com.io7m.cardant_gui.ui.internal.CAGStrings; import com.io7m.cardant_gui.ui.internal.CAGStringsType; @@ -62,6 +72,8 @@ import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Locale; import java.util.Map; @@ -74,6 +86,9 @@ public final class CAGApplication extends Application { + private static final Logger LOG = + LoggerFactory.getLogger(CAGApplication.class); + private final ApplicationDirectoriesType directories; /** @@ -129,25 +144,45 @@ public void start( CAGFileViewDialogs.class, new CAGFileViewDialogs(services) ); - services.register( - CAGItemAttachmentAddDialogs.class, - new CAGItemAttachmentAddDialogs(services) - ); services.register( CAGLocationAttachmentAddDialogs.class, new CAGLocationAttachmentAddDialogs(services) ); + services.register( + CAGLocationSelectDialogs.class, + new CAGLocationSelectDialogs(services) + ); + services.register( + CAGItemAttachmentAddDialogs.class, + new CAGItemAttachmentAddDialogs(services) + ); services.register( CAGLocationReparentDialogs.class, new CAGLocationReparentDialogs(services) ); + services.register( + CAGFileSelectDialogs.class, + new CAGFileSelectDialogs(services) + ); + services.register( + CAGItemSelectDialogs.class, + new CAGItemSelectDialogs(services) + ); final var status = new CAGStatusService(); services.register(CAGStatusService.class, status); - final var clientService = new CAGClientService(status, strings); + final var events = CAGEventService.create(); + services.register(CAGEventServiceType.class, events); + + final var clientService = new CAGClientService(status, events, strings); services.register(CAGClientServiceType.class, clientService); + services.register( + CAGFileTransferControllerType.class, + CAGFileTransferController.create(clientService) + ); + final var controller = CAGController.create(clientService); services.register(CAGControllerType.class, controller); @@ -167,69 +202,83 @@ public void start( () -> new CAGMainView(services) ), Map.entry( - CAGMainItemDetailsView.class, - () -> new CAGMainItemDetailsView(services) + CAGMainItemsView.class, + () -> new CAGMainItemsView(services) ), Map.entry( - CAGMainItemSearchView.class, - () -> new CAGMainItemSearchView(services) + CAGItemSearchView.class, + () -> new CAGItemSearchView(services) ), Map.entry( - CAGMainStockSearchView.class, - () -> new CAGMainStockSearchView(services) + CAGItemTableView.class, + () -> new CAGItemTableView(services) ), Map.entry( - CAGMainStockTableView.class, - () -> new CAGMainStockTableView(services) + CAGItemDetailsView.class, + () -> new CAGItemDetailsView(services) ), Map.entry( - CAGMainItemTableView.class, - () -> new CAGMainItemTableView(services) + CAGMainLocationsView.class, + () -> new CAGMainLocationsView(services) ), Map.entry( - CAGMainFileSearchView.class, - () -> new CAGMainFileSearchView(services) + CAGFileSearchView.class, + () -> new CAGFileSearchView(services) ), Map.entry( - CAGMainFileListView.class, - () -> new CAGMainFileListView(services) + CAGMainFilesView.class, + () -> new CAGMainFilesView(services) ), Map.entry( - CAGMainAuditSearchView.class, - () -> new CAGMainAuditSearchView(services) + CAGMainStockView.class, + () -> new CAGMainStockView(services) ), Map.entry( - CAGMainAuditTableView.class, - () -> new CAGMainAuditTableView(services) + CAGMainTypePackageSearchView.class, + () -> new CAGMainTypePackageSearchView(services) + ), + Map.entry( + CAGMainTypePackageTableView.class, + () -> new CAGMainTypePackageTableView(services) ), Map.entry( CAGMainTypePackageDetailsView.class, () -> new CAGMainTypePackageDetailsView(services) ), Map.entry( - CAGMainTypePackageSearchView.class, - () -> new CAGMainTypePackageSearchView(services) + CAGMainAuditSearchView.class, + () -> new CAGMainAuditSearchView(services) ), Map.entry( - CAGMainTypePackageTableView.class, - () -> new CAGMainTypePackageTableView(services) + CAGMainAuditTableView.class, + () -> new CAGMainAuditTableView(services) ), Map.entry( - CAGMainLocationDetailsView.class, - () -> new CAGMainLocationDetailsView(services) + CAGFileListView.class, + () -> new CAGFileListView(services) ), Map.entry( - CAGMainLocationSearchView.class, - () -> new CAGMainLocationSearchView(services) + CAGStockSearchView.class, + () -> new CAGStockSearchView(services) ), Map.entry( - CAGMainLocationTableView.class, - () -> new CAGMainLocationTableView(services) + CAGStockTableView.class, + () -> new CAGStockTableView(services) + ), + Map.entry( + CAGLocationTreeView.class, + () -> new CAGLocationTreeView(services) + ), + Map.entry( + CAGLocationDetailsView.class, + () -> new CAGLocationDetailsView(services) ) ); loader.setControllerFactory( clazz -> { + LOG.debug("Resolving controller {}", clazz); + final var supplier = controllers.get(clazz); if (supplier == null) { throw new IllegalStateException( diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGClientService.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGClientService.java index 5a903ab..a86c7f9 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGClientService.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGClientService.java @@ -25,6 +25,8 @@ import com.io7m.cardant.client.basic.CAClients; import com.io7m.cardant.model.CAFileID; import com.io7m.cardant.protocol.inventory.CAICommandType; +import com.io7m.cardant.protocol.inventory.CAIResponseItemGet; +import com.io7m.cardant.protocol.inventory.CAIResponseLocationGet; import com.io7m.cardant.protocol.inventory.CAIResponseType; import com.io7m.idstore.model.IdName; import com.io7m.jattribute.core.AttributeReadableType; @@ -71,6 +73,7 @@ public final class CAGClientService private final Semaphore commandSemaphore; private final CAClientType client; private final CAGStatusService statusService; + private final CAGEventServiceType events; private final CAGStringsType strings; private final Semaphore transferSemaphore; private final Semaphore imageSemaphore; @@ -79,16 +82,20 @@ public final class CAGClientService * The cardant client service. * * @param inStatusService The status service + * @param inEvents The event service * @param inStrings The strings */ public CAGClientService( final CAGStatusService inStatusService, + final CAGEventServiceType inEvents, final CAGStringsType inStrings) throws CAClientException { this.statusService = Objects.requireNonNull(inStatusService, "statusService"); + this.events = + Objects.requireNonNull(inEvents, "inEvents"); this.strings = Objects.requireNonNull(inStrings, "inStrings"); @@ -147,6 +154,10 @@ public void login( { LOG.debug("Login: {}", host); + Objects.requireNonNull(host, "host"); + Objects.requireNonNull(username, "username"); + Objects.requireNonNull(password, "password"); + this.executor.execute(() -> { try { this.commandSemaphore.acquire(); @@ -214,9 +225,23 @@ public CompletableFuture execute( try { this.statusService.publish(RUNNING, "Executing command…"); - future.complete( - this.client.sendAndWaitOrThrow(command, Duration.ofSeconds(30L)) - ); + + final var response = + this.client.sendAndWaitOrThrow(command, Duration.ofSeconds(30L)); + + switch (response) { + case final CAIResponseItemGet r -> { + this.events.publish(new CAGEventItemUpdated(r.data())); + } + case final CAIResponseLocationGet r -> { + this.events.publish(new CAGEventLocationUpdated(r.data())); + } + default -> { + // Nothing specific! + } + } + + future.complete(response); this.statusService.publish(IDLE, "Executed command."); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGCloseableSubscriber.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGCloseableSubscriber.java new file mode 100644 index 0000000..94c1b45 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGCloseableSubscriber.java @@ -0,0 +1,97 @@ +/* + * 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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; +import java.util.concurrent.Flow; +import java.util.function.Consumer; + +/** + * A subscriber implementation that is closeable. + * + * @param The type of received values + */ + +public final class CAGCloseableSubscriber + implements Flow.Subscriber, AutoCloseable +{ + private static final Logger LOG = + LoggerFactory.getLogger(CAGCloseableSubscriber.class); + + private final Consumer consumer; + private Flow.Subscription subscription; + + private CAGCloseableSubscriber( + final Consumer inConsumer) + { + this.consumer = + Objects.requireNonNull(inConsumer, "inConsumer"); + } + + /** + * A subscriber implementation that is closeable. + * + * @param inConsumer The value consumer + * @param The type of received values + * + * @return The subscriber + */ + + public static CAGCloseableSubscriber create( + final Consumer inConsumer) + { + return new CAGCloseableSubscriber<>(inConsumer); + } + + @Override + public void close() + { + this.subscription.cancel(); + } + + @Override + public void onSubscribe( + final Flow.Subscription inSubscription) + { + this.subscription = + Objects.requireNonNull(inSubscription, "subscription"); + } + + @Override + public void onNext( + final T item) + { + this.consumer.accept(Objects.requireNonNull(item, "item")); + } + + @Override + public void onError( + final Throwable throwable) + { + LOG.error("onError: ", throwable); + } + + @Override + public void onComplete() + { + + } +} 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 2dfbf54..ef10a35 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 @@ -17,37 +17,13 @@ package com.io7m.cardant_gui.ui.internal; -import com.io7m.cardant.client.api.CAClientTransferStatistics; -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.CALocation; -import com.io7m.cardant.model.CALocationID; -import com.io7m.cardant.model.CALocationSummary; -import com.io7m.cardant.model.CAMetadataType; -import com.io7m.cardant.model.CAStockOccurrenceType; -import com.io7m.cardant.model.CAStockSearchParameters; -import com.io7m.cardant.model.CATypeRecordIdentifier; 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.CAICommandAuditSearchBegin; -import com.io7m.cardant.protocol.inventory.CAICommandFileSearchBegin; -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.CAICommandLocationDelete; -import com.io7m.cardant.protocol.inventory.CAICommandLocationGet; -import com.io7m.cardant.protocol.inventory.CAICommandLocationList; -import com.io7m.cardant.protocol.inventory.CAICommandLocationPut; -import com.io7m.cardant.protocol.inventory.CAICommandStockSearchBegin; import com.io7m.cardant.protocol.inventory.CAICommandTypePackageGetText; import com.io7m.cardant.protocol.inventory.CAICommandTypePackageInstall; import com.io7m.cardant.protocol.inventory.CAICommandTypePackageSearchBegin; @@ -58,8 +34,6 @@ 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; @@ -76,18 +50,8 @@ import java.nio.file.Files; 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.TreeMap; -import java.util.TreeSet; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Flow; -import java.util.concurrent.SubmissionPublisher; -import java.util.stream.Collectors; - -import static com.io7m.cardant_gui.ui.internal.CAGTransferStatusType.Idle.IDLE; + import static java.nio.charset.StandardCharsets.UTF_8; /** @@ -99,45 +63,17 @@ public final class CAGController implements CAGControllerType private static final Logger LOG = LoggerFactory.getLogger(CAGController.class); - 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 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 itemSelected; private final SimpleStringProperty typePackageTextSelected; private final SortedList auditEventsSorted; - private final SortedList itemsSorted; - private final SortedList itemSelectedMetaSorted; private final SortedList typePackagesSorted; - 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 final ObservableList itemSelectedTypes; - private final SortedList itemSelectedTypesSorted; private final SimpleObjectProperty typePackageSelected; - private final ObservableList stockRead; - private final SortedList stockSorted; - private final ObservableList stock; - private final SimpleObjectProperty stockPages; - private final SubmissionPublisher tabSelection; private CAGController( final CAGClientServiceType inClientService) @@ -145,51 +81,11 @@ private CAGController( this.clientService = Objects.requireNonNull(inClientService, "clientService"); - this.tabSelection = - new SubmissionPublisher<>(); - - this.items = - FXCollections.observableArrayList(); - this.itemsSorted = - new SortedList<>(this.items); - this.itemsRead = - FXCollections.unmodifiableObservableList(this.items); - - this.stock = - FXCollections.observableArrayList(); - this.stockSorted = - new SortedList<>(this.stock); - this.stockRead = - FXCollections.unmodifiableObservableList(this.stock); - - this.stockPages = - new SimpleObjectProperty<>(new CAGPageRange(0L, 0L)); - - this.itemPages = - new SimpleObjectProperty<>(new CAGPageRange(0L, 0L)); - this.itemSelected = - new SimpleObjectProperty<>(); - - this.itemSelectedMeta = - FXCollections.observableArrayList(); - this.itemSelectedMetaSorted = - new SortedList<>(this.itemSelectedMeta); - this.itemSelectedAttachments = - FXCollections.observableArrayList(); - - this.itemSelectedTypes = - FXCollections.observableArrayList(); - this.itemSelectedTypesSorted = - new SortedList<>(this.itemSelectedTypes); - this.filePages = new SimpleObjectProperty<>(new CAGPageRange(0L, 0L)); this.files = FXCollections.observableArrayList(); - this.transferStatus = - new SimpleObjectProperty<>(IDLE); - this.auditEventPages = new SimpleObjectProperty<>(new CAGPageRange(0L, 0L)); this.auditEvents = @@ -207,21 +103,6 @@ private CAGController( this.typePackageTextSelected = new SimpleStringProperty(); this.typePackageSelected = - new SimpleObjectProperty(); - - 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<>(); } @@ -245,7 +126,6 @@ public static CAGControllerType create( private void onClientStatusChanged() { - this.items.clear(); this.auditEvents.clear(); this.files.clear(); } @@ -256,98 +136,6 @@ public String description() return "Main controller"; } - @Override - public ObservableList itemsView() - { - return this.itemsRead; - } - - @Override - public SortedList itemsViewSorted() - { - return this.itemsSorted; - } - - @Override - public void itemSearchBegin( - final CAItemSearchParameters searchParameters) - { - final var future = - this.clientService.execute( - new CAICommandItemSearchBegin(searchParameters) - ); - - future.thenAccept(response -> { - Platform.runLater(() -> { - final var data = - response.data(); - - final var newItemPage = - new ArrayList<>(data.items()); - - LOG.debug("Received {} items", newItemPage.size()); - this.itemPages.set( - new CAGPageRange( - (long) data.pageIndex(), - (long) data.pageCount() - ) - ); - this.items.setAll(newItemPage); - }); - }); - } - - @Override - public void itemGet( - final CAItemID id) - { - final var future = - this.clientService.execute(new CAICommandItemGet(id)); - - future.thenAccept(response -> { - Platform.runLater(() -> { - final var item = response.data(); - - this.itemSelected.set(item.summary()); - - this.itemSelectedMeta.setAll( - item.metadata() - .values() - .stream() - .toList() - ); - - this.itemSelectedAttachments.setAll( - item.attachments() - .values() - .stream() - .sorted(Comparator.comparing(o -> o.key().fileID())) - .collect(Collectors.toList()) - ); - - this.itemSelectedTypes.setAll(item.types()); - }); - }); - } - - @Override - public ObservableValue itemPages() - { - return this.itemPages; - } - - @Override - public ObservableValue itemSelected() - { - return this.itemSelected; - } - - @Override - public SortedList itemSelectedTypes() - { - return this.itemSelectedTypesSorted; - } - @Override public void typePackageGet( final CATypePackageIdentifier id) @@ -407,166 +195,6 @@ public void typePackageSelectNothing() this.typePackageTextSelected.set(null); } - @Override - public SortedList itemSelectedMetadata() - { - return this.itemSelectedMetaSorted; - } - - @Override - public void itemSelectNothing() - { - this.itemSelectedMeta.clear(); - this.itemSelected.set(null); - this.itemSelectedAttachments.clear(); - } - - @Override - public ObservableList itemSelectedAttachments() - { - return this.itemSelectedAttachments; - } - - @Override - public void fileSearchBegin( - final CAFileSearchParameters searchParameters) - { - final var future = - this.clientService.execute( - new CAICommandFileSearchBegin(searchParameters) - ); - - future.thenAccept(response -> { - Platform.runLater(() -> { - final var data = - response.data(); - - final var newItemPage = - new ArrayList<>(data.items()); - - LOG.debug("Received {} files", newItemPage.size()); - this.filePages.set( - new CAGPageRange( - (long) data.pageIndex(), - (long) data.pageCount() - ) - ); - this.files.setAll(newItemPage); - }); - }); - } - - @Override - public ObservableList filesView() - { - return this.files; - } - - @Override - public ObservableValue transferStatus() - { - return this.transferStatus; - } - - @Override - public void fileUpload( - final CAFileID fileID, - final Path file, - final String contentType, - final String description) - { - final var future = - this.clientService.fileUpload( - fileID, - file, - contentType, - description, - statistics -> { - Platform.runLater(() -> this.onUploadStatisticsChanged(statistics)); - } - ); - - future.thenAccept(response -> { - Platform.runLater(() -> this.transferStatus.set(IDLE)); - }); - } - - @Override - public void fileDownload( - final CAFileID fileID, - final Path file, - final Path fileTmp, - final long size, - final String hashAlgorithm, - final String hashValue) - { - final var future = - this.clientService.fileDownload( - fileID, - file, - fileTmp, - size, - hashAlgorithm, - hashValue, - statistics -> { - Platform.runLater(() -> this.onDownloadStatisticsChanged(statistics)); - } - ); - - future.thenAccept(response -> { - Platform.runLater(() -> this.transferStatus.set(IDLE)); - }); - } - - private void onDownloadStatisticsChanged( - final CAClientTransferStatistics statistics) - { - this.transferStatus.set(new CAGTransferStatusType.Downloading(statistics)); - } - - private void onUploadStatisticsChanged( - final CAClientTransferStatistics statistics) - { - this.transferStatus.set(new CAGTransferStatusType.Uploading(statistics)); - } - - @Override - public ObservableValue filePages() - { - return this.filePages; - } - - @Override - public CompletableFuture imageGet( - final CAFileID fileID, - final Path file, - final Path fileTmp, - final long size, - final String hashAlgorithm, - final String hashValue, - final int width, - final int height) - { - return this.clientService.imageGet( - fileID, - file, - fileTmp, - size, - hashAlgorithm, - hashValue, - width, - height - ); - } - - @Override - public void itemAttachmentAdd( - final CAICommandItemAttachmentAdd command) - { - this.clientService.execute(command); - this.itemGet(command.item()); - } - @Override public void auditSearchBegin( final CAAuditSearchParameters searchParameters) @@ -680,280 +308,4 @@ 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) - { - this.clientService.execute(command); - this.locationGet(command.location()); - } - - @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; - } - - final var future = - this.clientService.execute(new CAICommandLocationDelete(location.id())); - - future.thenAccept(response -> { - Platform.runLater(() -> { - this.locationTreeDelete(this.locationTree.get(), location.id()); - }); - }); - } - - @Override - public void locationCreate( - final String name) - { - final var future = - this.clientService.execute(new CAICommandLocationPut( - new CALocation( - CALocationID.random(), - Optional.empty(), - name, - new TreeMap<>(), - new TreeMap<>(), - new TreeSet<>() - ) - )); - - future.thenAccept(response -> this.locationSearchBegin()); - } - - @Override - public void locationReparent( - final CALocationID location, - final CALocationID newParent) - { - final var future0 = - this.clientService.execute(new CAICommandLocationGet(location)); - - final var future1 = - future0.thenCompose(response -> { - final var source = response.data(); - return this.clientService.execute( - new CAICommandLocationPut( - new CALocation( - source.id(), - Optional.of(newParent), - source.name(), - source.metadata(), - source.attachments(), - source.types() - ) - ) - ); - }); - - future1.thenAccept(response -> this.locationSearchBegin()); - } - - private boolean locationTreeDelete( - final TreeItem tree, - final CALocationID id) - { - if (Objects.equals(tree.getValue().id(), id)) { - tree.getParent() - .getChildren() - .remove(tree); - return true; - } - for (final var c : tree.getChildren()) { - if (this.locationTreeDelete(c, id)) { - return true; - } - } - return false; - } - - @Override - public ObservableList stockView() - { - return this.stockRead; - } - - @Override - public SortedList stockViewSorted() - { - return this.stockSorted; - } - - @Override - public void stockSearchBegin( - final CAStockSearchParameters searchParameters) - { - final var future = - this.clientService.execute( - new CAICommandStockSearchBegin(searchParameters) - ); - - future.thenAccept(response -> { - Platform.runLater(() -> { - final var data = - response.data(); - - final var newItemPage = - new ArrayList<>(data.items()); - - LOG.debug("Received {} stock", newItemPage.size()); - this.stockPages.set( - new CAGPageRange( - (long) data.pageIndex(), - (long) data.pageCount() - ) - ); - this.stock.setAll(newItemPage); - }); - }); - } - - @Override - public Flow.Publisher tabSelectionRequested() - { - return this.tabSelection; - } - - @Override - public void tabSelect( - final CAGTabKind tabKind) - { - this.tabSelection.submit( - Objects.requireNonNull(tabKind, "tabKind") - ); - } } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFactoryMapped.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFactoryMapped.java new file mode 100644 index 0000000..c703c8a --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFactoryMapped.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.jaffirm.core.Postconditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * A controller factory based on an immutable map of controllers. + * + * @param The base type of controllers. + */ + +public final class CAGControllerFactoryMapped + implements CAGControllerFactoryType +{ + private static final Logger LOG = + LoggerFactory.getLogger(CAGControllerFactoryMapped.class); + + private final Map, Supplier> controllers; + + private CAGControllerFactoryMapped( + final Map, Supplier> inControllers) + { + this.controllers = + Objects.requireNonNull(inControllers, "controllers"); + } + + /** + * A controller factory based on an immutable map of controllers. + * + * @param The base type of controllers. + * @param constructors The constructors + * + * @return A controller factory + */ + + @SafeVarargs + public static CAGControllerFactoryType create( + final Map.Entry, Supplier>... constructors) + { + Objects.requireNonNull(constructors, "constructors"); + + return new CAGControllerFactoryMapped<>( + Map.ofEntries(constructors) + ); + } + + @Override + public T call( + final Class clazz) + { + LOG.debug("Resolving controller: {}", clazz); + + final var supplier = this.controllers.get(clazz); + if (supplier == null) { + throw new IllegalStateException( + "No controller available for %s".formatted(clazz) + ); + } + + final var controller = supplier.get(); + Postconditions.checkPostconditionV( + clazz.isInstance(controller), + "Controller %s is an instance of %s", + controller, + clazz + ); + return controller; + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFactoryType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFactoryType.java new file mode 100644 index 0000000..ce7c4ed --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFactoryType.java @@ -0,0 +1,31 @@ +/* + * 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 javafx.util.Callback; + +/** + * The type of controller factories. + * @param The base type of controllers + */ + +public interface CAGControllerFactoryType + extends Callback, T> +{ + +} 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 db8f05e..25f534e 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,54 +17,13 @@ package com.io7m.cardant_gui.ui.internal; -import com.io7m.cardant.model.CAFileID; -import javafx.beans.value.ObservableValue; -import javafx.scene.image.Image; - -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; - /** * The main controller interface. */ public interface CAGControllerType extends CAGControllerAuditType, - CAGControllerFilesType, - CAGControllerItemsType, - CAGControllerStockType, - CAGControllerLocationsType, - CAGControllerTypePackagesType, - CAGControllerViewType + CAGControllerTypePackagesType { - /** - * @return The current transfer status - */ - - ObservableValue transferStatus(); - - /** - * Get an image. - * - * @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 - * @param width The requested width - * @param height The requested height - * - * @return The operation in progress - */ - CompletableFuture imageGet( - CAFileID fileID, - Path file, - Path fileTmp, - long size, - String hashAlgorithm, - String hashValue, - int width, - int height); } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDialogFactoryAbstract.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDialogFactoryAbstract.java index 633d731..6991fd2 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDialogFactoryAbstract.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGDialogFactoryAbstract.java @@ -23,6 +23,8 @@ import javafx.scene.layout.Pane; import javafx.stage.Modality; import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URL; @@ -38,6 +40,9 @@ public abstract class CAGDialogFactoryAbstract implements CAGDialogFactoryType { + private static final Logger LOG = + LoggerFactory.getLogger(CAGDialogFactoryAbstract.class); + private final RPServiceDirectoryType services; private final Class controllerClass; private final String fxmlResource; @@ -82,10 +87,9 @@ protected abstract String createStageTitle( A arguments ); - protected abstract C createController( + protected abstract CAGControllerFactoryType controllerFactory( A arguments, - Stage stage - ); + Stage stage); @Override public final C createDialogForStage( @@ -93,6 +97,7 @@ public final C createDialogForStage( final Stage stage) throws IOException { + Objects.requireNonNull(arguments, "arguments"); Objects.requireNonNull(stage, "stage"); final var xml = @@ -102,9 +107,12 @@ public final C createDialogForStage( final var loader = new FXMLLoader(xml, resources); - loader.setControllerFactory( - clazz -> this.createController(arguments, stage) - ); + final var controllerFactory = + this.controllerFactory(arguments, stage); + + loader.setControllerFactory(param -> { + return controllerFactory.call((Class) param); + }); final Pane pane = loader.load(); CAGCSS.setCSS(pane); @@ -120,6 +128,8 @@ public final C openDialogAndWait( final A arguments) throws IOException { + Objects.requireNonNull(arguments, "arguments"); + final var result = this.createDialog(arguments); result.stage().showAndWait(); return result.view(); @@ -130,6 +140,8 @@ public final CAGViewAndStage createDialog( final A arguments) throws IOException { + Objects.requireNonNull(arguments, "arguments"); + final var stage = new Stage(); final var controller = this.createDialogForStage(arguments, stage); diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventItemUpdated.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventItemUpdated.java new file mode 100644 index 0000000..fa8b1cc --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventItemUpdated.java @@ -0,0 +1,44 @@ +/* + * 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.CAItem; + +import java.util.Objects; + +/** + * An item was updated. + * + * @param item The item + */ + +public record CAGEventItemUpdated( + CAItem item) + implements CAGEventType +{ + /** + * An item was updated. + * + * @param item The item + */ + + public CAGEventItemUpdated + { + Objects.requireNonNull(item, "item"); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventLocationUpdated.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventLocationUpdated.java new file mode 100644 index 0000000..0123445 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventLocationUpdated.java @@ -0,0 +1,44 @@ +/* + * 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.CALocation; + +import java.util.Objects; + +/** + * A location was updated. + * + * @param location The location + */ + +public record CAGEventLocationUpdated( + CALocation location) + implements CAGEventType +{ + /** + * A location was updated. + * + * @param location The location + */ + + public CAGEventLocationUpdated + { + Objects.requireNonNull(location, "location"); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventService.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventService.java new file mode 100644 index 0000000..4fe80db --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventService.java @@ -0,0 +1,76 @@ +/* + * 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 java.util.Objects; +import java.util.concurrent.Flow; +import java.util.concurrent.SubmissionPublisher; + +/** + * The event service. + */ + +public final class CAGEventService + implements CAGEventServiceType +{ + private final SubmissionPublisher events; + + private CAGEventService() + { + this.events = new SubmissionPublisher<>(); + } + + /** + * The event service. + * + * @return The event service + */ + + public static CAGEventServiceType create() + { + return new CAGEventService(); + } + + @Override + public void publish( + final CAGEventType event) + { + this.events.submit(Objects.requireNonNull(event, "event")); + } + + @Override + public Flow.Publisher events() + { + return this.events; + } + + @Override + public String description() + { + return "The event service."; + } + + @Override + public String toString() + { + return String.format( + "[CAGEventService 0x%08x]", + Integer.valueOf(this.hashCode()) + ); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerViewType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventServiceType.java similarity index 74% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerViewType.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventServiceType.java index 7409499..e390d90 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerViewType.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventServiceType.java @@ -17,25 +17,28 @@ package com.io7m.cardant_gui.ui.internal; +import com.io7m.repetoir.core.RPServiceType; + import java.util.concurrent.Flow; /** - * View methods for the controller. + * The event service. */ -public interface CAGControllerViewType +public interface CAGEventServiceType + extends RPServiceType { /** - * @return A publisher of events indicating that a tab was requested for selection + * Publish an event. + * + * @param event The event */ - Flow.Publisher tabSelectionRequested(); + void publish(CAGEventType event); /** - * Request that a tab be selected. - * - * @param tabKind The tab + * @return The event stream */ - void tabSelect(CAGTabKind tabKind); + Flow.Publisher events(); } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventType.java new file mode 100644 index 0000000..00eb289 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGEventType.java @@ -0,0 +1,29 @@ +/* + * 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; + +/** + * The type of events. + */ + +public sealed interface CAGEventType + permits CAGEventItemUpdated, + CAGEventLocationUpdated +{ + +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileCreateView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileCreateView.java index a819f9e..0c13f40 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileCreateView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileCreateView.java @@ -55,12 +55,12 @@ public final class CAGFileCreateView { private static final Tika TIKA = new Tika(); - private final CAGControllerType controller; private final CAGFileChoosersType choosers; private final Stage stage; private final ExecutorService executor; private final SimpleObjectProperty fileDetails; private final CAGDatabaseType database; + private final CAGFileTransferControllerType transfers; @FXML private TextField file; @FXML private TextField description; @@ -81,12 +81,12 @@ public CAGFileCreateView( final RPServiceDirectoryType services, final Stage inStage) { - this.controller = - services.requireService(CAGControllerType.class); this.choosers = services.requireService(CAGFileChoosersType.class); this.database = services.requireService(CAGDatabaseType.class); + this.transfers = + services.requireService(CAGFileTransferControllerType.class); this.stage = Objects.requireNonNull(inStage, "stage"); @@ -148,7 +148,7 @@ private void onUploadSelected() { final var details = this.fileDetails.get(); - this.controller.fileUpload( + this.transfers.fileUpload( new CAFileID(UUID.randomUUID()), details.file, details.mediaType, diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFileListView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileListView.java similarity index 86% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFileListView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileListView.java index 23048e1..bc73ee4 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFileListView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileListView.java @@ -40,25 +40,29 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Path; +import java.util.Objects; import java.util.ResourceBundle; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILESEARCH_PAGEOF; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILES_TRANSFER_DOWNLOADING; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILES_TRANSFER_IDLE; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILES_TRANSFER_UPLOADING; import static com.io7m.cardant_gui.ui.internal.CAGTransferStatusType.Idle.IDLE; +import static com.io7m.cardant_gui.ui.internal.CAGUnit.UNIT; /** - * The main file list view. + * A file list view. */ -public final class CAGMainFileListView +public final class CAGFileListView implements CAGViewType { private final CAGStringsType strings; - private final CAGControllerType controller; private final CAGFileViewDialogs dialogs; private final CAGFileChoosersType choosers; private final CAGDatabaseType database; + private final CAGFileTransferControllerType transfers; + private CAGFileSearchControllerType search; @FXML private Label resultsLabel; @FXML private Button fileDownload; @@ -71,26 +75,44 @@ public final class CAGMainFileListView @FXML private ListView files; /** - * The main file list view. + * A file list view. * * @param services The service directory */ - public CAGMainFileListView( + public CAGFileListView( final RPServiceDirectoryType services) { this.strings = services.requireService(CAGStringsType.class); - this.controller = - services.requireService(CAGControllerType.class); this.dialogs = services.requireService(CAGFileViewDialogs.class); this.choosers = services.requireService(CAGFileChoosersType.class); + this.transfers = + services.requireService(CAGFileTransferControllerType.class); this.database = services.requireService(CAGDatabaseType.class); } + /** + * Set the controllers. + * + * @param inSearch The controller + */ + + public void setControllers( + final CAGFileSearchControllerType inSearch) + { + this.search = + Objects.requireNonNull(inSearch, "search"); + + this.files + .setItems(this.search.filesView()); + this.search.filesView() + .addListener(this::onFilesViewChanged); + } + @Override public void initialize( final URL url, @@ -102,18 +124,16 @@ public void initialize( this.fileDownload.setDisable(true); this.fileRemove.setDisable(true); - this.files.setCellFactory(new CAGFileCellFactory(this.strings)); - this.files.setItems(this.controller.filesView()); + this.files.setCellFactory( + new CAGFileCellFactory(this.strings)); + this.files.getSelectionModel() .setSelectionMode(SelectionMode.SINGLE); this.files.getSelectionModel() .getSelectedItems() .addListener((ListChangeListener) this::onFileSelectionChanged); - this.controller.filesView() - .addListener(this::onFilesViewChanged); - - this.controller.transferStatus() + this.transfers.transferStatus() .addListener((observable, oldValue, newValue) -> { this.onTransferStatusChanged(newValue); }); @@ -127,11 +147,11 @@ private void onFilesViewChanged( final var size = c.getList().size(); if (size > 0) { final var range = - this.controller.filePages().getValue(); + this.search.filePages().getValue(); this.resultsLabel.setText( this.strings.format( - CAGStringConstants.CARDANT_FILESEARCH_PAGEOF, + CARDANT_FILESEARCH_PAGEOF, Long.valueOf(range.pageIndex()), Long.valueOf(range.pageCount()) ) @@ -149,11 +169,13 @@ private void onFileSelectionChanged( this.fileAdd.setDisable(false); this.fileDownload.setDisable(true); this.fileRemove.setDisable(true); + this.search.fileSelectNone(); return; } this.fileDownload.setDisable(false); this.fileRemove.setDisable(false); + this.search.fileSelect(selected.get(0)); } private void onTransferStatusChanged( @@ -166,6 +188,7 @@ private void onTransferStatusChanged( ); this.transferRate.setText(""); this.transferSize.setText(""); + this.transferProgress.setVisible(false); this.transferProgress.setProgress(0.0); } case final Uploading uploading -> { @@ -174,6 +197,7 @@ private void onTransferStatusChanged( ); this.transferRate.setText(formatRate(uploading.statistics())); this.transferSize.setText(formatSize(uploading.statistics())); + this.transferProgress.setVisible(true); this.transferProgress.setProgress( uploading.statistics().percentNormalized() ); @@ -184,6 +208,7 @@ private void onTransferStatusChanged( ); this.transferRate.setText(formatRate(downloading.statistics())); this.transferSize.setText(formatSize(downloading.statistics())); + this.transferProgress.setVisible(true); this.transferProgress.setProgress( downloading.statistics().percentNormalized() ); @@ -226,7 +251,7 @@ private static String formatRate( private void onFileAddSelected() throws IOException { - this.dialogs.openDialogAndWait(null); + this.dialogs.openDialogAndWait(UNIT); } @FXML @@ -264,7 +289,7 @@ private void onFileDownloadSelected() this.files.getSelectionModel() .getSelectedItem(); - this.controller.fileDownload( + this.transfers.fileDownload( fileValue.id(), file, fileTmp, diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchController.java new file mode 100644 index 0000000..359c60c --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchController.java @@ -0,0 +1,129 @@ +/* + * 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.CAFileSearchParameters; +import com.io7m.cardant.model.CAFileType.CAFileWithoutData; +import com.io7m.cardant.protocol.inventory.CAICommandFileSearchBegin; +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import java.util.Objects; +import java.util.Optional; + +/** + * A file search controller. + */ + +public final class CAGFileSearchController + implements CAGFileSearchControllerType +{ + private final ObservableList files; + private final ObservableList filesRead; + private final SimpleObjectProperty pages; + private final CAGClientServiceType client; + private final SimpleObjectProperty> fileSelected; + + private CAGFileSearchController( + final CAGClientServiceType inClient) + { + this.client = + Objects.requireNonNull(inClient, "client"); + this.files = + FXCollections.observableArrayList(); + this.filesRead = + FXCollections.unmodifiableObservableList(this.files); + this.fileSelected = + new SimpleObjectProperty<>(Optional.empty()); + this.pages = + new SimpleObjectProperty<>(CAGPageRange.zero()); + } + + /** + * @param client The client + * + * @return A file search controller. + */ + + public static CAGFileSearchControllerType create( + final CAGClientServiceType client) + { + final var controller = new CAGFileSearchController(client); + client.status().subscribe((oldStatus, newStatus) -> { + controller.onClientStatusChanged(); + }); + return controller; + } + + private void onClientStatusChanged() + { + this.files.clear(); + this.pages.set(CAGPageRange.zero()); + } + + @Override + public void fileSearchBegin( + final CAFileSearchParameters searchParameters) + { + this.client.execute(new CAICommandFileSearchBegin(searchParameters)) + .thenAccept(response -> { + Platform.runLater(() -> { + final var data = response.data(); + this.files.setAll(data.items()); + this.pages.set(new CAGPageRange( + (long) data.pageIndex(), + (long) data.pageCount() + )); + }); + }); + } + + @Override + public ObservableList filesView() + { + return this.filesRead; + } + + @Override + public ObservableValue filePages() + { + return this.pages; + } + + @Override + public ObservableValue> fileSelected() + { + return this.fileSelected; + } + + @Override + public void fileSelect( + final CAFileWithoutData file) + { + this.fileSelected.set(Optional.of(file)); + } + + @Override + public void fileSelectNone() + { + this.fileSelected.set(Optional.empty()); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchControllerType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchControllerType.java new file mode 100644 index 0000000..d367988 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchControllerType.java @@ -0,0 +1,73 @@ +/* + * 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.CAFileSearchParameters; +import com.io7m.cardant.model.CAFileType.CAFileWithoutData; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; + +import java.util.Optional; + +/** + * A file search controller. + */ + +public interface CAGFileSearchControllerType +{ + /** + * Start searching for files. + * + * @param searchParameters The parameters + */ + + void fileSearchBegin( + CAFileSearchParameters searchParameters); + + /** + * @return The files for the current search query + */ + + ObservableList filesView(); + + /** + * @return The page range for the current file search query + */ + + ObservableValue filePages(); + + /** + * @return The currently selected file + */ + + ObservableValue> fileSelected(); + + /** + * Select a file. + * + * @param file The file + */ + + void fileSelect(CAFileWithoutData file); + + /** + * Select nothing. + */ + + void fileSelectNone(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFileSearchView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchView.java similarity index 94% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFileSearchView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchView.java index 7573a1b..9675eb4 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFileSearchView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSearchView.java @@ -40,6 +40,7 @@ import javafx.scene.control.TitledPane; import java.net.URL; +import java.util.Objects; import java.util.ResourceBundle; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_CANCEL; @@ -51,14 +52,13 @@ import static javafx.scene.control.ButtonBar.ButtonData.OK_DONE; /** - * The main file search view. + * A file search view. */ -public final class CAGMainFileSearchView +public final class CAGFileSearchView implements CAGViewType { private final CAGStringsType strings; - private final CAGControllerType controller; @FXML private ChoiceBox fileDescriptionMatch; @FXML private TextArea fileDescription; @@ -69,19 +69,19 @@ public final class CAGMainFileSearchView @FXML private Accordion accordion; @FXML private TitledPane basicParameters; + private CAGFileSearchControllerType search; + /** - * The main file search view. + * A file search view. * * @param services The service directory */ - public CAGMainFileSearchView( + public CAGFileSearchView( final RPServiceDirectoryType services) { this.strings = services.requireService(CAGStringsType.class); - this.controller = - services.requireService(CAGControllerType.class); } private void clearParameters() @@ -100,6 +100,19 @@ private void clearParameters() .setValue(Long.valueOf(Long.MAX_VALUE)); } + /** + * Set the controllers. + * + * @param inSearch The search controller + */ + + public void setControllers( + final CAGFileSearchControllerType inSearch) + { + this.search = + Objects.requireNonNull(inSearch, "search"); + } + @Override public void initialize( final URL url, @@ -166,7 +179,7 @@ private void onDescriptionMatchChanged( @FXML private void onSearchSelected() { - this.controller.fileSearchBegin( + this.search.fileSearchBegin( new CAFileSearchParameters( this.descriptionMatch(), this.mediaTypeMatch(), diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSelectDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSelectDialogs.java new file mode 100644 index 0000000..3ecd5db --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSelectDialogs.java @@ -0,0 +1,97 @@ +/* + * 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; + +import java.util.Map; +import java.util.Objects; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILES_SELECTTITLE; + +/** + * A file selection dialog. + */ + +public final class CAGFileSelectDialogs + extends CAGDialogFactoryAbstract +{ + /** + * A file selection dialog. + * + * @param services The service directory + */ + + public CAGFileSelectDialogs( + final RPServiceDirectoryType services) + { + super( + CAGFileSelectView.class, + "/com/io7m/cardant_gui/ui/internal/fileSelect.fxml", + services, + Modality.NONE + ); + } + + @Override + protected String createStageTitle( + final CAGFileSearchControllerType arguments) + { + Objects.requireNonNull(arguments, "arguments"); + + return this.strings().format(CARDANT_FILES_SELECTTITLE); + } + + @Override + protected CAGControllerFactoryType controllerFactory( + final CAGFileSearchControllerType arguments, + final Stage stage) + { + return CAGControllerFactoryMapped.create( + Map.entry( + CAGFileSelectView.class, + () -> new CAGFileSelectView(stage, arguments) + ), + Map.entry( + CAGFileListView.class, + () -> new CAGFileListView(this.services()) + ), + Map.entry( + CAGFileSearchView.class, + () -> new CAGFileSearchView(this.services()) + ) + ); + } + + @Override + public String description() + { + return "File selection dialogs."; + } + + @Override + public String toString() + { + return String.format( + "[CAGFileSearchDialogs 0x%08x]", + Integer.valueOf(this.hashCode()) + ); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSelectView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSelectView.java new file mode 100644 index 0000000..aac6311 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileSelectView.java @@ -0,0 +1,90 @@ +/* + * 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 javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * A file selection view. + */ + +public final class CAGFileSelectView + implements CAGViewType +{ + private final Stage stage; + private final CAGFileSearchControllerType controller; + + @FXML private Node fileList; + @FXML private CAGFileListView fileListController; + @FXML private Node fileSearch; + @FXML private CAGFileSearchView fileSearchController; + @FXML private Button select; + + /** + * A file selection view. + * + * @param inStage The stage + * @param inController The tree controller + */ + + public CAGFileSelectView( + final Stage inStage, + final CAGFileSearchControllerType inController) + { + this.stage = + Objects.requireNonNull(inStage, "stage"); + this.controller = + Objects.requireNonNull(inController, "inController"); + } + + @Override + public void initialize( + final URL file, + final ResourceBundle resources) + { + this.fileListController + .setControllers(this.controller); + this.fileSearchController + .setControllers(this.controller); + + this.controller.fileSelected() + .addListener((observable, oldValue, newValue) -> { + this.select.setDisable(newValue.isEmpty()); + }); + } + + @FXML + private void onCancelSelected() + { + this.controller.fileSelectNone(); + this.stage.close(); + } + + @FXML + private void onSelectSelected() + { + this.stage.close(); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileTransferController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileTransferController.java new file mode 100644 index 0000000..604191f --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileTransferController.java @@ -0,0 +1,146 @@ +/* + * 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.client.api.CAClientTransferStatistics; +import com.io7m.cardant.model.CAFileID; +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; + +import java.nio.file.Path; +import java.util.Objects; + +import static com.io7m.cardant_gui.ui.internal.CAGTransferStatusType.Idle.IDLE; + +/** + * A file transfer controller. + */ + +public final class CAGFileTransferController + implements CAGFileTransferControllerType +{ + private final CAGClientServiceType client; + private final SimpleObjectProperty transferStatus; + + /** + * A file transfer controller. + * + * @param inClient The client + * + * @return The controller + */ + + public static CAGFileTransferControllerType create( + final CAGClientServiceType inClient) + { + return new CAGFileTransferController(inClient); + } + + private CAGFileTransferController( + final CAGClientServiceType inClient) + { + this.client = + Objects.requireNonNull(inClient, "client"); + this.transferStatus = + new SimpleObjectProperty<>(IDLE); + } + + @Override + public void fileUpload( + final CAFileID fileID, + final Path file, + final String contentType, + final String description) + { + final var future = + this.client.fileUpload( + fileID, + file, + contentType, + description, + statistics -> { + Platform.runLater(() -> this.onUploadStatisticsChanged(statistics)); + } + ); + + future.thenAccept(response -> { + Platform.runLater(() -> this.transferStatus.set(IDLE)); + }); + } + + @Override + public void fileDownload( + final CAFileID fileID, + final Path file, + final Path fileTmp, + final long size, + final String hashAlgorithm, + final String hashValue) + { + final var future = + this.client.fileDownload( + fileID, + file, + fileTmp, + size, + hashAlgorithm, + hashValue, + statistics -> { + Platform.runLater(() -> this.onDownloadStatisticsChanged(statistics)); + } + ); + + future.thenAccept(response -> { + Platform.runLater(() -> this.transferStatus.set(IDLE)); + }); + } + + private void onDownloadStatisticsChanged( + final CAClientTransferStatistics statistics) + { + this.transferStatus.set(new CAGTransferStatusType.Downloading(statistics)); + } + + private void onUploadStatisticsChanged( + final CAClientTransferStatistics statistics) + { + this.transferStatus.set(new CAGTransferStatusType.Uploading(statistics)); + } + + @Override + public ObservableValue transferStatus() + { + return this.transferStatus; + } + + @Override + public String description() + { + return "File transfer service."; + } + + @Override + public String toString() + { + return String.format( + "[CAGFileTransferController 0x%08x]", + Integer.valueOf(this.hashCode()) + ); + } +} 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/CAGFileTransferControllerType.java similarity index 73% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerFilesType.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileTransferControllerType.java index a199327..ed6ec92 100644 --- 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/CAGFileTransferControllerType.java @@ -18,36 +18,18 @@ 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. + * The file transfer controller. */ -public interface CAGControllerFilesType +public interface CAGFileTransferControllerType 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. * @@ -81,11 +63,12 @@ void fileDownload( Path fileTmp, long size, String hashAlgorithm, - String hashValue); + String hashValue + ); /** - * @return The page range for the current file search query + * @return The current transfer status */ - ObservableValue filePages(); + ObservableValue transferStatus(); } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileViewDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileViewDialogs.java index 0b9f7b0..8ec2cb8 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileViewDialogs.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGFileViewDialogs.java @@ -21,12 +21,17 @@ import javafx.stage.Modality; import javafx.stage.Stage; +import java.util.Map; +import java.util.Objects; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_FILECREATE_OPENFILE; + /** * A file creation dialog. */ public final class CAGFileViewDialogs - extends CAGDialogFactoryAbstract + extends CAGDialogFactoryAbstract { /** * A file creation dialog. @@ -47,17 +52,27 @@ public CAGFileViewDialogs( @Override protected String createStageTitle( - final Void arguments) + final CAGUnit arguments) { - return this.strings().format(CAGStringConstants.CARDANT_FILECREATE_OPENFILE); + Objects.requireNonNull(arguments, "arguments"); + + return this.strings().format(CARDANT_FILECREATE_OPENFILE); } @Override - protected CAGFileCreateView createController( - final Void arguments, + protected CAGControllerFactoryType controllerFactory( + final CAGUnit arguments, final Stage stage) { - return new CAGFileCreateView(this.services(), stage); + Objects.requireNonNull(arguments, "arguments"); + Objects.requireNonNull(stage, "stage"); + + return CAGControllerFactoryMapped.create( + Map.entry( + CAGFileCreateView.class, + () -> new CAGFileCreateView(this.services(), stage) + ) + ); } @Override diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogArguments.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogArguments.java new file mode 100644 index 0000000..624634c --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogArguments.java @@ -0,0 +1,47 @@ +/* + * 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.CAItemID; + +import java.util.Objects; + +/** + * Dialog arguments. + * + * @param detailsController The details controller + * @param item The item + */ + +public record CAGItemAttachmentAddDialogArguments( + CAGItemDetailsControllerType detailsController, + CAItemID item) +{ + /** + * Dialog arguments. + * + * @param detailsController The details controller + * @param item The item + */ + + public CAGItemAttachmentAddDialogArguments + { + Objects.requireNonNull(item, "item"); + Objects.requireNonNull(detailsController, "detailsController"); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogs.java index f6bef54..bc486c2 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogs.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddDialogs.java @@ -21,12 +21,17 @@ import javafx.stage.Modality; import javafx.stage.Stage; +import java.util.Map; +import java.util.Objects; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ATTACHMENTADD_TITLE; + /** * An attachment addition dialog. */ public final class CAGItemAttachmentAddDialogs - extends CAGDialogFactoryAbstract + extends CAGDialogFactoryAbstract { /** * An attachment addition dialog. @@ -47,17 +52,27 @@ public CAGItemAttachmentAddDialogs( @Override protected String createStageTitle( - final Void arguments) + final CAGItemAttachmentAddDialogArguments arguments) { - return this.strings().format(CAGStringConstants.CARDANT_ATTACHMENTADD_TITLE); + Objects.requireNonNull(arguments, "arguments"); + + return this.strings().format(CARDANT_ATTACHMENTADD_TITLE); } @Override - protected CAGItemAttachmentAddView createController( - final Void arguments, + protected CAGControllerFactoryType controllerFactory( + final CAGItemAttachmentAddDialogArguments arguments, final Stage stage) { - return new CAGItemAttachmentAddView(stage, this.services()); + Objects.requireNonNull(arguments, "arguments"); + Objects.requireNonNull(stage, "stage"); + + return CAGControllerFactoryMapped.create( + Map.entry( + CAGItemAttachmentAddView.class, + () -> new CAGItemAttachmentAddView(stage, this.services(), arguments) + ) + ); } @Override diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddView.java index 1498b92..5088f0c 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemAttachmentAddView.java @@ -18,25 +18,23 @@ package com.io7m.cardant_gui.ui.internal; import com.io7m.cardant.model.CAFileID; -import com.io7m.cardant.model.CAFileType; import com.io7m.cardant.model.CAItemID; -import com.io7m.cardant.model.CAItemSummary; import com.io7m.cardant.protocol.inventory.CAICommandItemAttachmentAdd; import com.io7m.repetoir.core.RPServiceDirectoryType; -import javafx.collections.ListChangeListener; import javafx.fxml.FXML; +import javafx.scene.control.Alert; 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.io.IOException; import java.net.URL; import java.util.Objects; import java.util.ResourceBundle; import java.util.UUID; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ERROR_RELATIONEMPTY; + /** * The attachment addition view. */ @@ -46,32 +44,45 @@ public final class CAGItemAttachmentAddView { private final Stage stage; private final CAGStringsType strings; - private final CAGControllerType controller; + private final CAGItemDetailsControllerType itemController; + private final CAItemID itemId; + private final CAGFileSelectDialogs fileSelectDialogs; + private final CAGClientServiceType client; + private final CAGFileSearchControllerType fileSearchController; @FXML private Button addButton; - @FXML private Label resultsLabel; @FXML private TextField itemField; @FXML private TextField fileField; @FXML private TextField relationField; - @FXML private ListView files; /** * The attachment addition view. * - * @param inStage The stage - * @param services The services + * @param inStage The stage + * @param services The services + * @param arguments The arguments */ public CAGItemAttachmentAddView( final Stage inStage, - final RPServiceDirectoryType services) + final RPServiceDirectoryType services, + final CAGItemAttachmentAddDialogArguments arguments) { this.stage = Objects.requireNonNull(inStage, "stage"); this.strings = services.requireService(CAGStringsType.class); - this.controller = - services.requireService(CAGControllerType.class); + this.fileSelectDialogs = + services.requireService(CAGFileSelectDialogs.class); + this.client = + services.requireService(CAGClientServiceType.class); + + this.fileSearchController = + CAGFileSearchController.create(this.client); + this.itemController = + arguments.detailsController(); + this.itemId = + arguments.item(); } @Override @@ -79,59 +90,23 @@ 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.itemSelected() - .addListener((observable, oldValue, newValue) -> { - this.onItemSelectionChanged(newValue); - }); - - this.onItemSelectionChanged( - this.controller.itemSelected().getValue() - ); + this.itemField.setText(this.itemId.displayId()); - this.itemField.textProperty() - .addListener(observable -> { - this.validate(); - }); - - this.fileField.textProperty() - .addListener(observable -> { - this.validate(); + this.fileSearchController.fileSelected() + .addListener((observable, oldValue, newValue) -> { + this.addButton.setDisable(newValue.isEmpty()); }); - this.relationField.textProperty() - .addListener(observable -> { - this.validate(); + this.fileSearchController.fileSelected() + .addListener((observable, oldValue, newValue) -> { + if (newValue.isPresent()) { + this.fileField.setText(newValue.get().id().displayId()); + } else { + this.fileField.setText(""); + } }); } - private void validate() - { - this.addButton.setDisable(true); - - try { - this.createCommand(); - this.addButton.setDisable(false); - } catch (final Exception e) { - // Ignored - this.addButton.setDisable(true); - } - } - private CAICommandItemAttachmentAdd createCommand() { final var relationText = @@ -142,6 +117,13 @@ private CAICommandItemAttachmentAdd createCommand() this.fileField.getText().trim(); if (relationText.isEmpty()) { + final var alert = + new Alert( + Alert.AlertType.ERROR, + this.strings.format(CARDANT_ERROR_RELATIONEMPTY) + ); + + alert.showAndWait(); throw new IllegalArgumentException(); } @@ -152,70 +134,23 @@ private CAICommandItemAttachmentAdd createCommand() ); } - private void onItemSelectionChanged( - final CAItemSummary item) - { - if (item == null) { - this.itemField.setText(""); - return; - } - - this.itemField.setText(item.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.itemAttachmentAdd(this.createCommand()); + this.itemController.itemAttachmentAdd(this.createCommand()); this.stage.close(); } @FXML - private void onCancelSelected() - { - this.stage.close(); - } - - @FXML - private void onPageNextSelected() + private void onFileSelectSelected() + throws IOException { - + this.fileSelectDialogs.openDialogAndWait(this.fileSearchController); } @FXML - private void onPagePreviousSelected() + private void onCancelSelected() { - + this.stage.close(); } } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemDetailsController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemDetailsController.java new file mode 100644 index 0000000..d0b74fb --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemDetailsController.java @@ -0,0 +1,129 @@ +/* + * 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.CAItemID; +import com.io7m.cardant.model.CAItemSummary; +import com.io7m.cardant.protocol.inventory.CAICommandItemAttachmentAdd; +import com.io7m.cardant.protocol.inventory.CAICommandItemGet; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import java.util.Objects; + +/** + * An item details controller. + */ + +public final class CAGItemDetailsController + implements CAGItemDetailsControllerType +{ + private final CAGClientServiceType client; + private final CAGItemModelType itemSelected; + private final ObservableList items; + private final SortedList itemsSorted; + private final ObservableList itemsRead; + + /** + * Create a controller. + * + * @param clients The client service + * + * @return A controller + */ + + public static CAGItemDetailsControllerType create( + final CAGClientServiceType clients) + { + final var controller = new CAGItemDetailsController(clients); + clients.status().subscribe((oldStatus, newStatus) -> { + controller.onClientStatusChanged(); + }); + return controller; + } + + private void onClientStatusChanged() + { + this.items.clear(); + this.itemSelected.clear(); + } + + private CAGItemDetailsController( + final CAGClientServiceType inClientService) + { + this.client = + Objects.requireNonNull(inClientService, "inClientService"); + + this.itemSelected = + CAGItemModel.create(); + this.items = + FXCollections.observableArrayList(); + this.itemsSorted = + new SortedList<>(this.items); + this.itemsRead = + FXCollections.unmodifiableObservableList(this.items); + } + + @Override + public ObservableList itemsView() + { + return this.itemsRead; + } + + @Override + public SortedList itemsViewSorted() + { + return this.itemsSorted; + } + + @Override + public void itemSelect( + final CAItemID id) + { + final var future = + this.client.execute(new CAICommandItemGet(id)); + + future.thenAccept(response -> { + Platform.runLater(() -> { + this.itemSelected.update(response.data()); + }); + }); + } + + @Override + public void itemSelectNothing() + { + this.itemSelected.clear(); + } + + @Override + public void itemAttachmentAdd( + final CAICommandItemAttachmentAdd command) + { + this.client.execute(command); + this.itemSelect(command.item()); + } + + @Override + public CAGItemModelReadableType itemSelected() + { + return this.itemSelected; + } +} 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/CAGItemDetailsControllerType.java similarity index 59% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerItemsType.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemDetailsControllerType.java index eb322ed..0cc9fd3 100644 --- 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/CAGItemDetailsControllerType.java @@ -17,24 +17,17 @@ 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.model.CATypeRecordIdentifier; 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. + * Controller methods for the item details view. */ -public interface CAGControllerItemsType - extends RPServiceType +public interface CAGItemDetailsControllerType { /** * @return The items for the current search query @@ -49,27 +42,12 @@ public interface CAGControllerItemsType SortedList itemsViewSorted(); /** - * Start searching for items. - * - * @param searchParameters The search parameters - */ - - void itemSearchBegin( - CAItemSearchParameters searchParameters); - - /** - * Fetch an item. + * Select and fetch an item. * * @param id The item ID */ - void itemGet(CAItemID id); - - /** - * @return The metadata for the selected item - */ - - SortedList itemSelectedMetadata(); + void itemSelect(CAItemID id); /** * Clear the current item selection. @@ -77,12 +55,6 @@ void itemSearchBegin( void itemSelectNothing(); - /** - * @return The attachments for the selected item - */ - - ObservableList itemSelectedAttachments(); - /** * Add an attachment. * @@ -92,21 +64,9 @@ void itemSearchBegin( void itemAttachmentAdd( CAICommandItemAttachmentAdd command); - /** - * @return The page range for the current item search query - */ - - ObservableValue itemPages(); - /** * @return The currently selected item */ - ObservableValue itemSelected(); - - /** - * @return The currently assigned types - */ - - SortedList itemSelectedTypes(); + CAGItemModelReadableType itemSelected(); } 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/CAGItemDetailsView.java similarity index 86% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemDetailsView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemDetailsView.java index 3ddd9be..e538ce1 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/CAGItemDetailsView.java @@ -44,21 +44,23 @@ import java.net.URL; import java.nio.file.Files; import java.util.Objects; +import java.util.Optional; import java.util.ResourceBundle; /** * The main item details view. */ -public final class CAGMainItemDetailsView +public final class CAGItemDetailsView implements CAGViewType { private static final Logger LOG = - LoggerFactory.getLogger(CAGMainItemDetailsView.class); + LoggerFactory.getLogger(CAGItemDetailsView.class); - private final CAGControllerType controller; private final CAGStringsType strings; private final CAGItemAttachmentAddDialogs attachmentAddDialogs; + private final CAGClientServiceType client; + private CAGItemDetailsControllerType itemDetailsController; private CAGViewAndStage attachmentAddDialog; @FXML private TabPane mainItemDetails; @@ -73,11 +75,9 @@ public final class CAGMainItemDetailsView @FXML private Button attachmentRemove; @FXML private Button typeAssign; @FXML private Button typeUnassign; - @FXML private TableView types; @FXML private TableColumn typesColPkg; @FXML private TableColumn typesColType; - @FXML private TableView meta; @FXML private TableColumn metaColPkg; @FXML private TableColumn metaColType; @@ -90,17 +90,55 @@ public final class CAGMainItemDetailsView * @param services The service directory */ - public CAGMainItemDetailsView( + public CAGItemDetailsView( final RPServiceDirectoryType services) { - this.controller = - services.requireService(CAGControllerType.class); + this.client = + services.requireService(CAGClientServiceType.class); this.strings = services.requireService(CAGStringsType.class); this.attachmentAddDialogs = services.requireService(CAGItemAttachmentAddDialogs.class); } + /** + * Set the controllers. + * + * @param newController The controller + */ + + public void setControllers( + final CAGItemDetailsControllerType newController) + { + this.itemDetailsController = + Objects.requireNonNull(newController, "newController"); + + final var itemSelected = + this.itemDetailsController.itemSelected(); + + itemSelected.metadata() + .comparatorProperty() + .bind(this.meta.comparatorProperty()); + + this.meta.setItems(itemSelected.metadata()); + this.types.setItems(itemSelected.types()); + + this.attachments.setCellFactory( + new CAGItemAttachmentCellFactory(this.strings) + ); + this.attachments.setItems(itemSelected.attachments()); + + itemSelected.summary() + .addListener((observable, oldValue, newValue) -> { + this.itemSelectionChanged(newValue); + }); + + itemSelected.attachments() + .addListener((ListChangeListener) c -> { + this.loadThumbnail(); + }); + } + @Override public void initialize( final URL url, @@ -161,35 +199,6 @@ public void initialize( ); }); - this.controller.itemSelectedMetadata() - .comparatorProperty() - .bind(this.meta.comparatorProperty()); - - this.meta.setItems( - this.controller.itemSelectedMetadata() - ); - - this.types.setItems( - this.controller.itemSelectedTypes() - ); - - this.attachments.setCellFactory( - new CAGItemAttachmentCellFactory(this.strings) - ); - this.attachments.setItems( - this.controller.itemSelectedAttachments() - ); - - this.controller.itemSelected() - .addListener((observable, oldValue, newValue) -> { - this.itemSelectionChanged(newValue); - }); - - this.controller.itemSelectedAttachments() - .addListener((ListChangeListener) c -> { - this.loadThumbnail(); - }); - this.types.getSelectionModel() .getSelectedItems() .addListener((ListChangeListener) @@ -210,9 +219,9 @@ private void itemTypeSelectionChanged( } private void itemSelectionChanged( - final CAItemSummary newValue) + final Optional newOpt) { - if (newValue == null) { + if (newOpt.isEmpty()) { this.mainItemDetails.setDisable(true); this.idField.setText(""); this.nameField.setText(""); @@ -220,6 +229,7 @@ private void itemSelectionChanged( return; } + final var newValue = newOpt.orElseThrow(); this.mainItemDetails.setDisable(false); this.idField.setText(newValue.id().toString()); this.nameField.setText(newValue.name()); @@ -268,7 +278,7 @@ private void loadThumbnailFromAttachment( final var hashValue = attachmentFile.hashValue(); - this.controller.imageGet( + this.client.imageGet( fileID, file, fileTmp, @@ -308,7 +318,15 @@ private void onAttachmentAddSelected() { if (this.attachmentAddDialog == null) { this.attachmentAddDialog = - this.attachmentAddDialogs.createDialog(null); + this.attachmentAddDialogs.createDialog( + new CAGItemAttachmentAddDialogArguments( + this.itemDetailsController, this.itemDetailsController.itemSelected() + .summary() + .getValue() + .orElseThrow() + .id() + ) + ); } this.attachmentAddDialog.stage() diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModel.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModel.java new file mode 100644 index 0000000..9965fc3 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModel.java @@ -0,0 +1,132 @@ +/* + * 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.CAItem; +import com.io7m.cardant.model.CAItemSummary; +import com.io7m.cardant.model.CAMetadataType; +import com.io7m.cardant.model.CATypeRecordIdentifier; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import java.util.Comparator; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * An observable item. + */ + +public final class CAGItemModel implements CAGItemModelType +{ + private final SimpleObjectProperty> summary; + private final ObservableList metadata; + private final SortedList metadataSorted; + private final ObservableList attachments; + private final ObservableList types; + private final SortedList typesSorted; + private final ObservableList attachmentsRead; + + private CAGItemModel() + { + this.summary = + new SimpleObjectProperty<>(Optional.empty()); + this.metadata = + FXCollections.observableArrayList(); + this.metadataSorted = + new SortedList<>(this.metadata); + this.attachments = + FXCollections.observableArrayList(); + this.attachmentsRead = + FXCollections.unmodifiableObservableList(this.attachments); + this.types = + FXCollections.observableArrayList(); + this.typesSorted = + new SortedList<>(this.types); + } + + /** + * @return An observable item + */ + + public static CAGItemModelType create() + { + return new CAGItemModel(); + } + + @Override + public ObservableValue> summary() + { + return this.summary; + } + + @Override + public SortedList metadata() + { + return this.metadataSorted; + } + + @Override + public ObservableList attachments() + { + return this.attachmentsRead; + } + + @Override + public SortedList types() + { + return this.typesSorted; + } + + @Override + public void update( + final CAItem item) + { + this.summary.set(Optional.of(item.summary())); + + this.metadata.setAll( + item.metadata() + .values() + .stream() + .toList() + ); + + this.attachments.setAll( + item.attachments() + .values() + .stream() + .sorted(Comparator.comparing(o -> o.key().fileID())) + .collect(Collectors.toList()) + ); + + this.types.setAll(item.types()); + } + + @Override + public void clear() + { + this.summary.set(Optional.empty()); + this.metadata.clear(); + this.attachments.clear(); + this.types.clear(); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModelReadableType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModelReadableType.java new file mode 100644 index 0000000..056523b --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModelReadableType.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.CAAttachment; +import com.io7m.cardant.model.CAItemSummary; +import com.io7m.cardant.model.CAMetadataType; +import com.io7m.cardant.model.CATypeRecordIdentifier; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import java.util.Optional; + +/** + * An observable item. + */ + +public interface CAGItemModelReadableType +{ + /** + * @return The summary of the item, if the item exists + */ + + ObservableValue> summary(); + + /** + * @return The metadata for the item + */ + + SortedList metadata(); + + /** + * @return The attachments for the item + */ + + ObservableList attachments(); + + /** + * @return The currently assigned types + */ + + SortedList types(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGTabKind.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModelType.java similarity index 74% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGTabKind.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModelType.java index 113ec8c..305d874 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGTabKind.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemModelType.java @@ -17,45 +17,25 @@ package com.io7m.cardant_gui.ui.internal; +import com.io7m.cardant.model.CAItem; + /** - * The application tabs. + * An observable item. */ -public enum CAGTabKind +public interface CAGItemModelType extends CAGItemModelReadableType { /** - * The items. - */ - - TAB_ITEMS, - - /** - * The locations. - */ - - TAB_LOCATIONS, - - /** - * The stock. - */ - - TAB_STOCK, - - /** - * The files. - */ - - TAB_FILES, - - /** - * The type packages. + * Set all the item values. + * + * @param item The item */ - TAB_TYPE_PACKAGES, + void update(CAItem item); /** - * The audit log. + * Clear the item. */ - TAB_AUDIT + void clear(); } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchController.java new file mode 100644 index 0000000..3e6b144 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchController.java @@ -0,0 +1,115 @@ +/* + * 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.CAItemSearchParameters; +import com.io7m.cardant.model.CAItemSummary; +import com.io7m.cardant.protocol.inventory.CAICommandItemSearchBegin; +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import java.util.Objects; + +/** + * An item search controller. + */ + +public final class CAGItemSearchController + implements CAGItemSearchControllerType +{ + private final ObservableList itemsView; + private final SortedList itemsViewSorted; + private final SimpleObjectProperty itemPages; + private final CAGClientServiceType client; + + private CAGItemSearchController( + final CAGClientServiceType inClient) + { + this.client = + Objects.requireNonNull(inClient, "client"); + this.itemsView = + FXCollections.observableArrayList(); + this.itemsViewSorted = + new SortedList<>(this.itemsView); + this.itemPages = + new SimpleObjectProperty<>(CAGPageRange.zero()); + } + + /** + * @param client The client + * + * @return An item search controller. + */ + + public static CAGItemSearchControllerType create( + final CAGClientServiceType client) + { + final var controller = new CAGItemSearchController(client); + client.status().subscribe((oldStatus, newStatus) -> { + controller.onClientStatusChanged(); + }); + return controller; + } + + private void onClientStatusChanged() + { + this.itemsView.clear(); + this.itemPages.set(CAGPageRange.zero()); + } + + @Override + public void itemSearchBegin( + final CAItemSearchParameters parameters) + { + this.client.execute(new CAICommandItemSearchBegin(parameters)) + .thenAccept(response -> { + Platform.runLater(() -> { + final var data = response.data(); + this.itemsView.setAll(data.items()); + this.itemPages.set( + new CAGPageRange( + (long) data.pageIndex(), + (long) data.pageCount() + ) + ); + }); + }); + } + + @Override + public ObservableList itemsView() + { + return FXCollections.unmodifiableObservableList(this.itemsView); + } + + @Override + public SortedList itemsViewSorted() + { + return this.itemsViewSorted; + } + + @Override + public ObservableValue itemPages() + { + return this.itemPages; + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchControllerType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchControllerType.java new file mode 100644 index 0000000..f37ee7f --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchControllerType.java @@ -0,0 +1,58 @@ +/* + * 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.CAItemSearchParameters; +import com.io7m.cardant.model.CAItemSummary; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +/** + * The item search controller. + */ + +public interface CAGItemSearchControllerType +{ + /** + * Start searching for items. + * + * @param parameters The search parameters + */ + + void itemSearchBegin( + CAItemSearchParameters parameters); + + /** + * @return The item search results + */ + + ObservableList itemsView(); + + /** + * @return The item search results + */ + + SortedList itemsViewSorted(); + + /** + * @return The item search result pages + */ + + ObservableValue itemPages(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemSearchView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchView.java similarity index 95% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemSearchView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchView.java index 4b251e5..372dd96 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemSearchView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSearchView.java @@ -49,6 +49,7 @@ import java.util.ArrayList; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.ResourceBundle; import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_CANCEL; @@ -60,18 +61,19 @@ import static javafx.scene.control.ButtonBar.ButtonData.OK_DONE; /** - * The main item search view. + * An item search view. */ -public final class CAGMainItemSearchView +public final class CAGItemSearchView implements CAGViewType { private static final Logger LOG = - LoggerFactory.getLogger(CAGMainItemSearchView.class); + LoggerFactory.getLogger(CAGItemSearchView.class); private final CAGStringsType strings; private final CAMetadataMatchExpressions expressions; - private final CAGControllerType controller; + private CAGItemSearchControllerType controller; + private CAGMetaMatchTree metaTree; @FXML private ChoiceBox itemNameMatch; @FXML private ChoiceBox itemTypeMatch; @@ -84,23 +86,32 @@ public final class CAGMainItemSearchView @FXML private Accordion accordion; @FXML private TitledPane basicParameters; - private CAGMetaMatchTree metaTree; - /** - * The main item search view. + * An item search view. * * @param services The service directory */ - public CAGMainItemSearchView( + public CAGItemSearchView( final RPServiceDirectoryType services) { this.strings = services.requireService(CAGStringsType.class); this.expressions = new CAMetadataMatchExpressions(CAStrings.create(Locale.getDefault())); + } + + /** + * Set the controllers. + * + * @param inController The controller + */ + + public void setControllers( + final CAGItemSearchControllerType inController) + { this.controller = - services.requireService(CAGControllerType.class); + Objects.requireNonNull(inController, "controller"); } private void clearParameters() diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectDialogArguments.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectDialogArguments.java new file mode 100644 index 0000000..010eefe --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectDialogArguments.java @@ -0,0 +1,45 @@ +/* + * 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 java.util.Objects; + +/** + * Dialog arguments. + * + * @param details The details controller + * @param search The search controller + */ + +public record CAGItemSelectDialogArguments( + CAGItemDetailsControllerType details, + CAGItemSearchControllerType search) +{ + /** + * Dialog arguments. + * + * @param details The tree controller + * @param search The search controller + */ + + public CAGItemSelectDialogArguments + { + Objects.requireNonNull(details, "details"); + Objects.requireNonNull(search, "search"); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectDialogs.java new file mode 100644 index 0000000..cfbc5f5 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectDialogs.java @@ -0,0 +1,107 @@ +/* + * 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; + +import java.util.Map; +import java.util.Objects; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ITEMS_SELECTTITLE; + +/** + * An item selection dialog. + */ + +public final class CAGItemSelectDialogs + extends CAGDialogFactoryAbstract +{ + /** + * An item selection dialog. + * + * @param services The service directory + */ + + public CAGItemSelectDialogs( + final RPServiceDirectoryType services) + { + super( + CAGLocationSelectView.class, + "/com/io7m/cardant_gui/ui/internal/itemSelect.fxml", + services, + Modality.NONE + ); + } + + @Override + protected String createStageTitle( + final CAGItemSelectDialogArguments arguments) + { + Objects.requireNonNull(arguments, "arguments"); + + return this.strings().format(CARDANT_ITEMS_SELECTTITLE); + } + + @Override + protected CAGControllerFactoryType controllerFactory( + final CAGItemSelectDialogArguments arguments, + final Stage stage) + { + return CAGControllerFactoryMapped.create( + Map.entry( + CAGItemSearchView.class, + () -> new CAGItemSearchView(this.services()) + ), + Map.entry( + CAGItemDetailsView.class, + () -> new CAGItemDetailsView(this.services()) + ), + Map.entry( + CAGItemTableView.class, + () -> new CAGItemTableView(this.services()) + ), + Map.entry( + CAGItemSelectView.class, + () -> { + return new CAGItemSelectView( + stage, + arguments.details(), + arguments.search() + ); + } + ) + ); + } + + @Override + public String description() + { + return "Item select dialogs."; + } + + @Override + public String toString() + { + return String.format( + "[CAGItemSelectDialogs 0x%08x]", + Integer.valueOf(this.hashCode()) + ); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectView.java new file mode 100644 index 0000000..f6e2be1 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemSelectView.java @@ -0,0 +1,100 @@ +/* + * 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 javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * An item selection view. + */ + +public final class CAGItemSelectView + implements CAGViewType +{ + private final Stage stage; + private final CAGItemSearchControllerType search; + private final CAGItemDetailsControllerType details; + + @FXML private Node itemSearch; + @FXML private CAGItemSearchView itemSearchController; + @FXML private Node itemDetails; + @FXML private CAGItemDetailsView itemDetailsController; + @FXML private Node itemTable; + @FXML private CAGItemTableView itemTableController; + @FXML private Button select; + + /** + * An item selection view. + * + * @param inStage The stage + * @param inSearch The search controller + * @param inDetails The details controller + */ + + public CAGItemSelectView( + final Stage inStage, + final CAGItemDetailsControllerType inDetails, + final CAGItemSearchControllerType inSearch) + { + this.stage = + Objects.requireNonNull(inStage, "stage"); + this.details = + Objects.requireNonNull(inDetails, "inDetails"); + this.search = + Objects.requireNonNull(inSearch, "inSearch"); + } + + @Override + public void initialize( + final URL item, + final ResourceBundle resources) + { + this.itemSearchController + .setControllers(this.search); + this.itemDetailsController + .setControllers(this.details); + this.itemTableController + .setControllers(this.search, this.details); + + this.details.itemSelected() + .summary() + .addListener((observable, oldValue, newValue) -> { + this.select.setDisable(newValue.isEmpty()); + }); + } + + @FXML + private void onCancelSelected() + { + this.details.itemSelectNothing(); + this.stage.close(); + } + + @FXML + private void onSelectSelected() + { + this.stage.close(); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemTableView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemTableView.java similarity index 73% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemTableView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemTableView.java index 9a8be1c..004c50e 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemTableView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGItemTableView.java @@ -20,7 +20,6 @@ import com.io7m.cardant.model.CAItemSummary; import com.io7m.repetoir.core.RPServiceDirectoryType; import javafx.beans.property.ReadOnlyStringWrapper; -import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.scene.control.Button; @@ -31,19 +30,21 @@ import java.net.URL; import java.util.Objects; import java.util.ResourceBundle; -import java.util.UUID; /** * The table of items. */ -public final class CAGMainItemTableView +public final class CAGItemTableView implements CAGViewType { - private final CAGControllerType controller; private final CAGStringsType strings; + private CAGItemSearchControllerType search; + private CAGItemDetailsControllerType details; @FXML private TableView mainItemTable; + @FXML private TableColumn colId; + @FXML private TableColumn colName; @FXML private Label resultsLabel; @FXML private Button itemAdd; @FXML private Button itemRemove; @@ -54,17 +55,42 @@ public final class CAGMainItemTableView * @param inServices The service directory */ - public CAGMainItemTableView( + public CAGItemTableView( final RPServiceDirectoryType inServices) { Objects.requireNonNull(inServices, "services"); - this.controller = - inServices.requireService(CAGControllerType.class); this.strings = inServices.requireService(CAGStringsType.class); } + /** + * Set the controlers. + * + * @param inSearch The search controller + * @param inDetails The details controller + */ + + public void setControllers( + final CAGItemSearchControllerType inSearch, + final CAGItemDetailsControllerType inDetails) + { + this.search = + Objects.requireNonNull(inSearch, "search"); + this.details = + Objects.requireNonNull(inDetails, "details"); + + this.search.itemsViewSorted() + .comparatorProperty() + .bind(this.mainItemTable.comparatorProperty()); + + this.search.itemsView() + .addListener(this::onItemsViewChanged); + + this.mainItemTable.setItems( + this.search.itemsViewSorted()); + } + @Override public void initialize( final URL url, @@ -74,40 +100,23 @@ public void initialize( this.mainItemTable.setPlaceholder(new Label("")); - final var tableColumns = - this.mainItemTable.getColumns(); - - final var tableIDColumn = - (TableColumn) tableColumns.get(0); - final var tableNameColumn = - (TableColumn) tableColumns.get(1); - - tableIDColumn.setSortable(true); - tableIDColumn.setReorderable(false); - tableIDColumn.setCellValueFactory( + this.colId.setSortable(true); + this.colId.setReorderable(false); + this.colId.setCellValueFactory( param -> { - return new SimpleObjectProperty<>(param.getValue().id().id()); + return new ReadOnlyStringWrapper(param.getValue().id().displayId()); }); - tableNameColumn.setSortable(true); - tableNameColumn.setReorderable(false); - tableNameColumn.setCellValueFactory( + this.colName.setSortable(true); + this.colName.setReorderable(false); + this.colName.setCellValueFactory( param -> { return new ReadOnlyStringWrapper(param.getValue().name()); }); - this.controller.itemsViewSorted() - .comparatorProperty() - .bind(this.mainItemTable.comparatorProperty()); - - this.controller.itemsView() - .addListener(this::onItemsViewChanged); - this.mainItemTable.getSelectionModel() .getSelectedItems() .addListener((ListChangeListener) this::onItemSelectionChanged); - - this.mainItemTable.setItems(this.controller.itemsViewSorted()); } private void onItemSelectionChanged( @@ -117,14 +126,14 @@ private void onItemSelectionChanged( final var selected = c.getList(); if (selected.isEmpty()) { - this.controller.itemSelectNothing(); + this.details.itemSelectNothing(); return; } if (selected.size() == 1) { final var selectedItem = selected.get(0); this.itemRemove.setDisable(false); - this.controller.itemGet(selectedItem.id()); + this.details.itemSelect(selectedItem.id()); return; } } @@ -135,7 +144,7 @@ private void onItemsViewChanged( final var size = c.getList().size(); if (size > 0) { final var range = - this.controller.itemPages().getValue(); + this.search.itemPages().getValue(); this.resultsLabel.setText( this.strings.format( diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddDialogArguments.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddDialogArguments.java new file mode 100644 index 0000000..ab12f2e --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationAttachmentAddDialogArguments.java @@ -0,0 +1,47 @@ +/* + * 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.CALocationID; + +import java.util.Objects; + +/** + * Dialog arguments. + * + * @param treeController The tree controller + * @param location The location + */ + +public record CAGLocationAttachmentAddDialogArguments( + CAGLocationTreeControllerType treeController, + CALocationID location) +{ + /** + * Dialog arguments. + * + * @param treeController The tree controller + * @param location The location + */ + + public CAGLocationAttachmentAddDialogArguments + { + Objects.requireNonNull(location, "location"); + Objects.requireNonNull(treeController, "detailsController"); + } +} 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 index cc965eb..8646485 100644 --- 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 @@ -21,12 +21,17 @@ import javafx.stage.Modality; import javafx.stage.Stage; +import java.util.Map; +import java.util.Objects; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ATTACHMENTADD_TITLE; + /** * An attachment addition dialog. */ public final class CAGLocationAttachmentAddDialogs - extends CAGDialogFactoryAbstract + extends CAGDialogFactoryAbstract { /** * An attachment addition dialog. @@ -47,17 +52,34 @@ public CAGLocationAttachmentAddDialogs( @Override protected String createStageTitle( - final Void arguments) + final CAGLocationAttachmentAddDialogArguments arguments) { - return this.strings().format(CAGStringConstants.CARDANT_ATTACHMENTADD_TITLE); + Objects.requireNonNull(arguments, "arguments"); + + return this.strings().format(CARDANT_ATTACHMENTADD_TITLE); } @Override - protected CAGLocationAttachmentAddView createController( - final Void arguments, + protected CAGControllerFactoryType controllerFactory( + final CAGLocationAttachmentAddDialogArguments arguments, final Stage stage) { - return new CAGLocationAttachmentAddView(stage, this.services()); + Objects.requireNonNull(arguments, "arguments"); + Objects.requireNonNull(stage, "stage"); + + return CAGControllerFactoryMapped.create( + Map.entry( + CAGLocationAttachmentAddView.class, + () -> { + return new CAGLocationAttachmentAddView( + stage, + this.services(), + arguments.treeController(), + arguments.location() + ); + } + ) + ); } @Override 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 index 28eaf5f..916db46 100644 --- 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 @@ -20,23 +20,24 @@ 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.Alert; 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.io.IOException; import java.net.URL; import java.util.Objects; import java.util.ResourceBundle; import java.util.UUID; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_ERROR_RELATIONEMPTY; + /** * The attachment addition view. */ @@ -45,8 +46,12 @@ public final class CAGLocationAttachmentAddView implements CAGViewType { private final Stage stage; + private final CAGLocationTreeControllerType treeController; private final CAGStringsType strings; - private final CAGControllerType controller; + private final CALocationID location; + private final CAGFileSelectDialogs fileSelectDialogs; + private final CAGClientServiceType client; + private final CAGFileSearchControllerType fileSearchController; @FXML private Button addButton; @FXML private Label resultsLabel; @@ -58,20 +63,33 @@ public final class CAGLocationAttachmentAddView /** * The attachment addition view. * - * @param inStage The stage - * @param services The services + * @param inLocation The location + * @param inStage The stage + * @param services The services + * @param inController The controller */ public CAGLocationAttachmentAddView( final Stage inStage, - final RPServiceDirectoryType services) + final RPServiceDirectoryType services, + final CAGLocationTreeControllerType inController, + final CALocationID inLocation) { this.stage = Objects.requireNonNull(inStage, "stage"); this.strings = services.requireService(CAGStringsType.class); - this.controller = - services.requireService(CAGControllerType.class); + this.fileSelectDialogs = + services.requireService(CAGFileSelectDialogs.class); + this.client = + services.requireService(CAGClientServiceType.class); + + this.fileSearchController = + CAGFileSearchController.create(this.client); + this.treeController = + Objects.requireNonNull(inController, "controller"); + this.location = + Objects.requireNonNull(inLocation, "inLocation"); } @Override @@ -79,55 +97,23 @@ 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.locationField.setText(this.location.displayId()); - this.fileField.textProperty() - .addListener(observable -> { - this.validate(); + this.fileSearchController.fileSelected() + .addListener((observable, oldValue, newValue) -> { + this.addButton.setDisable(newValue.isEmpty()); }); - this.relationField.textProperty() - .addListener(observable -> { - this.validate(); + this.fileSearchController.fileSelected() + .addListener((observable, oldValue, newValue) -> { + if (newValue.isPresent()) { + this.fileField.setText(newValue.get().id().displayId()); + } else { + this.fileField.setText(""); + } }); } - 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 = @@ -138,6 +124,13 @@ private CAICommandLocationAttachmentAdd createCommand() this.fileField.getText().trim(); if (relationText.isEmpty()) { + final var alert = + new Alert( + Alert.AlertType.ERROR, + this.strings.format(CARDANT_ERROR_RELATIONEMPTY) + ); + + alert.showAndWait(); throw new IllegalArgumentException(); } @@ -148,70 +141,23 @@ private CAICommandLocationAttachmentAdd createCommand() ); } - 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.treeController.locationAttachmentAdd(this.createCommand()); this.stage.close(); } @FXML - private void onPageNextSelected() + private void onFileSelectSelected() + throws IOException { - + this.fileSelectDialogs.openDialogAndWait(this.fileSearchController); } @FXML - private void onPagePreviousSelected() + private void onCancelSelected() { - + this.stage.close(); } } 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/CAGLocationDetailsView.java similarity index 76% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationDetailsView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationDetailsView.java index 3b562a5..cab5930 100644 --- 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/CAGLocationDetailsView.java @@ -19,6 +19,7 @@ import com.io7m.cardant.model.CAAttachment; import com.io7m.cardant.model.CAAttachmentRelations; +import com.io7m.cardant.model.CALocationID; import com.io7m.cardant.model.CALocationSummary; import com.io7m.cardant.model.CAMetadataType; import com.io7m.lanark.core.RDottedName; @@ -43,21 +44,22 @@ import java.net.URL; import java.nio.file.Files; import java.util.Objects; +import java.util.Optional; import java.util.ResourceBundle; /** - * The main location details view. + * A location details view. */ -public final class CAGMainLocationDetailsView +public final class CAGLocationDetailsView implements CAGViewType { private static final Logger LOG = - LoggerFactory.getLogger(CAGMainLocationDetailsView.class); + LoggerFactory.getLogger(CAGLocationDetailsView.class); - private final CAGControllerType controller; private final CAGStringsType strings; private final CAGLocationAttachmentAddDialogs attachmentAddDialogs; + private final CAGClientServiceType client; private CAGViewAndStage attachmentAddDialog; @FXML private TabPane mainItemDetails; @@ -66,29 +68,72 @@ public final class CAGMainLocationDetailsView @FXML private ImageView thumbnail; @FXML private ProgressBar thumbnailLoading; @FXML private TableView meta; + @FXML private TableColumn colPkg; + @FXML private TableColumn colType; + @FXML private TableColumn colField; + @FXML private TableColumn colValue; @FXML private ListView attachments; @FXML private Button metaAdd; @FXML private Button metaRemove; @FXML private Button attachmentAdd; @FXML private Button attachmentRemove; + private CAGLocationTreeControllerType controller; + /** - * The main location details view. + * A location details view. * * @param services The service directory */ - public CAGMainLocationDetailsView( + public CAGLocationDetailsView( final RPServiceDirectoryType services) { - this.controller = - services.requireService(CAGControllerType.class); + this.client = + services.requireService(CAGClientServiceType.class); this.strings = services.requireService(CAGStringsType.class); this.attachmentAddDialogs = services.requireService(CAGLocationAttachmentAddDialogs.class); } + /** + * Set the controllers. + * + * @param inDetails The controller + */ + + public void setControllers( + final CAGLocationTreeControllerType inDetails) + { + this.controller = + Objects.requireNonNull(inDetails, "details"); + + final var selected = + this.controller.locationSelected(); + + selected.metadata() + .comparatorProperty() + .bind(this.meta.comparatorProperty()); + + this.meta.setItems(selected.metadata()); + + this.attachments.setCellFactory( + new CAGItemAttachmentCellFactory(this.strings) + ); + this.attachments.setItems(selected.attachments()); + + selected.summary() + .addListener((observable, oldValue, newValue) -> { + this.locationSelectionChanged(newValue); + }); + + selected.attachments() + .addListener((ListChangeListener) c -> { + this.loadThumbnail(); + }); + } + @Override public void initialize( final URL url, @@ -99,82 +144,47 @@ public void initialize( 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( + this.colPkg.setSortable(true); + this.colPkg.setReorderable(false); + this.colPkg.setCellValueFactory( param -> { return new SimpleObjectProperty<>( param.getValue().name().typeName().packageName() ); }); - tableTypeColumn.setSortable(true); - tableTypeColumn.setReorderable(false); - tableTypeColumn.setCellValueFactory( + this.colType.setSortable(true); + this.colType.setReorderable(false); + this.colType.setCellValueFactory( param -> { return new SimpleObjectProperty<>( param.getValue().name().typeName().typeName() ); }); - tableFieldColumn.setSortable(true); - tableFieldColumn.setReorderable(false); - tableFieldColumn.setCellValueFactory( + this.colField.setSortable(true); + this.colField.setReorderable(false); + this.colField.setCellValueFactory( param -> { return new SimpleObjectProperty<>( param.getValue().name().fieldName() ); }); - tableValueColumn.setSortable(true); - tableValueColumn.setReorderable(false); - tableValueColumn.setCellValueFactory( + this.colValue.setSortable(true); + this.colValue.setReorderable(false); + this.colValue.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) + final Optional newOpt) { - if (newValue == null) { + if (newOpt.isEmpty()) { this.mainItemDetails.setDisable(true); this.idField.setText(""); this.nameField.setText(""); @@ -182,6 +192,7 @@ private void locationSelectionChanged( return; } + final var newValue = newOpt.orElseThrow(); this.mainItemDetails.setDisable(false); this.idField.setText(newValue.id().toString()); this.nameField.setText(newValue.name()); @@ -230,7 +241,7 @@ private void loadThumbnailFromAttachment( final var hashValue = attachmentFile.hashValue(); - this.controller.imageGet( + this.client.imageGet( fileID, file, fileTmp, @@ -270,7 +281,12 @@ private void onAttachmentAddSelected() { if (this.attachmentAddDialog == null) { this.attachmentAddDialog = - this.attachmentAddDialogs.createDialog(null); + this.attachmentAddDialogs.createDialog( + new CAGLocationAttachmentAddDialogArguments( + this.controller, + CALocationID.of(this.idField.getText()) + ) + ); } this.attachmentAddDialog.stage() diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModel.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModel.java new file mode 100644 index 0000000..cf1ba7a --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModel.java @@ -0,0 +1,132 @@ +/* + * 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.CALocation; +import com.io7m.cardant.model.CALocationSummary; +import com.io7m.cardant.model.CAMetadataType; +import com.io7m.cardant.model.CATypeRecordIdentifier; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import java.util.Comparator; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * An observable location. + */ + +public final class CAGLocationModel implements CAGLocationModelType +{ + private final SimpleObjectProperty> summary; + private final ObservableList metadata; + private final SortedList metadataSorted; + private final ObservableList attachments; + private final ObservableList types; + private final SortedList typesSorted; + private final ObservableList attachmentsRead; + + private CAGLocationModel() + { + this.summary = + new SimpleObjectProperty<>(Optional.empty()); + this.metadata = + FXCollections.observableArrayList(); + this.metadataSorted = + new SortedList<>(this.metadata); + this.attachments = + FXCollections.observableArrayList(); + this.attachmentsRead = + FXCollections.unmodifiableObservableList(this.attachments); + this.types = + FXCollections.observableArrayList(); + this.typesSorted = + new SortedList<>(this.types); + } + + /** + * @return An observable location. + */ + + public static CAGLocationModelType create() + { + return new CAGLocationModel(); + } + + @Override + public ObservableValue> summary() + { + return this.summary; + } + + @Override + public SortedList metadata() + { + return this.metadataSorted; + } + + @Override + public ObservableList attachments() + { + return this.attachmentsRead; + } + + @Override + public SortedList types() + { + return this.typesSorted; + } + + @Override + public void update( + final CALocation location) + { + this.summary.set(Optional.of(location.summary())); + + this.metadata.setAll( + location.metadata() + .values() + .stream() + .toList() + ); + + this.attachments.setAll( + location.attachments() + .values() + .stream() + .sorted(Comparator.comparing(o -> o.key().fileID())) + .collect(Collectors.toList()) + ); + + this.types.setAll(location.types()); + } + + @Override + public void clear() + { + this.summary.set(Optional.empty()); + this.metadata.clear(); + this.attachments.clear(); + this.types.clear(); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModelReadableType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModelReadableType.java new file mode 100644 index 0000000..637092a --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModelReadableType.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.CAAttachment; +import com.io7m.cardant.model.CALocationSummary; +import com.io7m.cardant.model.CAMetadataType; +import com.io7m.cardant.model.CATypeRecordIdentifier; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import java.util.Optional; + +/** + * An observable location. + */ + +public interface CAGLocationModelReadableType +{ + /** + * @return The summary of the location, if the location exists + */ + + ObservableValue> summary(); + + /** + * @return The metadata for the location + */ + + SortedList metadata(); + + /** + * @return The attachments for the location + */ + + ObservableList attachments(); + + /** + * @return The currently assigned types + */ + + SortedList types(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModelType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModelType.java new file mode 100644 index 0000000..41b71a7 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationModelType.java @@ -0,0 +1,41 @@ +/* + * 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.CALocation; + +/** + * An observable location. + */ + +public interface CAGLocationModelType extends CAGLocationModelReadableType +{ + /** + * Set all the location values. + * + * @param location The location + */ + + void update(CALocation location); + + /** + * Clear the location. + */ + + void clear(); +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentDialogs.java index 46bb065..1e67a0e 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentDialogs.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentDialogs.java @@ -18,19 +18,45 @@ package com.io7m.cardant_gui.ui.internal; import com.io7m.cardant.model.CALocationID; +import com.io7m.cardant_gui.ui.internal.CAGLocationReparentDialogs.Arguments; import com.io7m.repetoir.core.RPServiceDirectoryType; import javafx.stage.Modality; import javafx.stage.Stage; +import java.util.Map; import java.util.Objects; +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_LOCATIONREPARENT_TITLE; + /** * A location reparent dialog. */ public final class CAGLocationReparentDialogs - extends CAGDialogFactoryAbstract + extends CAGDialogFactoryAbstract { + /** + * The arguments. + * + * @param controller The location tree controller + * @param locationID The location ID + */ + + public record Arguments( + CAGLocationTreeControllerType controller, + CALocationID locationID) + { + /** + * The arguments. + */ + + public Arguments + { + Objects.requireNonNull(controller, "controller"); + Objects.requireNonNull(locationID, "locationID"); + } + } + /** * A location reparent dialog. * @@ -50,19 +76,33 @@ public CAGLocationReparentDialogs( @Override protected String createStageTitle( - final CALocationID arguments) + final Arguments arguments) { Objects.requireNonNull(arguments, "arguments"); - return this.strings().format(CAGStringConstants.CARDANT_LOCATIONREPARENT_TITLE); + + return this.strings().format(CARDANT_LOCATIONREPARENT_TITLE); } @Override - protected CAGLocationReparentView createController( - final CALocationID arguments, + protected CAGControllerFactoryType controllerFactory( + final Arguments arguments, final Stage stage) { Objects.requireNonNull(arguments, "arguments"); - return new CAGLocationReparentView(stage, this.services(), arguments); + Objects.requireNonNull(stage, "stage"); + + return CAGControllerFactoryMapped.create( + Map.entry( + CAGLocationReparentView.class, + () -> { + return new CAGLocationReparentView( + stage, + this.services(), + arguments.locationID, + arguments.controller + ); + }) + ); } @Override diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentView.java index c02bfce..f4583dc 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationReparentView.java @@ -39,7 +39,7 @@ public final class CAGLocationReparentView { private final Stage stage; private final CAGStringsType strings; - private final CAGControllerType controller; + private final CAGLocationTreeControllerType controller; private final CALocationID locationID; @FXML private TreeView locationTree; @@ -52,21 +52,23 @@ public final class CAGLocationReparentView * @param inStage The stage * @param services The services * @param inLocationID The child location + * @param inController The tree controller */ public CAGLocationReparentView( final Stage inStage, final RPServiceDirectoryType services, - final CALocationID inLocationID) + final CALocationID inLocationID, + final CAGLocationTreeControllerType inController) { this.stage = Objects.requireNonNull(inStage, "stage"); this.strings = services.requireService(CAGStringsType.class); - this.controller = - services.requireService(CAGControllerType.class); this.locationID = Objects.requireNonNull(inLocationID, "locationID"); + this.controller = + Objects.requireNonNull(inController, "controller"); } @Override diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationSelectDialogs.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationSelectDialogs.java new file mode 100644 index 0000000..01ec79d --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationSelectDialogs.java @@ -0,0 +1,103 @@ +/* + * 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; + +import java.util.Map; +import java.util.Objects; + +import static com.io7m.cardant_gui.ui.internal.CAGStringConstants.CARDANT_LOCATIONS_SELECTTITLE; + +/** + * A location selection dialog. + */ + +public final class CAGLocationSelectDialogs + extends CAGDialogFactoryAbstract +{ + /** + * A location selection dialog. + * + * @param services The service directory + */ + + public CAGLocationSelectDialogs( + final RPServiceDirectoryType services) + { + super( + CAGLocationSelectView.class, + "/com/io7m/cardant_gui/ui/internal/locationSelect.fxml", + services, + Modality.NONE + ); + } + + @Override + protected String createStageTitle( + final CAGLocationTreeControllerType arguments) + { + Objects.requireNonNull(arguments, "arguments"); + + return this.strings().format(CARDANT_LOCATIONS_SELECTTITLE); + } + + protected CAGLocationSelectView createController( + final CAGLocationTreeControllerType arguments, + final Stage stage) + { + Objects.requireNonNull(arguments, "arguments"); + Objects.requireNonNull(stage, "stage"); + + return new CAGLocationSelectView(stage, arguments); + } + + @Override + protected CAGControllerFactoryType controllerFactory( + final CAGLocationTreeControllerType arguments, + final Stage stage) + { + return CAGControllerFactoryMapped.create( + Map.entry( + CAGLocationTreeView.class, + () -> new CAGLocationTreeView(this.services()) + ), + Map.entry( + CAGLocationSelectView.class, + () -> new CAGLocationSelectView(stage, arguments) + ) + ); + } + + @Override + public String description() + { + return "Location select dialogs."; + } + + @Override + public String toString() + { + return String.format( + "[CAGLocationSelectDialogs 0x%08x]", + Integer.valueOf(this.hashCode()) + ); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationSelectView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationSelectView.java new file mode 100644 index 0000000..76a267d --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationSelectView.java @@ -0,0 +1,86 @@ +/* + * 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 javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * A location selection view. + */ + +public final class CAGLocationSelectView + implements CAGViewType +{ + private final Stage stage; + private final CAGLocationTreeControllerType controller; + + @FXML private Node locationTree; + @FXML private CAGLocationTreeView locationTreeController; + @FXML private Button select; + + /** + * A location selection view. + * + * @param inStage The stage + * @param inController The tree controller + */ + + public CAGLocationSelectView( + final Stage inStage, + final CAGLocationTreeControllerType inController) + { + this.stage = + Objects.requireNonNull(inStage, "stage"); + this.controller = + Objects.requireNonNull(inController, "inController"); + } + + @Override + public void initialize( + final URL location, + final ResourceBundle resources) + { + this.locationTreeController.setControllers(this.controller); + + this.controller.locationSelected() + .summary() + .addListener((observable, oldValue, newValue) -> { + this.select.setDisable(newValue.isEmpty()); + }); + } + + @FXML + private void onCancelSelected() + { + this.controller.locationSelectNothing(); + this.stage.close(); + } + + @FXML + private void onSelectSelected() + { + this.stage.close(); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationTreeController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationTreeController.java new file mode 100644 index 0000000..900be6c --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationTreeController.java @@ -0,0 +1,285 @@ +/* + * 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.CALocation; +import com.io7m.cardant.model.CALocationID; +import com.io7m.cardant.model.CALocationSummary; +import com.io7m.cardant.protocol.inventory.CAICommandLocationAttachmentAdd; +import com.io7m.cardant.protocol.inventory.CAICommandLocationDelete; +import com.io7m.cardant.protocol.inventory.CAICommandLocationGet; +import com.io7m.cardant.protocol.inventory.CAICommandLocationList; +import com.io7m.cardant.protocol.inventory.CAICommandLocationPut; +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.control.TreeItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Objects; +import java.util.Optional; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * A location tree controller. + */ + +public final class CAGLocationTreeController + implements CAGLocationTreeControllerType +{ + private static final Logger LOG = + LoggerFactory.getLogger(CAGLocationTreeController.class); + + 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 ObservableList locationsView; + private final SimpleObjectProperty locationPages; + private final SimpleObjectProperty> locationTree; + private final CAGClientServiceType client; + private final CAGLocationModelType locationSelected; + + private CAGLocationTreeController( + final CAGClientServiceType inClient) + { + this.client = + Objects.requireNonNull(inClient, "client"); + this.locationSelected = + CAGLocationModel.create(); + this.locationsView = + FXCollections.observableArrayList(); + this.locationTree = + new SimpleObjectProperty<>(); + this.locationPages = + new SimpleObjectProperty<>(CAGPageRange.zero()); + } + + /** + * @param client The client + * + * @return A location tree controller. + */ + + public static CAGLocationTreeControllerType create( + final CAGClientServiceType client) + { + final var controller = new CAGLocationTreeController(client); + client.status().subscribe((oldStatus, newStatus) -> { + controller.onClientStatusChanged(); + }); + return controller; + } + + private void onClientStatusChanged() + { + this.locationsView.clear(); + this.locationPages.set(CAGPageRange.zero()); + } + + @Override + public ObservableValue> locationTree() + { + return this.locationTree; + } + + @Override + public void locationSearchBegin() + { + final var future = + this.client.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 locationRemove( + final CALocationID location) + { + if (Objects.equals(location, ROOT_LOCATION)) { + return; + } + + final var future = + this.client.execute(new CAICommandLocationDelete(location)); + + future.thenAccept(response -> { + Platform.runLater(() -> { + this.locationTreeDelete(this.locationTree.get(), location); + }); + }); + } + + @Override + public void locationCreate( + final String name) + { + final var future = + this.client.execute(new CAICommandLocationPut( + new CALocation( + CALocationID.random(), + Optional.empty(), + name, + new TreeMap<>(), + new TreeMap<>(), + new TreeSet<>() + ) + )); + + future.thenAccept(response -> this.locationSearchBegin()); + } + + @Override + public void locationReparent( + final CALocationID location, + final CALocationID newParent) + { + final var future0 = + this.client.execute(new CAICommandLocationGet(location)); + + final var future1 = + future0.thenCompose(response -> { + final var source = response.data(); + return this.client.execute( + new CAICommandLocationPut( + new CALocation( + source.id(), + Optional.of(newParent), + source.name(), + source.metadata(), + source.attachments(), + source.types() + ) + ) + ); + }); + + future1.thenAccept(response -> this.locationSearchBegin()); + } + + @Override + public void locationSelect( + final CALocationID id) + { + if (Objects.equals(id, ROOT_LOCATION)) { + return; + } + + final var future = + this.client.execute(new CAICommandLocationGet(id)); + + future.thenAccept(response -> { + Platform.runLater(() -> { + this.locationSelected.update(response.data()); + }); + }); + } + + @Override + public void locationSelectNothing() + { + this.locationSelected.clear(); + } + + @Override + public void locationAttachmentAdd( + final CAICommandLocationAttachmentAdd command) + { + this.client.execute(command); + this.locationSelect(command.location()); + } + + @Override + public CAGLocationModelReadableType locationSelected() + { + return this.locationSelected; + } + + private boolean locationTreeDelete( + final TreeItem tree, + final CALocationID id) + { + if (Objects.equals(tree.getValue().id(), id)) { + tree.getParent() + .getChildren() + .remove(tree); + return true; + } + for (final var c : tree.getChildren()) { + if (this.locationTreeDelete(c, id)) { + return true; + } + } + return false; + } +} 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/CAGLocationTreeControllerType.java similarity index 70% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerLocationsType.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationTreeControllerType.java index cd62960..c83e093 100644 --- 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/CAGLocationTreeControllerType.java @@ -17,23 +17,17 @@ 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. + * A location tree controller. */ -public interface CAGControllerLocationsType - extends RPServiceType +public interface CAGLocationTreeControllerType { /** * @return The location tree @@ -48,74 +42,59 @@ public interface CAGControllerLocationsType void locationSearchBegin(); /** - * Fetch an location. + * Remove the selected 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 + * @param location The location */ - ObservableList locationSelectedAttachments(); + void locationRemove( + CALocationID location); /** - * Add an attachment. + * Create a location with a name. * - * @param command The command + * @param name The name */ - void locationAttachmentAdd( - CAICommandLocationAttachmentAdd command); + void locationCreate(String name); /** - * @return The page range for the current location search query + * Set the parent of {@code location} to {@code newParent}. + * + * @param location The location + * @param newParent The new parent */ - ObservableValue locationPages(); + void locationReparent( + CALocationID location, + CALocationID newParent); /** - * @return The currently selected location + * Select and fetch an location. + * + * @param id The location ID */ - ObservableValue locationSelected(); + void locationSelect(CALocationID id); /** - * Remove the selected location. + * Clear the current location selection. */ - void locationRemove(); + void locationSelectNothing(); /** - * Create a location with a name. + * Add an attachment. * - * @param name The name + * @param command The command */ - void locationCreate(String name); + void locationAttachmentAdd( + CAICommandLocationAttachmentAdd command); /** - * Set the parent of {@code location} to {@code newParent}. - * - * @param location The location - * @param newParent The new parent + * @return The currently selected location */ - void locationReparent( - CALocationID location, - CALocationID newParent); + CAGLocationModelReadableType locationSelected(); } 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/CAGLocationTreeView.java similarity index 87% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationTableView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGLocationTreeView.java index 7b0b133..e274ebe 100644 --- 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/CAGLocationTreeView.java @@ -43,13 +43,12 @@ import static javafx.scene.control.ButtonBar.ButtonData.OK_DONE; /** - * The table of locations. + * The tree of locations. */ -public final class CAGMainLocationTableView +public final class CAGLocationTreeView implements CAGViewType { - private final CAGControllerType controller; private final CAGStringsType strings; private final CAGClientServiceType clients; private final CAGLocationReparentDialogs reparentDialogs; @@ -59,19 +58,19 @@ public final class CAGMainLocationTableView @FXML private Button locationRemove; @FXML private Button locationReparent; + private CAGLocationTreeControllerType controller; + /** - * The table of locations. + * The tree of locations. * * @param inServices The service directory */ - public CAGMainLocationTableView( + public CAGLocationTreeView( final RPServiceDirectoryType inServices) { Objects.requireNonNull(inServices, "services"); - this.controller = - inServices.requireService(CAGControllerType.class); this.strings = inServices.requireService(CAGStringsType.class); this.clients = @@ -80,34 +79,47 @@ public CAGMainLocationTableView( inServices.requireService(CAGLocationReparentDialogs.class); } - @Override - public void initialize( - final URL url, - final ResourceBundle resourceBundle) + /** + * Set the controllers. + * + * @param inController The controller + */ + + public void setControllers( + final CAGLocationTreeControllerType inController) { - this.locationRemove.setDisable(true); - this.locationReparent.setDisable(true); + this.controller = + Objects.requireNonNull(inController, "controller"); - this.mainLocationTree.setCellFactory( - new CAGLocationCellFactory(this.strings) + this.mainLocationTree.setRoot( + this.controller.locationTree() + .getValue() ); + this.controller.locationTree() + .addListener((observable, oldValue, newValue) -> { + this.mainLocationTree.setRoot(newValue); + }); + this.clients.status() .subscribe((oldStatus, newStatus) -> { Platform.runLater(() -> { this.onClientStatusChanged(oldStatus, newStatus); }); }); + } - this.mainLocationTree.setRoot( - this.controller.locationTree() - .getValue() - ); + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.locationRemove.setDisable(true); + this.locationReparent.setDisable(true); - this.controller.locationTree() - .addListener((observable, oldValue, newValue) -> { - this.mainLocationTree.setRoot(newValue); - }); + this.mainLocationTree.setCellFactory( + new CAGLocationCellFactory(this.strings) + ); this.mainLocationTree.getSelectionModel() .selectedItemProperty() @@ -129,7 +141,7 @@ private void onLocationSelectionChanged( this.locationReparent.setDisable(false); this.locationRemove.setDisable(false); - this.controller.locationGet(newValue.getValue().id()); + this.controller.locationSelect(newValue.getValue().id()); } private void onClientStatusChanged( @@ -167,10 +179,13 @@ private void onLocationReparentSelected() throws IOException { this.reparentDialogs.openDialogAndWait( - this.mainLocationTree.getSelectionModel() - .getSelectedItem() - .getValue() - .id() + new CAGLocationReparentDialogs.Arguments( + this.controller, + this.mainLocationTree.getSelectionModel() + .getSelectedItem() + .getValue() + .id() + ) ); } @@ -208,7 +223,13 @@ private void onLocationRemoveSelected() } if (r.get().equals(confirm)) { - this.controller.locationRemove(); + this.controller.locationRemove( + this.controller.locationSelected() + .summary() + .getValue() + .orElseThrow() + .id() + ); } } } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFilesView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFilesView.java new file mode 100644 index 0000000..169e2e1 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainFilesView.java @@ -0,0 +1,70 @@ +/* + * 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.fxml.FXML; +import javafx.scene.Node; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * The main files view. + */ + +public final class CAGMainFilesView implements CAGViewType +{ + private final CAGClientServiceType client; + + @FXML private Node fileListView; + @FXML private CAGFileListView fileListViewController; + @FXML private Node fileSearchView; + @FXML private CAGFileSearchView fileSearchViewController; + + private final CAGFileSearchControllerType fileSearchController; + + /** + * The main files view. + * + * @param services The application services. + */ + + public CAGMainFilesView( + final RPServiceDirectoryType services) + { + Objects.requireNonNull(services, "services"); + + this.client = + services.requireService(CAGClientServiceType.class); + this.fileSearchController = + CAGFileSearchController.create(this.client); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.fileListViewController + .setControllers(this.fileSearchController); + this.fileSearchViewController + .setControllers(this.fileSearchController); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemsView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemsView.java new file mode 100644 index 0000000..fda49ca --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainItemsView.java @@ -0,0 +1,80 @@ +/* + * 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.fxml.FXML; +import javafx.scene.Node; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * The main items view. + */ + +public final class CAGMainItemsView implements CAGViewType +{ + private final CAGClientServiceType client; + + @FXML private Node itemTableView; + @FXML private CAGItemTableView itemTableViewController; + @FXML private Node itemSearchView; + @FXML private CAGItemSearchView itemSearchViewController; + @FXML private Node itemDetailsView; + @FXML private CAGItemDetailsView itemDetailsViewController; + + private final CAGItemDetailsControllerType itemDetailsController; + private final CAGItemSearchControllerType itemSearchController; + + /** + * The main items view. + * + * @param services The application services. + */ + + public CAGMainItemsView( + final RPServiceDirectoryType services) + { + Objects.requireNonNull(services, "services"); + + this.client = + services.requireService(CAGClientServiceType.class); + this.itemDetailsController = + CAGItemDetailsController.create(this.client); + this.itemSearchController = + CAGItemSearchController.create(this.client); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.itemTableViewController + .setControllers( + this.itemSearchController, + this.itemDetailsController + ); + this.itemSearchViewController + .setControllers(this.itemSearchController); + this.itemDetailsViewController + .setControllers(this.itemDetailsController); + } +} 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 deleted file mode 100644 index 286ba94..0000000 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationSearchView.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * 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/CAGMainLocationsView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationsView.java new file mode 100644 index 0000000..db805bc --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainLocationsView.java @@ -0,0 +1,69 @@ +/* + * 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.fxml.FXML; +import javafx.scene.Node; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * The main locations view. + */ + +public final class CAGMainLocationsView implements CAGViewType +{ + private final CAGClientServiceType client; + private final CAGLocationTreeControllerType locationTreeController; + + @FXML private Node locationTreeView; + @FXML private CAGLocationTreeView locationTreeViewController; + @FXML private Node locationDetailsView; + @FXML private CAGLocationDetailsView locationDetailsViewController; + + /** + * The main locations view. + * + * @param services The application services. + */ + + public CAGMainLocationsView( + final RPServiceDirectoryType services) + { + Objects.requireNonNull(services, "services"); + + this.client = + services.requireService(CAGClientServiceType.class); + this.locationTreeController = + CAGLocationTreeController.create(this.client); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.locationDetailsViewController.setControllers( + this.locationTreeController); + this.locationTreeViewController.setControllers( + this.locationTreeController); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockView.java new file mode 100644 index 0000000..75ec5a4 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockView.java @@ -0,0 +1,69 @@ +/* + * 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.fxml.FXML; +import javafx.scene.Node; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +/** + * The main stock view. + */ + +public final class CAGMainStockView implements CAGViewType +{ + private final CAGClientServiceType client; + private final CAGStockSearchControllerType stockSearchController; + + @FXML private Node stockSearchView; + @FXML private CAGStockSearchView stockSearchViewController; + @FXML private Node stockTableView; + @FXML private CAGStockTableView stockTableViewController; + + /** + * The main stock view. + * + * @param services The application services. + */ + + public CAGMainStockView( + final RPServiceDirectoryType services) + { + Objects.requireNonNull(services, "services"); + + this.client = + services.requireService(CAGClientServiceType.class); + this.stockSearchController = + CAGStockSearchController.create(this.client); + } + + @Override + public void initialize( + final URL url, + final ResourceBundle resourceBundle) + { + this.stockTableViewController.setControllers( + this.stockSearchController); + this.stockSearchViewController.setControllers( + this.stockSearchController); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainView.java index f821609..aa534fb 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainView.java @@ -48,7 +48,6 @@ public final class CAGMainView private final CAGClientServiceType clients; private final CAGStatusService status; private final CAGStringsType strings; - private final CAGControllerType controller; @FXML private TabPane mainTabs; @FXML private ProgressBar mainProgress; @@ -72,8 +71,6 @@ public CAGMainView( { this.services = Objects.requireNonNull(inServices, "services"); - this.controller = - this.services.requireService(CAGControllerType.class); this.clients = this.services.requireService(CAGClientServiceType.class); this.status = @@ -102,38 +99,6 @@ public void initialize( this.status.publish(new CAGStatusEvent( IDLE, this.strings.format(CAGStringConstants.CARDANT_UI_BOOT) )); - - this.controller.tabSelectionRequested() - .subscribe(new CAGPerpetualSubscriber<>(kind -> { - Platform.runLater(() -> { - switch (kind) { - case TAB_ITEMS -> { - this.mainTabs.getSelectionModel() - .select(this.tabItems); - } - case TAB_LOCATIONS -> { - this.mainTabs.getSelectionModel() - .select(this.tabLocations); - } - case TAB_STOCK -> { - this.mainTabs.getSelectionModel() - .select(this.tabStock); - } - case TAB_FILES -> { - this.mainTabs.getSelectionModel() - .select(this.tabFiles); - } - case TAB_TYPE_PACKAGES -> { - this.mainTabs.getSelectionModel() - .select(this.tabTypePackages); - } - case TAB_AUDIT -> { - this.mainTabs.getSelectionModel() - .select(this.tabAudit); - } - } - }); - })); } private void onStatusEvent( 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 index 741d2c3..61bf4b8 100644 --- 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 @@ -28,5 +28,15 @@ public record CAGPageRange( long pageIndex, long pageCount) { + private static final CAGPageRange ZERO = + new CAGPageRange(0L, 0L); + /** + * @return The zero page range + */ + + public static CAGPageRange zero() + { + return ZERO; + } } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchController.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchController.java new file mode 100644 index 0000000..8fba042 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchController.java @@ -0,0 +1,129 @@ +/* + * 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.CAStockOccurrenceType; +import com.io7m.cardant.model.CAStockSearchParameters; +import com.io7m.cardant.protocol.inventory.CAICommandStockSearchBegin; +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Objects; + +/** + * A stock search controller. + */ + +public final class CAGStockSearchController + implements CAGStockSearchControllerType +{ + private static final Logger LOG = + LoggerFactory.getLogger(CAGStockSearchController.class); + + private final ObservableList stockRead; + private final SortedList stockSorted; + private final ObservableList stock; + private final SimpleObjectProperty stockPages; + private final CAGClientServiceType client; + + private CAGStockSearchController( + final CAGClientServiceType inClient) + { + this.client = + Objects.requireNonNull(inClient, "client"); + + this.stock = + FXCollections.observableArrayList(); + this.stockSorted = + new SortedList<>(this.stock); + this.stockRead = + FXCollections.unmodifiableObservableList(this.stock); + + this.stockPages = + new SimpleObjectProperty<>(CAGPageRange.zero()); + } + + /** + * @param client The client + * + * @return A stock search controller. + */ + + public static CAGStockSearchControllerType create( + final CAGClientServiceType client) + { + final var controller = new CAGStockSearchController(client); + client.status().subscribe((oldStatus, newStatus) -> { + controller.onClientStatusChanged(); + }); + return controller; + } + + private void onClientStatusChanged() + { + this.stock.clear(); + this.stockPages.set(CAGPageRange.zero()); + } + + @Override + public ObservableList stockView() + { + return this.stockRead; + } + + @Override + public SortedList stockViewSorted() + { + return this.stockSorted; + } + + @Override + public void stockSearchBegin( + final CAStockSearchParameters searchParameters) + { + final var future = + this.client.execute( + new CAICommandStockSearchBegin(searchParameters) + ); + + future.thenAccept(response -> { + Platform.runLater(() -> { + final var data = + response.data(); + + final var newItemPage = + new ArrayList<>(data.items()); + + LOG.debug("Received {} stock", newItemPage.size()); + this.stockPages.set( + new CAGPageRange( + (long) data.pageIndex(), + (long) data.pageCount() + ) + ); + this.stock.setAll(newItemPage); + }); + }); + } +} diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerStockType.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchControllerType.java similarity index 93% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerStockType.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchControllerType.java index 2be32ff..ee23506 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGControllerStockType.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchControllerType.java @@ -19,7 +19,6 @@ import com.io7m.cardant.model.CAStockOccurrenceType; import com.io7m.cardant.model.CAStockSearchParameters; -import com.io7m.repetoir.core.RPServiceType; import javafx.collections.ObservableList; import javafx.collections.transformation.SortedList; @@ -27,8 +26,7 @@ * Stock methods for the controller. */ -public interface CAGControllerStockType - extends RPServiceType +public interface CAGStockSearchControllerType { /** * @return The stock for the current search query diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockSearchView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchView.java similarity index 63% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockSearchView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchView.java index b3a20c0..e372119 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockSearchView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockSearchView.java @@ -19,7 +19,11 @@ import com.io7m.cardant.model.CAIncludeDeleted; import com.io7m.cardant.model.CAItemID; +import com.io7m.cardant.model.CALocationID; import com.io7m.cardant.model.CALocationMatchType; +import com.io7m.cardant.model.CALocationMatchType.CALocationExact; +import com.io7m.cardant.model.CALocationMatchType.CALocationWithDescendants; +import com.io7m.cardant.model.CALocationMatchType.CALocationsAll; import com.io7m.cardant.model.CAStockOccurrenceKind; import com.io7m.cardant.model.CAStockSearchParameters; import com.io7m.cardant.model.comparisons.CAComparisonExactType; @@ -28,16 +32,17 @@ import javafx.fxml.FXML; import javafx.scene.control.Accordion; import javafx.scene.control.Alert; +import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceBox; import javafx.scene.control.TextField; import javafx.scene.control.TitledPane; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.io.IOException; import java.net.URL; import java.util.HashSet; +import java.util.Objects; import java.util.ResourceBundle; import java.util.UUID; @@ -53,23 +58,25 @@ * The main stock search view. */ -public final class CAGMainStockSearchView +public final class CAGStockSearchView implements CAGViewType { - private static final Logger LOG = - LoggerFactory.getLogger(CAGMainStockSearchView.class); - private final CAGStringsType strings; - private final CAGControllerType controller; + private final CAGLocationSelectDialogs locationSelectDialogs; + private final CAGClientServiceType client; + private final CAGItemSelectDialogs itemSelectDialogs; + private CAGStockSearchControllerType controller; @FXML private ChoiceBox locationMatch; @FXML private TextField locationField; - @FXML private ChoiceBox idMatch; - @FXML private TextField idField; + @FXML private ChoiceBox itemMatch; + @FXML private TextField itemField; @FXML private CheckBox includeSerial; @FXML private CheckBox includeSets; @FXML private TitledPane basicParameters; @FXML private Accordion accordion; + @FXML private Button locationSelect; + @FXML private Button itemSelect; /** * The main stock search view. @@ -77,13 +84,30 @@ public final class CAGMainStockSearchView * @param services The service directory */ - public CAGMainStockSearchView( + public CAGStockSearchView( final RPServiceDirectoryType services) { this.strings = services.requireService(CAGStringsType.class); + this.client = + services.requireService(CAGClientServiceType.class); + this.locationSelectDialogs = + services.requireService(CAGLocationSelectDialogs.class); + this.itemSelectDialogs = + services.requireService(CAGItemSelectDialogs.class); + } + + /** + * Set the controllers. + * + * @param inController The controller + */ + + public void setControllers( + final CAGStockSearchControllerType inController) + { this.controller = - services.requireService(CAGControllerType.class); + Objects.requireNonNull(inController, "controller"); } private void clearParameters() @@ -98,16 +122,16 @@ public void initialize( { this.accordion.setExpandedPane(this.basicParameters); - this.idField.setDisable(true); + this.itemField.setDisable(true); this.locationField.setDisable(true); - this.idMatch.setItems( + this.itemMatch.setItems( FXCollections.observableArrayList(CAGItemIDMatchKind.values())); - this.idMatch.setConverter( + this.itemMatch.setConverter( new CAGItemIDMatchConverter(this.strings)); - this.idMatch.getSelectionModel() + this.itemMatch.getSelectionModel() .select(CAGItemIDMatchKind.ANY); - this.idMatch.getSelectionModel() + this.itemMatch.getSelectionModel() .selectedItemProperty() .addListener((observable, oldValue, newValue) -> { this.onIDMatchChanged(newValue); @@ -132,9 +156,11 @@ private void onLocationMatchChanged( switch (newValue) { case ANY -> { this.locationField.setDisable(true); + this.locationSelect.setDisable(true); } case EXACTLY, DESCENDANTS_OF -> { this.locationField.setDisable(false); + this.locationSelect.setDisable(false); } } } @@ -144,18 +170,59 @@ private void onIDMatchChanged( { switch (newValue) { case ANY -> { - this.idField.setDisable(true); + this.itemField.setDisable(true); + this.itemSelect.setDisable(true); } case EQUAL_TO, NOT_EQUAL_TO -> { - this.idField.setDisable(false); + this.itemField.setDisable(false); + this.itemSelect.setDisable(false); } } } + @FXML + private void onItemSelectSelected() + throws IOException + { + final var searchController = + CAGItemSearchController.create(this.client); + final var detailsController = + CAGItemDetailsController.create(this.client); + + this.itemSelectDialogs.openDialogAndWait( + new CAGItemSelectDialogArguments( + detailsController, + searchController + ) + ); + + final var selectedLocationOpt = + detailsController.itemSelected() + .summary() + .getValue(); + + selectedLocationOpt.ifPresent(item -> { + this.itemField.setText(item.id().displayId()); + }); + } + @FXML private void onLocationSelectSelected() + throws IOException { + final var locationController = + CAGLocationTreeController.create(this.client); + + this.locationSelectDialogs.openDialogAndWait(locationController); + + final var selectedLocationOpt = + locationController.locationSelected() + .summary() + .getValue(); + selectedLocationOpt.ifPresent(location -> { + this.locationField.setText(location.id().displayId()); + }); } @FXML @@ -171,7 +238,7 @@ private void onSearchSelected() this.controller.stockSearchBegin( new CAStockSearchParameters( - new CALocationMatchType.CALocationsAll(), + this.locationSelection(), this.itemIDSelection(), occurrences, CAIncludeDeleted.INCLUDE_ONLY_LIVE, @@ -180,20 +247,39 @@ private void onSearchSelected() ); } + private CALocationMatchType locationSelection() + { + return switch (this.locationMatch.getValue()) { + case ANY -> { + yield new CALocationsAll(); + } + case EXACTLY -> { + yield new CALocationExact( + CALocationID.of(this.locationField.getText().trim()) + ); + } + case DESCENDANTS_OF -> { + yield new CALocationWithDescendants( + CALocationID.of(this.locationField.getText().trim()) + ); + } + }; + } + private CAComparisonExactType itemIDSelection() { - return switch (this.idMatch.getValue()) { + return switch (this.itemMatch.getValue()) { case ANY -> { yield new CAComparisonExactType.Anything<>(); } case EQUAL_TO -> { yield new CAComparisonExactType.IsEqualTo<>( - new CAItemID(UUID.fromString(this.idField.getText().trim())) + new CAItemID(UUID.fromString(this.itemField.getText().trim())) ); } case NOT_EQUAL_TO -> { yield new CAComparisonExactType.IsNotEqualTo<>( - new CAItemID(UUID.fromString(this.idField.getText().trim())) + new CAItemID(UUID.fromString(this.itemField.getText().trim())) ); } }; diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableItemCell.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableItemCell.java index 4226353..f8d89fa 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableItemCell.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableItemCell.java @@ -49,12 +49,10 @@ public final class CAGStockTableItemCell /** * A cell displaying a stock item. * - * @param controller The controller * @param strings The string resources */ public CAGStockTableItemCell( - final CAGControllerType controller, final CAGStringsType strings) { this.menuItemCopy = @@ -70,8 +68,7 @@ public CAGStockTableItemCell( this.menuItemCopy.setOnAction(event -> this.textField.copy()); this.menuItemSelectAll.setOnAction(event -> this.textField.selectAll()); this.menuItemOpen.setOnAction(event -> { - controller.itemGet(this.itemNow); - controller.tabSelect(CAGTabKind.TAB_ITEMS); + }); this.customMenu = diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableLocationCell.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableLocationCell.java index e9cdd31..0948272 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableLocationCell.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableLocationCell.java @@ -51,12 +51,10 @@ public final class CAGStockTableLocationCell /** * A cell displaying a location. * - * @param controller The controller * @param strings The string resources */ public CAGStockTableLocationCell( - final CAGControllerType controller, final CAGStringsType strings) { this.textField.setTooltip(this.tooltip); @@ -74,8 +72,7 @@ public CAGStockTableLocationCell( this.menuItemCopy.setOnAction(event -> this.textField.copy()); this.menuItemSelectAll.setOnAction(event -> this.textField.selectAll()); this.menuItemLocOpen.setOnAction(event -> { - controller.locationGet(this.locationNow.id()); - controller.tabSelect(CAGTabKind.TAB_LOCATIONS); + throw new IllegalStateException(); }); this.customMenu = diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockTableView.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableView.java similarity index 82% rename from com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockTableView.java rename to com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableView.java index 10127de..4136e4a 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGMainStockTableView.java +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGStockTableView.java @@ -38,13 +38,13 @@ * The table of stock. */ -public final class CAGMainStockTableView +public final class CAGStockTableView implements CAGViewType { - private final CAGControllerType controller; private final CAGStringsType strings; + private CAGStockSearchControllerType controller; - @FXML private TableView mainStockTable; + @FXML private TableView stockTable; @FXML private TableColumn colLocation; @FXML private TableColumn colItem; @FXML private TableColumn colName; @@ -53,6 +53,7 @@ public final class CAGMainStockTableView @FXML private Label resultsLabel; @FXML private Button itemAdd; @FXML private Button itemRemove; + @FXML private Button itemMove; /** * The table of stock. @@ -60,17 +61,34 @@ public final class CAGMainStockTableView * @param inServices The service directory */ - public CAGMainStockTableView( + public CAGStockTableView( final RPServiceDirectoryType inServices) { Objects.requireNonNull(inServices, "services"); - this.controller = - inServices.requireService(CAGControllerType.class); this.strings = inServices.requireService(CAGStringsType.class); } + /** + * Set the controllers. + * + * @param inController The controller + */ + + public void setControllers( + final CAGStockSearchControllerType inController) + { + this.controller = + Objects.requireNonNull(inController, "controller"); + + this.controller.stockView() + .addListener(this::onStocksViewChanged); + + this.stockTable.setItems( + this.controller.stockViewSorted()); + } + @Override public void initialize( final URL url, @@ -78,15 +96,15 @@ public void initialize( { this.resultsLabel.setText(""); - this.mainStockTable.setPlaceholder(new Label("")); - this.mainStockTable.setSelectionModel(null); + this.stockTable.setPlaceholder(new Label("")); + this.stockTable.setSelectionModel(null); this.colLocation.setReorderable(false); this.colLocation.setCellValueFactory(param -> { return new ReadOnlyObjectWrapper<>(param.getValue().location()); }); this.colLocation.setCellFactory(param -> { - return new CAGStockTableLocationCell(this.controller, this.strings); + return new CAGStockTableLocationCell(this.strings); }); this.colItem.setReorderable(false); @@ -94,7 +112,7 @@ public void initialize( return new ReadOnlyObjectWrapper<>(param.getValue().item().id()); }); this.colItem.setCellFactory(param -> { - return new CAGStockTableItemCell(this.controller, this.strings); + return new CAGStockTableItemCell(this.strings); }); this.colName.setReorderable(false); @@ -120,11 +138,6 @@ public void initialize( this.colCount.setCellFactory(param -> { return new CAGStockTableCountCell(); }); - - this.controller.stockView() - .addListener(this::onStocksViewChanged); - - this.mainStockTable.setItems(this.controller.stockViewSorted()); } private void onStocksViewChanged( @@ -146,13 +159,19 @@ private void onPageNextSelected() } @FXML - private void onItemAddSelected() + private void onStockAddSelected() + { + + } + + @FXML + private void onStockMoveSelected() { } @FXML - private void onItemRemoveSelected() + private void onStockRemoveSelected() { } diff --git a/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGUnit.java b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGUnit.java new file mode 100644 index 0000000..4f88a73 --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/java/com/io7m/cardant_gui/ui/internal/CAGUnit.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * The unit type. + */ + +public enum CAGUnit +{ + /** + * The unit value. + */ + + UNIT +} 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 fc1690c..c763660 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 @@ -25,26 +25,32 @@ public sealed interface CAGViewType extends Initializable permits CAGFileCreateView, + CAGFileListView, + CAGFileSearchView, + CAGFileSelectView, CAGItemAttachmentAddView, + CAGItemDetailsView, + CAGItemSearchView, + CAGItemSelectView, + CAGItemTableView, CAGLocationAttachmentAddView, + CAGLocationDetailsView, CAGLocationReparentView, + CAGLocationSelectView, + CAGLocationTreeView, CAGLoginView, CAGMainAuditSearchView, CAGMainAuditTableView, - CAGMainFileListView, - CAGMainFileSearchView, - CAGMainItemDetailsView, - CAGMainItemSearchView, - CAGMainItemTableView, - CAGMainLocationDetailsView, - CAGMainLocationSearchView, - CAGMainLocationTableView, - CAGMainStockSearchView, - CAGMainStockTableView, + CAGMainFilesView, + CAGMainItemsView, + CAGMainLocationsView, + CAGMainStockView, CAGMainTypePackageDetailsView, CAGMainTypePackageSearchView, CAGMainTypePackageTableView, - CAGMainView + CAGMainView, + CAGStockSearchView, + CAGStockTableView { } diff --git a/com.io7m.cardant_gui.ui/src/main/java/module-info.java b/com.io7m.cardant_gui.ui/src/main/java/module-info.java index 916242e..25b2a68 100644 --- a/com.io7m.cardant_gui.ui/src/main/java/module-info.java +++ b/com.io7m.cardant_gui.ui/src/main/java/module-info.java @@ -37,6 +37,7 @@ requires com.io7m.darco.api; requires com.io7m.darco.sqlite; requires com.io7m.jade.api; + requires com.io7m.jaffirm.core; requires com.io7m.jattribute.core; requires com.io7m.jmulticlose.core; requires com.io7m.jsx.prettyprint; 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 4087874..1fb9a13 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 @@ -38,6 +38,7 @@ cardant.cancel=Cancel cardant.copy=Copy cardant.description=Description cardant.download=Download +cardant.error.relationEmpty=The relation field cannot be empty. cardant.exact.any=Is anything. cardant.exact.equalTo=Is equal to: cardant.exact.notEqualTo=Is not equal to: @@ -51,6 +52,7 @@ cardant.fileCreate.select=Select... cardant.fileCreate.upload=Upload cardant.fileSearch.pageOf=Page {0} of {1} cardant.fileSearch.sizeRange=Size Range +cardant.files.selectTitle=Select a file... cardant.files.transfer.downloading=Downloading... cardant.files.transfer.idle=No transfers are currently in progress. cardant.files.transfer.uploading=Uploading... @@ -89,6 +91,7 @@ cardant.itemSearch.pageOf=Page {0} of {1} cardant.itemSearch.search=Search cardant.itemSearch.searchClear=Clear cardant.item_id=Item ID +cardant.items.selectTitle=Select an item... cardant.items=Items cardant.localFile=File cardant.location.createTitle=Specify a location name. @@ -97,6 +100,7 @@ cardant.locationDetails.nameSet=Set... cardant.locationReparent.title=Select a new location parent. 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.selectTitle=Select a location... cardant.locations=Locations cardant.login.bookmark.createMain=Please enter a name for the bookmark. cardant.login.bookmark.createTitle=Create Bookmark @@ -123,6 +127,7 @@ 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.select=Select cardant.selectAll=Select All cardant.serial=Serial cardant.setComparison.any=Are anything. @@ -148,11 +153,14 @@ 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.files.select=Select a file. cardant.tooltip.items.add=Add a new item. cardant.tooltip.items.remove=Remove item(s). +cardant.tooltip.items.select=Select an item. cardant.tooltip.locations.add=Add a new location. cardant.tooltip.locations.remove=Remove location(s). cardant.tooltip.locations.reparent=Reparent a location. +cardant.tooltip.locations.select=Select a location. 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! @@ -168,6 +176,9 @@ 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.stock.add=Add new stock. +cardant.tooltip.stock.move=Move stock. +cardant.tooltip.stock.remove=Remove stock. cardant.tooltip.typeAssign=Assign a type. cardant.tooltip.typePackages.packageAdd=Add a new type package. cardant.tooltip.typePackages.packageRemove=Remove the selected type packages. 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 c762686..ac3d475 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 @@ -21,7 +21,7 @@ - + @@ -73,19 +73,19 @@ - + - + - + - - - - + + + + + - + - - - - - - + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locationSelect.fxml b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locationSelect.fxml new file mode 100644 index 0000000..829ea0d --- /dev/null +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/locationSelect.fxml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + @@ -86,12 +103,12 @@ - + + - - + diff --git a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/typePackageSearch.fxml b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/typePackageSearch.fxml index 9a9d2c1..bf83e64 100644 --- a/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/typePackageSearch.fxml +++ b/com.io7m.cardant_gui.ui/src/main/resources/com/io7m/cardant_gui/ui/internal/typePackageSearch.fxml @@ -19,7 +19,8 @@ - + @@ -56,19 +57,19 @@ - + - +