From 7394a18d3a7c1d544dc31e294fa0e0de81c09d57 Mon Sep 17 00:00:00 2001 From: Frederick Vollbrecht <39002042+vollbrecht-work@users.noreply.github.com> Date: Thu, 18 Sep 2025 09:56:12 +0200 Subject: [PATCH 1/5] New ConceptController with Single SemanticFormMenu for all SemanticForms --- .../klwindows/concept/ConceptKlWindow.java | 193 +++- .../ikm/komet/kview/mvvm/model/DescrName.java | 2 - .../mvvm/view/concept/ConceptController.java | 834 +++++++++++------- .../kview/mvvm/view/concept/ConceptNode.java | 300 ++++--- .../concept/ConceptPropertiesController.java | 351 ++++++++ .../ConceptPropertiesMenuController.java | 50 ++ .../ConceptPropertiesNameFormController.java | 238 +++++ .../ConceptPropertiesNameMenuController.java | 48 + .../mvvm/view/journal/JournalController.java | 3 +- .../properties/HistoryChangeController.java | 3 + .../mvvm/viewmodel/ConceptViewModel.java | 2 +- .../mvvm/viewmodel/ConceptViewModelNext.java | 368 ++++++++ .../mvvm/viewmodel/DescrNameViewModel.java | 70 +- .../viewmodel/DescrNameViewModelNext.java | 250 ++++++ kview/src/main/java/module-info.java | 4 +- .../mvvm/view/concept/concept-details.fxml | 2 +- .../mvvm/view/concept/concept-prop-menu.fxml | 83 ++ .../view/concept/concept-prop-name-form.fxml | 205 +++++ .../view/concept/concept-prop-name-menu.fxml | 62 ++ .../mvvm/view/concept/concept-properties.fxml | 77 ++ 20 files changed, 2607 insertions(+), 538 deletions(-) create mode 100644 kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesController.java create mode 100644 kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesMenuController.java create mode 100644 kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameFormController.java create mode 100644 kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameMenuController.java create mode 100644 kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModelNext.java create mode 100644 kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModelNext.java create mode 100644 kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-prop-menu.fxml create mode 100644 kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-prop-name-form.fxml create mode 100644 kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-prop-name-menu.fxml create mode 100644 kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-properties.fxml diff --git a/kview/src/main/java/dev/ikm/komet/kview/klwindows/concept/ConceptKlWindow.java b/kview/src/main/java/dev/ikm/komet/kview/klwindows/concept/ConceptKlWindow.java index 4ee13394ab..2a441a7744 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/klwindows/concept/ConceptKlWindow.java +++ b/kview/src/main/java/dev/ikm/komet/kview/klwindows/concept/ConceptKlWindow.java @@ -19,26 +19,42 @@ import dev.ikm.komet.framework.activity.ActivityStream; import dev.ikm.komet.framework.activity.ActivityStreamOption; import dev.ikm.komet.framework.activity.ActivityStreams; +import dev.ikm.komet.framework.controls.EntityLabelWithDragAndDrop; import dev.ikm.komet.framework.view.ViewProperties; import dev.ikm.komet.kview.klwindows.AbstractEntityChapterKlWindow; import dev.ikm.komet.kview.klwindows.EntityKlWindowState; import dev.ikm.komet.kview.klwindows.EntityKlWindowType; import dev.ikm.komet.kview.klwindows.EntityKlWindowTypes; +import dev.ikm.komet.kview.mvvm.view.concept.ConceptController; import dev.ikm.komet.kview.mvvm.view.concept.ConceptNode; import dev.ikm.komet.kview.mvvm.view.concept.ConceptNodeFactory; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext.ConceptPropertyKeys; import dev.ikm.komet.preferences.KometPreferences; import dev.ikm.tinkar.common.alert.AlertStreams; +import dev.ikm.tinkar.common.flow.FlowSubscriber; import dev.ikm.tinkar.common.id.PublicIdStringKey; import dev.ikm.tinkar.common.id.PublicIds; import dev.ikm.tinkar.common.util.uuid.UuidT5Generator; +import dev.ikm.tinkar.entity.Entity; import dev.ikm.tinkar.terms.EntityFacade; +import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ChangeListener; +import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; +import org.carlfx.cognitive.loader.Config; +import org.carlfx.cognitive.loader.FXMLMvvmLoader; +import org.carlfx.cognitive.loader.JFXNode; +import org.carlfx.cognitive.loader.NamedVm; import org.eclipse.collections.api.factory.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.UUID; +import static dev.ikm.komet.kview.fxutils.CssHelper.defaultStyleSheet; +import static dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModel.CURRENT_ENTITY; import static dev.ikm.komet.kview.mvvm.viewmodel.FormViewModel.CREATE; import static dev.ikm.komet.kview.mvvm.viewmodel.FormViewModel.MODE; @@ -55,6 +71,14 @@ public class ConceptKlWindow extends AbstractEntityChapterKlWindow { private final ConceptNode conceptNode; private final PublicIdStringKey detailsActivityStreamKey; + private final JFXNode conceptJFXNode; + private final ConceptViewModelNext conceptViewModelNext = new ConceptViewModelNext(); + + // + protected final SimpleObjectProperty entityFocusProperty = new SimpleObjectProperty<>(); + protected FlowSubscriber invalidationSubscriber; + protected ChangeListener entityFocusChangeListener; + // /** * Constructs a new {@code ConceptKlWindow}. * @@ -76,37 +100,81 @@ public ConceptKlWindow(UUID journalTopic, EntityFacade entityFacade, // Create a unique key for the details activity stream. this.detailsActivityStreamKey = new PublicIdStringKey<>(PublicIds.of(uuid.toString()), uniqueDetailsTopic); - ActivityStreams.create(detailsActivityStreamKey); + ActivityStreams.create(detailsActivityStreamKey); // TODO: we ignore the return value here?? test what we get returned to understand the actual streamKey type + + // create a unique topic for each concept detail instance + UUID conceptTopic = UUID.randomUUID(); + + // Create a ConceptViewModel with preSet Propertys + + NamedVm conceptViewModelNext = new NamedVm("conceptViewModelNext", this.conceptViewModelNext); + conceptViewModelNext.viewModel() + .setValue(ConceptPropertyKeys.VIEW_PROPERTIES, viewProperties) + .setValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT, isCreateMode) + .setValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE, entityFacade) + .setValue(ConceptPropertyKeys.ASOCIATED_JOURNAL_WINDOW_TOPIC, journalTopic) + .setValue(ConceptPropertyKeys.THIS_UNIQUE_CONCEPT_TOPIC, conceptTopic) + .reset(); // make sure that View values are init + + LOG.info(conceptViewModelNext.viewModel().toString()); + + Config conceptConfig = new Config(ConceptController.class.getResource(ConceptController.CONCEPT_DETAILS_VIEW_FXML_FILE)).addNamedViewModel(conceptViewModelNext); + + this.conceptJFXNode = FXMLMvvmLoader.make(conceptConfig); + + LOG.info(conceptViewModelNext.viewModel().toString()); + this.conceptJFXNode.controller().updateView(); + + // Programmatically change CSS Theme + this.conceptJFXNode.node().getStylesheets().clear(); + String styleSheet = defaultStyleSheet(); + this.conceptJFXNode.node().getStylesheets().add(styleSheet); + + // Initialize the DetailsNode with a factory. KometNodeFactory conceptDetailsNodeFactory = new ConceptNodeFactory(); this.conceptNode = (ConceptNode) conceptDetailsNodeFactory.create(viewProperties.parentView(), - detailsActivityStreamKey, + detailsActivityStreamKey, // TODO: understand - we create a activity stream and we asociated the PUBLISH option to it. The two other options are subscribe / sync ActivityStreamOption.PUBLISH.keyForOption(), AlertStreams.ROOT_ALERT_STREAM_KEY, true, journalTopic); // Configure the details node if we are in create mode. - if (isCreateMode) { - conceptNode.getConceptDetailsViewController() - .getConceptViewModel() - .setPropertyValue(MODE, CREATE); - conceptNode.getConceptDetailsViewController().updateView(); - } +// if (isCreateMode) { +// conceptNode.getConceptDetailsViewController() +// .getConceptViewModel() +// .setPropertyValue(MODE, CREATE); +// conceptNode.getConceptDetailsViewController().updateView(); +// } + // TODO: wtf is this late update mechanism // This will refresh the Concept details, history, timeline conceptNode.handleActivity(Lists.immutable.of(entityFacade)); // Getting the concept window pane - this.paneWindow = (Pane) conceptNode.getNode(); + paneWindow = this.conceptJFXNode.node(); // Set the onClose callback for the details window. - conceptNode.getConceptDetailsViewController().setOnCloseConceptWindow(detailsController -> { - ActivityStreams.delete(detailsActivityStreamKey); - getOnClose().ifPresent(Runnable::run); - // TODO more clean up such as view models and listeners just in case (memory). + conceptJFXNode.controller().setOnCloseConceptWindow( + detailsController -> { + ActivityStreams.delete(detailsActivityStreamKey); + getOnClose().ifPresent(Runnable::run); + // TODO more clean up such as view models and listeners just in case (memory). + } + ); + + this.conceptViewModelNext.getViewProperties().nodeView().addListener((obs, oldViewCoord, newViewCoord) -> { + if (newViewCoord != null) { + LOG.info("refresh concept window when view coordinate has changed." + newViewCoord); + //updateView(); // this was the ConceptController refresh + this.conceptViewModelNext.reset(); // TODO: verify how this is working + } }); + + + //conceptNode.getConceptDetailsViewController().setOnCloseConceptWindow(); } /** @@ -118,14 +186,6 @@ public PublicIdStringKey getDetailsActivityStreamKey() { return detailsActivityStreamKey; } - /** - * Returns the {@link ConceptNode} associated with this window. - * - * @return the {@link ConceptNode} used for concept viewing or editing - */ - public ConceptNode getDetailsNode() { - return conceptNode; - } @Override public EntityKlWindowType getWindowType() { @@ -134,25 +194,104 @@ public EntityKlWindowType getWindowType() { @Override protected boolean isPropertyPanelOpen() { - return conceptNode.getConceptDetailsViewController().isPropertiesPanelOpen(); + return conceptJFXNode.controller().isPropertiesPanelOpen(); } @Override protected void setPropertyPanelOpen(boolean isOpen) { - conceptNode.getConceptDetailsViewController().setPropertiesPanelOpen(isOpen); + conceptJFXNode.controller().setPropertiesPanelOpen(isOpen); } @Override protected String selectedPropertyPanel() { - String pane = conceptNode.getPropertiesViewController().selectedView(); - LOG.debug("saving with Concept " + pane); - return pane; + // TODO: get viewModel via Concept node and save / restore property window that way + //String pane = conceptNode.getPropertiesViewController().selectedView(); + //LOG.debug("saving with Concept " + pane); + //return pane; + return "TODO"; } @Override protected void setSelectedPropertyPanel(String selectedPanel) { + // TODO: get viewModel via Concept node and save / restore property window that way LOG.debug("restoring pane with "+ selectedPanel); - conceptNode.getPropertiesViewController().restoreSelectedView(selectedPanel); + //conceptNode.getPropertiesViewController().restoreSelectedView(selectedPanel); + } + + // For ConceptNode in the case that Concept should not be displayed on the journalView + public BorderPane getConceptBorderPane() { + return this.conceptJFXNode.node(); + } + + public ConceptController getConceptController() { + return this.conceptJFXNode.controller(); + } + + private void listenOnEntityFacadeUpdate() { + // remove later when closing + this.entityFocusChangeListener = (observable, oldEntityFacade, newEntityFacade) -> { + if (newEntityFacade != null) { + + if (newEntityFacade == oldEntityFacade) { + LOG.info("WE GOT UPDATE ON ENTITY FACADE WITHOUT ANYTHING CHANGING!"); + } + + // TODO: what was ConceptNode title and tooltip used for + //titleProperty.set(viewProperties.calculator().getPreferredDescriptionTextWithFallbackOrNid(newEntityFacade)); + //toolTipTextProperty.set(viewProperties.calculator().getFullyQualifiedDescriptionTextWithFallbackOrNid(newEntityFacade)); + + // forceupdating the Model with new state + conceptViewModelNext.setValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE, newEntityFacade); + + + // Populate Detail View +// if (getConceptDetailsViewController() != null) { +// getConceptDetailsViewController() +// .getConceptViewModel() +// .setPropertyValue(CURRENT_ENTITY, newEntityFacade); +// getConceptDetailsViewController().updateView(); +// } + + // Populate Properties View + // +// if (getPropertiesViewController() != null) { +// getPropertiesViewController().updateModel(viewProperties, newEntityFacade); +// getPropertiesViewController().updateView(); +// } + + // Populate Timeline View //TODO: dont forget abotu timeline reset here +// if (getTimelineViewController() != null) { +// getTimelineViewController().resetConfigPathAndModules(); +// getTimelineViewController().updateModel(viewProperties, newEntityFacade); +// getTimelineViewController().updateView(); +// } + + } else { + // Show a blank view (nothing selected) + //titleProperty.set(EntityLabelWithDragAndDrop.EMPTY_TEXT); + //toolTipTextProperty.set(EntityLabelWithDragAndDrop.EMPTY_TEXT); + conceptViewModelNext.setValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE, newEntityFacade); + //getConceptDetailsViewController().clearView(); + //getPropertiesViewController().clearView(); // does nothing currently + // getPropertiesViewController().updateModel(viewProperties, newEntityFacade); // TODO: updates history/hirarchy controller + } + + }; + + // When a new entity is selected populate the view. An entity has been selected upstream (activity stream) + this.entityFocusProperty.addListener(this.entityFocusChangeListener); + + // If database updates the underlying entity, this will do a force update of the UI. + this.invalidationSubscriber = new FlowSubscriber<>(nid -> { + if (entityFocusProperty.get() != null && entityFocusProperty.get().nid() == nid) { + // component has changed, need to update. + Platform.runLater(() -> entityFocusProperty.set(null)); + Platform.runLater(() -> entityFocusProperty.set(Entity.provider().getEntityFast(nid))); + } + }); + + // Register to the Entity Service + Entity.provider().addSubscriberWithWeakReference(this.invalidationSubscriber); } } diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/model/DescrName.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/model/DescrName.java index 2670557818..f740a83731 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/mvvm/model/DescrName.java +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/model/DescrName.java @@ -40,8 +40,6 @@ public class DescrName { private ConceptEntity language; - - // the public ID of the description semantic that this class represents private PublicId semanticPublicId; diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptController.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptController.java index d9b6250903..3774da4c7e 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptController.java +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptController.java @@ -33,8 +33,9 @@ import dev.ikm.komet.kview.fxutils.SlideOutTrayHelper; import dev.ikm.komet.kview.mvvm.model.DescrName; import dev.ikm.komet.kview.mvvm.view.journal.VerticallyFilledPane; -import dev.ikm.komet.kview.mvvm.view.properties.PropertiesController; -import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModel; +import dev.ikm.komet.kview.mvvm.view.timeline.TimelineController; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext.*; import dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase; import dev.ikm.komet.preferences.KometPreferences; import dev.ikm.tinkar.common.id.PublicId; @@ -48,12 +49,15 @@ import dev.ikm.tinkar.events.Subscriber; import dev.ikm.tinkar.terms.*; import javafx.application.Platform; -import javafx.beans.InvalidationListener; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ObservableList; import javafx.css.PseudoClass; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.geometry.Side; import javafx.scene.Node; import javafx.scene.control.*; @@ -64,13 +68,13 @@ import javafx.scene.shape.SVGPath; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; -import org.carlfx.cognitive.loader.InjectViewModel; -import org.carlfx.cognitive.viewmodel.ValidationViewModel; +import org.carlfx.cognitive.loader.*; import org.eclipse.collections.api.factory.Lists; import org.eclipse.collections.api.list.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; @@ -80,6 +84,7 @@ import java.util.function.Consumer; import static dev.ikm.komet.kview.events.ClosePropertiesPanelEvent.CLOSE_PROPERTIES; +import static dev.ikm.komet.kview.fxutils.CssHelper.defaultStyleSheet; import static dev.ikm.komet.kview.fxutils.IconsHelper.IconType.ATTACHMENT; import static dev.ikm.komet.kview.fxutils.IconsHelper.IconType.COMMENTS; import static dev.ikm.komet.kview.fxutils.MenuHelper.fireContextMenuEvent; @@ -89,9 +94,7 @@ import static dev.ikm.komet.kview.fxutils.window.DraggableSupport.removeDraggableNodes; import static dev.ikm.komet.kview.mvvm.model.DataModelHelper.*; import static dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModel.*; -import static dev.ikm.komet.kview.mvvm.viewmodel.FormViewModel.MODE; import static dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase.Properties.FORM_TIME_TEXT; -import static dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase.Properties.IS_CONFIRMED_OR_SUBMITTED; import static dev.ikm.tinkar.common.service.PrimitiveData.PREMUNDANE_TIME; import static dev.ikm.tinkar.common.util.time.DateTimeUtil.PREMUNDANE; import static dev.ikm.tinkar.coordinate.stamp.StampFields.*; @@ -102,6 +105,9 @@ import static dev.ikm.tinkar.terms.TinkarTerm.*; public class ConceptController { + public static final String CONCEPT_DETAILS_VIEW_FXML_FILE = "concept-details.fxml"; + + private static final String CONCEPT_TIMELINE_VIEW_FXML_FILE = "timeline.fxml"; private static final PseudoClass STAMP_SELECTED = PseudoClass.getPseudoClass("selected"); @@ -237,12 +243,22 @@ public class ConceptController { /** * A function from the caller. This class passes a boolean true if classifier button is pressed invoke caller's function to be returned a view. */ + + // TODO: feature or bug?: this links the "reasonorToggleButton" from journal.fxml / JournalController with this button + // e.g if this controller toggle is activated -> slideOut also the global reasonnerToggleButton + // slideIn is the same logic. E.g if local state is different make the globalState the same + // if both are the same state nothing changes + + // this functionality is only used in Concept. Not in pattern/semantic private Consumer reasonerResultsControllerConsumer; - private PropertiesController propertiesController; + + +// @InjectViewModel +// private ConceptViewModel conceptViewModel; @InjectViewModel - private ConceptViewModel conceptViewModel; + private ConceptViewModelNext conceptViewModelNext; private EvtBus eventBus; private UUID conceptTopic; @@ -278,59 +294,40 @@ public class ConceptController { private boolean isUpdatingStampSelection = false; + // + private JFXNode propertiesJFXNode; + + private TimelineController timelineController; + private Pane timelinePane; + public ConceptController() { } - public ConceptController(UUID conceptTopic) { - this.conceptTopic = conceptTopic; - } +// public ConceptController(UUID conceptTopic) { +// this.conceptTopic = conceptTopic; +// } @FXML public void initialize() { - stampViewControl.selectedProperty().subscribe(this::onStampSelectionChanged); - identiconImageView.setOnContextMenuRequested(contextMenuEvent -> { - // query all available memberships (semantics having the purpose as 'membership', and no fields) - // query current concept's membership semantic records. - // build menuItems according to 'add' or 'remove' , style to look like figma designs style classes. - // show offset to the right of the identicon - ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); - EntityFacade currentConceptFacade = conceptViewModel.getPropertyValue(CURRENT_ENTITY); - List patterns = getMembershipPatterns(); - ContextMenu membershipContextMenu = new ContextMenu(); - membershipContextMenu.getStyleClass().add("kview-context-menu"); + setupNodes(); + + // attach properties and timeline node to slideout + addPaneToTray(timelinePane, timelineSlideoutTrayPane); + addPaneToTray(propertiesJFXNode.node(), propertiesSlideoutTrayPane); + + String styleSheet = defaultStyleSheet(); + propertiesJFXNode.node().getStylesheets().add(styleSheet); + timelinePane.getStylesheets().add(styleSheet); + + setupTimelineBindings(); + setupPropertyBindings(); + + setupDetailsBanner(); + + setupDetailsNameSemantics(); - Comparator patternMenuComparator = (m1, m2) -> m1.getText().compareToIgnoreCase(m2.getText()); - List addedMenuItems = new ArrayList<>(); - List removedMenuItems = new ArrayList<>(); - for (PatternEntityVersion pattern : patterns) { - MenuItem menuItem = new MenuItem(); - if (isInMembershipPattern(currentConceptFacade.nid(), pattern.nid(), viewCalculator)) { - menuItem.setText("Remove from " + pattern.entity().description()); - menuItem.setOnAction(evt -> removeFromMembershipPattern(currentConceptFacade.nid(), pattern.entity(), viewCalculator)); - addedMenuItems.add(menuItem); - } else { - menuItem.setText("Add to " + pattern.entity().description()); - menuItem.setOnAction(evt -> addToMembershipPattern(currentConceptFacade, pattern.entity(), viewCalculator)); - removedMenuItems.add(menuItem); - } - } - if (!addedMenuItems.isEmpty()) { - // sort the added (able to be removed) - addedMenuItems.sort(patternMenuComparator); - membershipContextMenu.getItems().addAll(addedMenuItems); - // then add a menu line separator - if (!removedMenuItems.isEmpty()) { - membershipContextMenu.getItems().add(new SeparatorMenuItem()); - } - } - // then add the sorted removed (that can be added) - removedMenuItems.sort(patternMenuComparator); - membershipContextMenu.getItems().addAll(removedMenuItems); - membershipContextMenu.show(identiconImageView, contextMenuEvent.getScreenX(), - contextMenuEvent.getSceneY() + identiconImageView.getFitHeight()); - }); Tooltip.install(fqnTitleText, conceptNameTooltip); @@ -381,78 +378,107 @@ public void initialize() { eventBus.subscribe(conceptTopic, ClosePropertiesPanelEvent.class, closePropertiesPanelEventSubscriber); // Listener when user enters a new fqn - ObservableList fullyQualifiedNames = getConceptViewModel().getObservableList(FULLY_QUALIFIED_NAMES); - fullyQualifiedNames.addListener((InvalidationListener) observable -> { - if (!fullyQualifiedNames.isEmpty()) { - DescrName fqnDescrName = fullyQualifiedNames.get(0); - updateConceptBanner(); + ObservableList fqnFacades = conceptViewModelNext.getObservableList(ConceptPropertyKeys.ASOCIATED_FQN_DESCRIPTION_SEMANTICS); + fqnFacades.subscribe( () -> { + SimpleBooleanProperty hasValidStampProp = conceptViewModelNext.getProperty(ConceptPropertyKeys.HAS_VALID_STAMP); + if (hasValidStampProp.getValue()) { + // TODO: make sure updateConceptBanner equivalent is there + updateFullyQualifiedNamesDescription(fqnFacades); + } else { + LOG.error("Stamp not valid -> Cannot update FQN description"); } - updateFullyQualifiedNamesDescription(fullyQualifiedNames); - }); - ObservableList otherNames = getConceptViewModel().getObservableList(OTHER_NAMES); - otherNames.addListener((InvalidationListener) obs -> { - if (!otherNames.isEmpty()) { - propertiesController.setHasOtherName(true); + }); +// ObservableList fullyQualifiedNames = getConceptViewModel().getObservableList(FULLY_QUALIFIED_NAMES); +// fullyQualifiedNames.addListener((InvalidationListener) observable -> { +// if (!fullyQualifiedNames.isEmpty()) { +// DescrName fqnDescrName = fullyQualifiedNames.get(0); +// updateConceptBanner(); +// } +// updateFullyQualifiedNamesDescription(fullyQualifiedNames); +// }); + + ObservableList otherNameFacades = conceptViewModelNext.getObservableList(ConceptViewModelNext.ConceptPropertyKeys.ASOCIATED_OTHER_NAME_DESCRIPTION_SEMANTICS); + otherNameFacades.subscribe( () -> { + SimpleBooleanProperty hasValidStampProp = conceptViewModelNext.getProperty(ConceptPropertyKeys.HAS_VALID_STAMP); + + if (hasValidStampProp.getValue()) { + updateOtherNamesDescription(otherNameFacades); + } else { + LOG.error("Stamp not valid -> Cannot update otherName description"); } - updateOtherNamesDescription(otherNames); }); - // Listens for events related to new fqn or other names added to this concept. Subscriber is responsible for - // the final create concept transaction. - createConceptEventSubscriber = evt -> { - DescrName descrName = evt.getModel(); +// // TODO: This is a wired binding we may want to reconstruct that +// ObservableList otherNames = getConceptViewModel().getObservableList(OTHER_NAMES); +// otherNames.addListener((InvalidationListener) obs -> { +// if (!otherNames.isEmpty()) { +// propertiesController.setHasOtherName(true); +// } +// updateOtherNamesDescription(otherNames); +// }); - if (getConceptViewModel() == null || descrName == null) { - LOG.warn("ViewModel should not be null. Event type:" + evt.getEventType()); - return; - } - if (CREATE.equals(conceptViewModel.getPropertyValue(MODE))) { - if (evt.getEventType() == CreateConceptEvent.ADD_FQN) { - fullyQualifiedNames.clear(); - fullyQualifiedNames.add(descrName); - } else if (evt.getEventType() == CreateConceptEvent.ADD_OTHER_NAME) { - otherNames.add(descrName); - }else if (evt.getEventType() == CreateConceptEvent.EDIT_OTHER_NAME) { // Since we are - updateOtherNamesDescription(otherNames); - }else { // Since we are - updateFullyQualifiedNamesDescription(fullyQualifiedNames); - } - // Attempts to write data - boolean isWritten = conceptViewModel.createConcept(propertiesController.getStampFormViewModel()); - // when written the mode changes to EDIT. - LOG.info("Is " + conceptViewModel + " created? " + isWritten); - if (isWritten) { - updateView(); - } - // remove 'Add Fully Qualified Name' from the menu - setUpDescriptionContextMenu(addDescriptionButton); - //TODO revisit: why should the mode ever be edit inside a create event? - } else if (EDIT.equals(conceptViewModel.getPropertyValue(MODE))){ - conceptViewModel.addOtherName(conceptViewModel.getViewProperties().calculator().viewCoordinateRecord().editCoordinate(), descrName); - otherNames.add(descrName); - } + // TODO: down below the outcommented code listend fro each AddFqn EditFqn AddOther and EditOther + // TODO: it provided mostly a DescrName with the call, that was then pluged back into + // TODO: what we just need is to handle the case when a user "creates a new FQN or otherName with the UI Button" + // TODO: since the case when a user clicks on a existing one is handeld via the above subscribers - }; - eventBus.subscribe(conceptTopic, CreateConceptEvent.class, createConceptEventSubscriber); + // Listens for events related to new fqn or other names added to this concept. Subscriber is responsible for + // the final create concept transaction. +// createConceptEventSubscriber = evt -> { +// DescrName descrName = evt.getModel(); +// +// if (getConceptViewModel() == null || descrName == null) { +// LOG.warn("ViewModel should not be null. Event type:" + evt.getEventType()); +// return; +// } +// +// if (CREATE.equals(conceptViewModel.getPropertyValue(MODE))) { +// if (evt.getEventType() == CreateConceptEvent.ADD_FQN) { +// fullyQualifiedNames.clear(); +// fullyQualifiedNames.add(descrName); +// } else if (evt.getEventType() == CreateConceptEvent.ADD_OTHER_NAME) { +// otherNames.add(descrName); +// }else if (evt.getEventType() == CreateConceptEvent.EDIT_OTHER_NAME) { // Since we are +// updateOtherNamesDescription(otherNames); +// }else { // Since we are +// updateFullyQualifiedNamesDescription(fullyQualifiedNames); +// } +// // Attempts to write data +// boolean isWritten = conceptViewModel.createConcept(propertiesController.getStampFormViewModel()); +// // when written the mode changes to EDIT. +// LOG.info("Is " + conceptViewModel + " created? " + isWritten); +// if (isWritten) { +// updateView(); +// } +// // remove 'Add Fully Qualified Name' from the menu +// setUpDescriptionContextMenu(addDescriptionButton); +// //TODO revisit: why should the mode ever be edit inside a create event? +// } else if (EDIT.equals(conceptViewModel.getPropertyValue(MODE))){ +// conceptViewModel.addOtherName(conceptViewModel.getViewProperties().calculator().viewCoordinateRecord().editCoordinate(), descrName); +// otherNames.add(descrName); +// } +// +// }; +// eventBus.subscribe(conceptTopic, CreateConceptEvent.class, createConceptEventSubscriber); // set up the event handler for editing a concept - editConceptEventSubscriber = evt -> { - DescrName descrName = evt.getModel(); - - if (getConceptViewModel() == null || descrName == null) { - LOG.warn("ViewModel should not be null. Event type:" + evt.getEventType()); - return; - } - if (EDIT.equals(conceptViewModel.getPropertyValue(MODE))) { - if (evt.getEventType() == EditConceptEvent.EDIT_FQN) { - // the listener will fire on the FQN when we update this - fullyQualifiedNames.add(descrName); - } - } - }; - eventBus.subscribe(conceptTopic, EditConceptEvent.class, editConceptEventSubscriber); +// editConceptEventSubscriber = evt -> { +// DescrName descrName = evt.getModel(); +// +// if (getConceptViewModel() == null || descrName == null) { +// LOG.warn("ViewModel should not be null. Event type:" + evt.getEventType()); +// return; +// } +// if (EDIT.equals(conceptViewModel.getPropertyValue(MODE))) { +// if (evt.getEventType() == EditConceptEvent.EDIT_FQN) { +// // the listener will fire on the FQN when we update this +// fullyQualifiedNames.add(descrName); +// } +// } +// }; +// eventBus.subscribe(conceptTopic, EditConceptEvent.class, editConceptEventSubscriber); // listen to rules changes to update the axioms @@ -475,6 +501,7 @@ public void initialize() { conceptContentScrollPane.pseudoClassStateChanged(V_SCROLLBAR_NEEDED, isVerticalScrollbarVisible(conceptContentScrollPane))); // TODO: When event bus is more universally used the database can emit events. For now we listen for a refresh calculator events + // TODO: this can be moved into the Node Later and we can have there a clean ExternalUpdate <--> ConceptViewModel update // Refresh Concept window refreshCalculatorEventSubscriber = _ -> { LOG.info("Refresh concept window details"); @@ -504,35 +531,220 @@ public void initialize() { updateView(); } }; - EvtBusFactory.getDefaultEvtBus().subscribe(conceptViewModel.getPropertyValue(CURRENT_JOURNAL_WINDOW_TOPIC), + EvtBusFactory.getDefaultEvtBus().subscribe(conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_UNIQUE_CONCEPT_TOPIC), GenEditingEvent.class, refreshSubscriber); - conceptViewModel.getViewProperties().nodeView().addListener((obs, oldViewCoord, newViewCoord) -> { - if (newViewCoord != null) { - LOG.info("refresh concept window when view coordinate has changed." + newViewCoord); - updateView(); + + + + // TODO: this is wrong it should be -> when we have a entityFacade we cannot edit anymore ever right? + SimpleObjectProperty thisFacade = conceptViewModelNext.getProperty(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); + BooleanBinding nonEditableAxiom = thisFacade.isNotNull(); + addAxiomButton.visibleProperty().bind(nonEditableAxiom); + + // this through bindings is handeld in the properties controller itself, simmilar on how overall window state is handeld +// hasValidStampProp.subscribe((isValidStamp) -> { +// if( isValidStamp) { // we can now bind the values directly +// // TODO instead of this mumble jumble between Concept and Propertie Controller do the right thing +// //propertiesController +// } else { // we can only show "default stamp" values +// +// } +// }); + + // in create mode we do not have a stamp by default, thats why the first thing we need to archive + // is having a valid stamp. Hold anyting else until that. + + + + +// conceptViewModel.getProperty(MODE).subscribe(() -> { +// propertiesController.setEditMode(conceptViewModel.getPropertyValue(MODE).equals(EDIT)); +// // setEditMode true if current conceptViewModel Mode is Edit otherwise False +// +// if (conceptViewModel.getPropertyValue(MODE).equals(CREATE)) { +// StampFormViewModelBase stampFormViewModel = propertiesController.getStampFormViewModel(); +// // if conceptViewModel say CREATE than we sub on stampFormViewModel on IS_CONFIRMED_OR_SUBMITTED +// stampFormViewModel.getProperty(IS_CONFIRMED_OR_SUBMITTED).subscribe(this::onConfirmStampFormWhenCreating); +// } else { +// // add axiom pencil is only for create mode +// // In view mode you can't add a sufficient/necc set +// addAxiomButton.setVisible(false); +// } +// }); + } + + // setup ConceptPropertiesController only for now + // later can also do timeline etc + private void setupNodes() { + + Config propertiesConfig = new Config(ConceptPropertiesController.class.getResource( + ConceptPropertiesController.CONCEPT_PROPERTIES_FXML_FILE + )).addNamedViewModel(new NamedVm("conceptViewModelNext", conceptViewModelNext)); + this.propertiesJFXNode= FXMLMvvmLoader.make(propertiesConfig); + + // Load Timeline View Panel (FXML & Controller) + FXMLLoader timelineFXMLLoader = new FXMLLoader(TimelineController.class.getResource(CONCEPT_TIMELINE_VIEW_FXML_FILE)); + try { + this.timelinePane = timelineFXMLLoader.load(); + this.timelineController = timelineFXMLLoader.getController(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void setupTimelineBindings() { + // This will highlight with green around the pane when the user selects a date point in the timeline. + this.timelineController.onDatePointSelected((changeCoordinate) -> { + propertiesJFXNode.controller().getHistoryChangeController().highlightListItemByChangeCoordinate(changeCoordinate); + }); + // When Date points are in range (range slider) + this.timelineController.onDatePointInRange((rangeToggleOn, changeCoordinates) -> { + if (rangeToggleOn) { + propertiesJFXNode.controller().getHistoryChangeController().filterByRange(changeCoordinates); + propertiesJFXNode.controller().getHierarchyController().diffNavigationGraph(changeCoordinates); + } else { + propertiesJFXNode.controller().getHistoryChangeController().unfilterByRange(); + propertiesJFXNode.controller().getHierarchyController().diffNavigationGraph(Set.of()); + } + }); + + } + + private void setupPropertyBindings() {} + + private void setupDetailsBanner() { + setupIdenticon(); + setupStampBindings(); + } + + private void setupIdenticon() { + identiconImageView.setOnContextMenuRequested(contextMenuEvent -> { + // query all available memberships (semantics having the purpose as 'membership', and no fields) + // query current concept's membership semantic records. + // build menuItems according to 'add' or 'remove' , style to look like figma designs style classes. + // show offset to the right of the identicon + ViewCalculator viewCalculator = conceptViewModelNext.getViewProperties().calculator(); + EntityFacade currentConceptFacade = conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); + List patterns = getMembershipPatterns(); + ContextMenu membershipContextMenu = new ContextMenu(); + membershipContextMenu.getStyleClass().add("kview-context-menu"); + + Comparator patternMenuComparator = (m1, m2) -> m1.getText().compareToIgnoreCase(m2.getText()); + List addedMenuItems = new ArrayList<>(); + List removedMenuItems = new ArrayList<>(); + for (PatternEntityVersion pattern : patterns) { + MenuItem menuItem = new MenuItem(); + if (isInMembershipPattern(currentConceptFacade.nid(), pattern.nid(), viewCalculator)) { + menuItem.setText("Remove from " + pattern.entity().description()); + menuItem.setOnAction(evt -> removeFromMembershipPattern(currentConceptFacade.nid(), pattern.entity(), viewCalculator)); + addedMenuItems.add(menuItem); + } else { + menuItem.setText("Add to " + pattern.entity().description()); + menuItem.setOnAction(evt -> addToMembershipPattern(currentConceptFacade, pattern.entity(), viewCalculator)); + removedMenuItems.add(menuItem); + } + } + if (!addedMenuItems.isEmpty()) { + // sort the added (able to be removed) + addedMenuItems.sort(patternMenuComparator); + membershipContextMenu.getItems().addAll(addedMenuItems); + // then add a menu line separator + if (!removedMenuItems.isEmpty()) { + membershipContextMenu.getItems().add(new SeparatorMenuItem()); + } + } + // then add the sorted removed (that can be added) + removedMenuItems.sort(patternMenuComparator); + membershipContextMenu.getItems().addAll(removedMenuItems); + + membershipContextMenu.show(identiconImageView, contextMenuEvent.getScreenX(), + contextMenuEvent.getSceneY() + identiconImageView.getFitHeight()); + }); + } + + private void setupStampBindings() { + // posibility stamp view state + // newConcept(no facade) & noValidStamp + // -> DISPLAY: BLANK | DO : waiting for stampForm to return as a valid one + // newConcept(no facade) & ValidStamp via Form + // -> DISPLAY: formValues, "uncommited" | DO : waiting for concept to be created and commited on axim creation + // on facade update ( e.g non null facade) + // -> DISPLAY: values derived from facade | DO: wait for either new facade update or update from form? + + stampViewControl.selectedProperty().subscribe(this::onStampSelectionChanged); // This needed ? + + SimpleBooleanProperty isNewConcept = conceptViewModelNext.getProperty(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + SimpleBooleanProperty hasValidStamp = conceptViewModelNext.getProperty(ConceptPropertyKeys.HAS_VALID_STAMP); + SimpleObjectProperty thisEntityProp = conceptViewModelNext.getProperty(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); + + BooleanBinding shouldUpdateStampInfo = isNewConcept.and(hasValidStamp); + + + hasValidStamp.subscribe((isStamp) -> { + if (isStamp) { + LOG.info("has valid stamp triggerd: " + isStamp); + + onConfirmStampFormWhenCreating(); } }); - conceptViewModel.getProperty(MODE).subscribe(() -> { - propertiesController.setEditMode(conceptViewModel.getPropertyValue(MODE).equals(EDIT)); +// shouldUpdateStampInfo.subscribe(isTimeToUpdate -> { +// LOG.info("In create mode: Created new STAMP correctly -> update stamp info"); +// if(isTimeToUpdate) { +// onConfirmStampFormWhenCreating(); +// } +// }); + + if (shouldUpdateStampInfo.getValue().equals(true)) { // if this all true directly at startup then once fire it manualy + onConfirmStampFormWhenCreating(); + } + + thisEntityProp.subscribe(conceptEntity -> { + + }); + } + + private void setupDetailsNameSemantics() { - if (conceptViewModel.getPropertyValue(MODE).equals(CREATE)) { - StampFormViewModelBase stampFormViewModel = propertiesController.getStampFormViewModel(); - stampFormViewModel.getProperty(IS_CONFIRMED_OR_SUBMITTED).subscribe(this::onConfirmStampFormWhenCreating); + SimpleBooleanProperty hasValidStampProp = conceptViewModelNext.getProperty(ConceptPropertyKeys.HAS_VALID_STAMP); + + // TODO: make sure that we can only change otherName or other FQN but not the primary fqn in non create Mode + addDescriptionButton.disableProperty().bind(hasValidStampProp.not()); + + // Listener when user enters a new fqn + ObservableList fqnFacades = conceptViewModelNext.getObservableList(ConceptPropertyKeys.ASOCIATED_FQN_DESCRIPTION_SEMANTICS); + fqnFacades.subscribe( () -> { + //SimpleBooleanProperty hasValidStampProp = conceptViewModelNext.getProperty(ConceptPropertyKeys.HAS_VALID_STAMP); + if (hasValidStampProp.getValue()) { + // TODO: make sure updateConceptBanner equivalent is there + updateFullyQualifiedNamesDescription(fqnFacades); } else { - // add axiom pencil is only for create mode - // In view mode you can't add a sufficient/necc set - addAxiomButton.setVisible(false); + LOG.error("Stamp not valid -> Cannot update FQN description"); } + }); + + ObservableList otherNameFacades = conceptViewModelNext.getObservableList(ConceptViewModelNext.ConceptPropertyKeys.ASOCIATED_OTHER_NAME_DESCRIPTION_SEMANTICS); + otherNameFacades.subscribe( () -> { + //SimpleBooleanProperty hasValidStampProp = conceptViewModelNext.getProperty(ConceptPropertyKeys.HAS_VALID_STAMP); + + if (hasValidStampProp.getValue()) { + updateOtherNamesDescription(otherNameFacades); + } else { + LOG.error("Stamp not valid -> Cannot update otherName description"); + } + }); + } + private void onConfirmStampFormWhenCreating() { // Update StampViewControl - StampFormViewModelBase stampFormViewModel = propertiesController.getStampFormViewModel(); - ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); + StampFormViewModelBase stampFormViewModel = this.propertiesJFXNode.controller().getStampFormViewModel(); + ViewCalculator viewCalculator = conceptViewModelNext.getViewProperties().calculator(); + LOG.info(stampFormViewModel.toString()); // - Status State status = stampFormViewModel.getPropertyValue(STATUS); String statusText = viewCalculator.getPreferredDescriptionTextWithFallbackOrNid(status.nid()); @@ -617,8 +829,8 @@ private boolean shouldConsumeVerticalScroll(ScrollPane scrollPane, ScrollEvent e return (atTop && deltaY > 0) || (atBottom && deltaY < 0); } - public ValidationViewModel getConceptViewModel() { - return conceptViewModel; + public ConceptViewModelNext getConceptViewModel() { + return conceptViewModelNext; } private void setUpDescriptionContextMenu(Button addDescriptionButton) { @@ -628,8 +840,10 @@ private void setUpDescriptionContextMenu(Button addDescriptionButton) { } private void onAddDescriptionButtonPressed(ActionEvent actionEvent) { - if (this.conceptViewModel.getPropertyValue(MODE).equals(CREATE) && - getConceptViewModel().getObservableList(FULLY_QUALIFIED_NAMES).isEmpty()) { + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + boolean hasNoFQNSemantics = this.conceptViewModelNext.getObservableList(ConceptPropertyKeys.ASOCIATED_FQN_DESCRIPTION_SEMANTICS).isEmpty(); + if ( isNewConcept && + hasNoFQNSemantics) { // Show the context menu with 'Add Fully Qualified' option when it is a new concept in create mode and // there is no fully qualified name. fireContextMenuEvent(actionEvent, Side.RIGHT, 2, 0); @@ -641,10 +855,12 @@ private void onAddDescriptionButtonPressed(ActionEvent actionEvent) { private void showAddAnotherNameUI() { ConceptEntity currentConcept = null; - if (getConceptViewModel().getPropertyValue(CURRENT_ENTITY) instanceof EntityProxy.Concept concept) { + if (this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE) instanceof EntityProxy.Concept concept) { + LOG.info("current ConceptEntitiy via EntityProxy Service !!!"); currentConcept = (ConceptEntity) EntityService.get().getEntity(concept.nid()).get(); } else { - currentConcept = getConceptViewModel().getPropertyValue(CURRENT_ENTITY); + LOG.info("current ConceptEntitiy read from our state !!!"); // TODO: maybe this is not correct? + currentConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); } if (currentConcept != null) { // in edit mode, will have a concept and public id @@ -676,19 +892,21 @@ private ContextMenu buildMenuOptionContextMenu() { // if there is a fully qualified name, then do not give the option Add Fully Qualified Object[][] menuItems; + + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + boolean hasNoFQNSet = this.conceptViewModelNext.getObservableList(ConceptPropertyKeys.ASOCIATED_FQN_DESCRIPTION_SEMANTICS).isEmpty(); // show the 'Add Fully Qualified' option when it is a new concept in create mode and there is no fully qualified name - if (this.conceptViewModel.getPropertyValue(MODE).equals(CREATE) && - getConceptViewModel().getObservableList(FULLY_QUALIFIED_NAMES).isEmpty()) { + if (isNewConcept && + hasNoFQNSet) { menuItems = new Object[][]{ {"ADD DESCRIPTION", true, new String[]{"menu-header-left-align"}, null, null}, {MenuHelper.SEPARATOR}, - {"Add Fully Qualified Name", true, null, (EventHandler) actionEvent -> - eventBus.publish(conceptTopic, new AddFullyQualifiedNameEvent(contextMenu, - AddFullyQualifiedNameEvent.ADD_FQN, conceptViewModel.getViewProperties())), + {"Add Fully Qualified Name", true, null, (EventHandler) actionEvent -> { + conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); + } , createConceptEditDescrIcon()}, {"Add Other Name", true, null, (EventHandler) actionEvent -> { - eventBus.publish(conceptTopic, new AddOtherNameToConceptEvent(contextMenu, - AddOtherNameToConceptEvent.ADD_DESCRIPTION)); + conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); }, createConceptEditDescrIcon()}, {MenuHelper.SEPARATOR}, @@ -711,9 +929,9 @@ private ContextMenu buildMenuOptionContextMenu() { MenuItem menuItem = menuHelper.createMenuOption( String.valueOf(menuItemObj[NAME]), /* name */ Boolean.parseBoolean(String.valueOf(menuItemObj[ENABLED])), /* enabled */ - (String[]) menuItemObj[STYLES], /* styling */ + (String[]) menuItemObj[STYLES], /* styling */ menuItemAction, /* action when selected */ - (Node) menuItemObj[GRAPHIC] /* optional graphic */ + (Node) menuItemObj[GRAPHIC] /* optional graphic */ ); contextMenu.getItems().add(menuItem); } @@ -737,41 +955,33 @@ private void popupAddAxiomContextMenu(ActionEvent actionEvent) { @FXML private void addNecessarySet(ActionEvent actionEvent) { - conceptViewModel.setPropertyValue(AXIOM, ConceptViewModel.NECESSARY_SET); + conceptViewModelNext.setPropertyValue(AXIOM, ConceptViewModelNext.NECESSARY_SET); + createConcept(); - // Attempts to write data - if (CREATE.equals(conceptViewModel.getPropertyValue(MODE))) { - boolean isWritten = conceptViewModel.createConcept(propertiesController.getStampFormViewModel()); - LOG.info("Is " + conceptViewModel + " created? " + isWritten); - if (isWritten) { - updateView(); - } - } } @FXML private void addSufficientSet(ActionEvent actionEvent) { - conceptViewModel.setPropertyValue(AXIOM, ConceptViewModel.SUFFICIENT_SET); + conceptViewModelNext.setPropertyValue(AXIOM, ConceptViewModelNext.SUFFICIENT_SET); + createConcept(); + + } + private void createConcept() { + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); // Attempts to write data - if (CREATE.equals(conceptViewModel.getPropertyValue(MODE))) { - boolean isWritten = conceptViewModel.createConcept(propertiesController.getStampFormViewModel()); - LOG.info("Is " + conceptViewModel + " created? " + isWritten); + if (isNewConcept) { // TODO: is this the correct | e.g do we make sure that this does not happen in wrong state + boolean isWritten = conceptViewModelNext.createConcept(this.propertiesJFXNode.controller().getStampFormViewModel()); + LOG.info("Is " + conceptViewModelNext + " created? " + isWritten); if (isWritten) { - updateView(); + LOG.info( "yell at our overlords to reset ourself"); + //TODO: reset ourselfs + //updateView(); } } } - public void attachPropertiesViewSlideoutTray(Pane propertiesViewBorderPane, - PropertiesController propertiesController) { - this.propertiesController = propertiesController; - addPaneToTray(propertiesViewBorderPane, propertiesSlideoutTrayPane); - } - public void attachTimelineViewSlideoutTray(Pane timelineViewBorderPane) { - addPaneToTray(timelineViewBorderPane, timelineSlideoutTrayPane); - } private void addPaneToTray(Pane contentViewPane, Pane slideoutTrayPane) { double width = contentViewPane.getWidth(); contentViewPane.setLayoutX(width); @@ -795,7 +1005,7 @@ void closeConceptWindow(ActionEvent event) { // Clean up the draggable nodes removeDraggableNodes(detailsOuterBorderPane, conceptHeaderControlToolBarHbox, - propertiesController != null ? propertiesController.getPropertiesTabsPane() : null); + this.propertiesJFXNode.controller() != null ? this.propertiesJFXNode.controller().getPropertiesTabsPane() : null); if (this.onCloseConceptWindow != null) { onCloseConceptWindow.accept(this); @@ -817,12 +1027,13 @@ public Pane getPropertiesSlideoutTrayPane() { } public void updateView() { - EntityFacade entityFacade = conceptViewModel.getPropertyValue(CURRENT_ENTITY); - if (entityFacade != null) { // edit concept - getConceptViewModel().setPropertyValue(MODE, EDIT); - } else { // create concept - getConceptViewModel().setPropertyValue(MODE, CREATE); - } + // TODO: this should have happend on whatever called updateView +// EntityFacade entityFacade = conceptViewModel.getPropertyValue(CURRENT_ENTITY); +// if (entityFacade != null) { // edit concept +// getConceptViewModel().setPropertyValue(MODE, EDIT); +// } else { // create concept +// getConceptViewModel().setPropertyValue(MODE, CREATE); +// } // Display info for top banner area updateConceptBanner(); @@ -847,18 +1058,22 @@ public void onReasonerSlideoutTray(Consumer reasonerResultsControl */ public void updateConceptBanner() { // do not update ui should be blank - if (getConceptViewModel().getPropertyValue(MODE) == CREATE) { + + // in create mode we update the ui through a callback from thew viewmodel ( e.g getting the uncommited stamp) + // and not through this updateConceptBanner + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + if (isNewConcept) { return; } - EntityFacade entityFacade = conceptViewModel.getPropertyValue(CURRENT_ENTITY); - final ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); + EntityFacade entityFacade = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); + + final ViewCalculator viewCalculator = this.conceptViewModelNext.getViewProperties().calculator(); // check to see if the latest version exists viewCalculator.latest(entityFacade).ifPresentOrElse( entityVersion -> { - // Title (FQN of concept) String conceptNameStr = viewCalculator.languageCalculator().getDescriptionTextOrNid(entityFacade.nid()); fqnTitleText.setText(conceptNameStr); @@ -903,7 +1118,7 @@ public void updateConceptBanner() { }, // else no value present () -> { - getConceptViewModel().setPropertyValue(MODE, VIEW); + //getConceptViewModel().setPropertyValue(MODE, VIEW); stampViewControl.setStatus(NO_VERSION_FOR_VIEW_TEXT); stampViewControl.setModule(NO_VERSION_FOR_VIEW_TEXT); stampViewControl.setAuthor(NO_VERSION_FOR_VIEW_TEXT); @@ -920,26 +1135,30 @@ private void updateDisplayIdentifier(ViewCalculator viewCalculator, ConceptFacad public void updateFullyQualifiedNamesDescription(List descrNameViewModels) { fullyQualifiedNameNodeListControl.getItems().clear(); - descrNameViewModels.forEach(fullyQualifedName -> { + descrNameViewModels.forEach(nameModel -> { // start adding a row - VBox fullyQualifiedNameVBox = generateDescriptionSemanticRow(fullyQualifedName); + VBox fullyQualifiedNameVBox = generateDescriptionSemanticRow(nameModel); TextFlow firstRow = (TextFlow) fullyQualifiedNameVBox.getChildren().getFirst(); - firstRow.setOnMouseClicked(event -> eventBus.publish(conceptTopic, - new EditConceptFullyQualifiedNameEvent(fullyQualifiedNameVBox, - EditConceptFullyQualifiedNameEvent.EDIT_FQN, fullyQualifedName))); + firstRow.setOnMouseClicked(event -> { + LOG.info("clicked to update fqn"); + conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_DESCRIPTION_SEMANTIC, new UncommittedSemanticNameDescr(nameModel)); + conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); + }); fullyQualifiedNameNodeListControl.getItems().add(fullyQualifiedNameVBox); }); } public void updateOtherNamesDescription(List descrNameViewModels) { otherNamesNodeListControl.getItems().clear(); - descrNameViewModels.forEach(otherName -> { + descrNameViewModels.forEach(nameModel -> { // start adding a row - VBox otherNameBox = generateDescriptionSemanticRow(otherName); + VBox otherNameBox = generateDescriptionSemanticRow(nameModel); TextFlow firstRow = (TextFlow) otherNameBox.getChildren().getFirst(); - firstRow.setOnMouseClicked(event -> eventBus.publish(conceptTopic, - new EditOtherNameConceptEvent(otherNameBox, - EditOtherNameConceptEvent.EDIT_OTHER_NAME, otherName))); + firstRow.setOnMouseClicked(event -> { + LOG.info("clicked to update otherName"); + conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_DESCRIPTION_SEMANTIC, new UncommittedSemanticNameDescr(nameModel)); + conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); + }); otherNamesNodeListControl.getItems().add(otherNameBox); }); } @@ -949,13 +1168,17 @@ public void updateOtherNamesDescription(List descrNameViewModels) { */ public void updateConceptDescription() { // do not update ui should be blank - if (getConceptViewModel().getPropertyValue(MODE) == CREATE) { + + //TODO: while we are updating the stamp via callbacks in new Concepts we do it differently for desciptions.. + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + if (isNewConcept) { return; } - final ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); - EntityFacade entityFacade = conceptViewModel.getPropertyValue(CURRENT_ENTITY); + final ViewCalculator viewCalculator = this.conceptViewModelNext.getViewProperties().calculator(); + + EntityFacade entityFacade = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); viewCalculator.latest(entityFacade).ifPresentOrElse( _ -> { @@ -969,6 +1192,7 @@ public void updateConceptDescription() { int descriptionTypeIndex = patternEntityVersion.indexForMeaning(DESCRIPTION_TYPE.nid()); descriptionSemanticsMap.forEach((semanticEntityVersion, fieldDescriptions) -> { + EntityFacade fieldTypeValue = (EntityFacade) semanticEntityVersion.fieldValues().get(descriptionTypeIndex); boolean isFQN = FULLY_QUALIFIED_NAME_DESCRIPTION_TYPE.nid() == fieldTypeValue.nid(); boolean isOtherName = REGULAR_NAME_DESCRIPTION_TYPE.nid() == fieldTypeValue.nid(); @@ -979,9 +1203,15 @@ public void updateConceptDescription() { VBox fullyQualifiedNameBox = generateDescriptionSemanticRow(semanticEntityVersion, fieldDescriptions); PublicId fullyQuallifiedNamePublicId = (PublicId) fullyQualifiedNameBox.getChildren().getFirst().getUserData(); TextFlow row = (TextFlow) fullyQualifiedNameBox.getChildren().getFirst(); - row.setOnMouseClicked(event -> eventBus.publish(conceptTopic, - new EditConceptFullyQualifiedNameEvent(fullyQualifiedNameBox, - EditConceptFullyQualifiedNameEvent.EDIT_FQN, fullyQuallifiedNamePublicId))); + row.setOnMouseClicked(event -> { + LOG.info("clicked edit FQN Name = " + semanticEntityVersion + " " + fieldDescriptions); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_DESCRIPTION_SEMANTIC, new SemanticPublicId(fullyQuallifiedNamePublicId)); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); +// // TODO: remove associated event +// eventBus.publish(conceptTopic, +// new EditConceptFullyQualifiedNameEvent(fullyQualifiedNameBox, +// EditConceptFullyQualifiedNameEvent.EDIT_FQN, fullyQuallifiedNamePublicId)); + }); fullyQualifiedNameNodeListControl.getItems().add(fullyQualifiedNameBox); LOG.debug("FQN Name = " + semanticEntityVersion + " " + fieldDescriptions); } else if (isOtherName) { @@ -989,9 +1219,15 @@ public void updateConceptDescription() { VBox otherNameBox = generateDescriptionSemanticRow(semanticEntityVersion, fieldDescriptions); PublicId otherNamePublicId = (PublicId) otherNameBox.getChildren().getFirst().getUserData(); TextFlow firstRow = (TextFlow) otherNameBox.getChildren().getFirst(); - firstRow.setOnMouseClicked(event -> eventBus.publish(conceptTopic, - new EditOtherNameConceptEvent(otherNameBox, - EditOtherNameConceptEvent.EDIT_OTHER_NAME, otherNamePublicId))); + firstRow.setOnMouseClicked(event -> { + LOG.info("edit Other Names = " + semanticEntityVersion + " " + fieldDescriptions); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_DESCRIPTION_SEMANTIC, new SemanticPublicId(otherNamePublicId)); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); +// // TODO: remove associated event +// eventBus.publish(conceptTopic, +// new EditOtherNameConceptEvent(otherNameBox, +// EditOtherNameConceptEvent.EDIT_OTHER_NAME, otherNamePublicId)); + }); otherNamesNodeListControl.getItems().add(otherNameBox); LOG.debug("Other Names = " + semanticEntityVersion + " " + fieldDescriptions); @@ -1008,14 +1244,15 @@ public void updateConceptDescription() { }, // else no value present () -> { - getConceptViewModel().setPropertyValue(MODE, VIEW); - List fqns = getConceptViewModel().getValue(FULLY_QUALIFIED_NAMES); + LOG.error(" try to populate description semantics while not having a valid concept entity and not being in create mode"); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT, false); + List fqns = this.conceptViewModelNext.getList(ConceptPropertyKeys.ASOCIATED_FQN_DESCRIPTION_SEMANTICS); if (fqns != null && !fqns.isEmpty()) { VBox fqnVBox = new VBox(new Label(NO_VERSION_FOR_VIEW_TEXT)); fullyQualifiedNameNodeListControl.getItems().clear(); fullyQualifiedNameNodeListControl.getItems().add(fqnVBox); } - List ots = getConceptViewModel().getValue(OTHER_NAMES); + List ots = this.conceptViewModelNext.getList(ConceptPropertyKeys.ASOCIATED_OTHER_NAME_DESCRIPTION_SEMANTICS); if (ots != null && !ots.isEmpty()) { VBox otherNameVBox = new VBox(new Label(NO_VERSION_FOR_VIEW_TEXT)); otherNamesNodeListControl.getItems().clear(); @@ -1033,118 +1270,83 @@ public void updateConceptDescription() { * @return */ private VBox generateDescriptionSemanticRow(SemanticEntityVersion semanticEntityVersion, List fieldDescriptions) { - VBox textFlowsBox = new VBox(); + ViewCalculator viewCalculator = this.conceptViewModelNext.getViewProperties().calculator(); //* + + boolean hasFieldDescription = !fieldDescriptions.isEmpty(); + String nameDescText = getFieldValueByMeaning(semanticEntityVersion, TinkarTerm.TEXT_FOR_DESCRIPTION); + + PublicId semanticPubId = semanticEntityVersion.publicId(); //* + boolean isACommitedSemantic = semanticPubId != null; String descrSemanticStr = String.join(", ", fieldDescriptions); + //---- - // create textflow to hold regular name label - TextFlow row1 = new TextFlow(); - String otherNameDescText = getFieldValueByMeaning(semanticEntityVersion, TinkarTerm.TEXT_FOR_DESCRIPTION); - Text otherNameLabel = new Text(otherNameDescText); - otherNameLabel.getStyleClass().add("descr-concept-name"); + return updateThing(viewCalculator, semanticPubId, descrSemanticStr, nameDescText); + } - Text semanticDescrText = new Text(); - if (!fieldDescriptions.isEmpty()) { - semanticDescrText.setText(" (%s)".formatted(descrSemanticStr)); - semanticDescrText.getStyleClass().add("descr-concept-name"); + // This method is usefull when we are adding new semantics descr and want to update + // the shown display while the parent concept itself is a) in create mode && not commited + // otherwise we can always get the "latest" via DB + private VBox generateDescriptionSemanticRow(DescrName nameModel) { + ViewCalculator viewCalculator = this.conceptViewModelNext.getViewProperties().calculator(); //* + ConceptEntity caseSigConcept = nameModel.getCaseSignificance(); //* + String casSigText = viewCalculator.languageCalculator().getDescriptionTextOrNid(caseSigConcept.nid()); //* + ConceptEntity langConcept = nameModel.getLanguage(); //* + String langText = viewCalculator.languageCalculator().getDescriptionTextOrNid(langConcept.nid()); //* + boolean hasFieldDescription = !casSigText.isEmpty() && !langText.isEmpty(); + // TODO: need to retrieve the description Semantic's Text field (latest version text). + PublicId semanticPubId = nameModel.getSemanticPublicId(); //* + boolean isACommitedSemantic = semanticPubId != null; + String nameDescText; + // the semanticPublicId is null in CREATE mode, so use the nameText that was entered + // instead of the semanticPublicId field value + if (isACommitedSemantic) { + int nid = EntityService.get().nidForPublicId(semanticPubId); + Latest regularDescriptionTextversion = viewCalculator.latest(nid); + nameDescText = regularDescriptionTextversion.get().fieldValues().get(1).toString(); } else { - semanticDescrText.setText(""); + nameDescText = nameModel.getNameText(); } - // add the other name label and description semantic label - row1.getStyleClass().add("descr-semantic-container"); - // store the public id of this semantic entity version - // so that when clicked the event bus can pass it to the form - // and the form can populate the data from the publicId - row1.setUserData(semanticEntityVersion.publicId()); - row1.getChildren().addAll(otherNameLabel, semanticDescrText); - - TextFlow row2 = new TextFlow(); - Text dateAddedLabel = new Text("Date Added: "); - dateAddedLabel.getStyleClass().add("grey8-12pt-bold"); - - if (semanticEntityVersion.publicId() != null) { - ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); - Latest semanticVersionLatest = viewCalculator.latest(Entity.nid(semanticEntityVersion.publicId())); - semanticVersionLatest.ifPresent(entityVersion -> { - long rawTime = entityVersion.time(); - String dateText = null; - if (rawTime == PREMUNDANE_TIME) { - dateText = PREMUNDANE; - } else { - Locale userLocale = Locale.getDefault(); - LocalDate localDate = Instant.ofEpochMilli(rawTime).atZone(ZoneId.systemDefault()).toLocalDate(); - DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(userLocale); - dateText = formatter.format(localDate); - } + LOG.info("NameLabel : "+ nameDescText); + String descrSemanticStr = "%s, %s".formatted(casSigText, langText); //*- - Text dateLabel = new Text(dateText); - dateLabel.getStyleClass().add("grey8-12pt-bold"); + //----- - Region spacer = new Region(); - spacer.setMinWidth(10); + return updateThing(viewCalculator, semanticPubId, descrSemanticStr, nameDescText); - Hyperlink attachmentHyperlink = createActionLink(IconsHelper.createIcon(ATTACHMENT)); - Hyperlink commentsHyperlink = createActionLink(IconsHelper.createIcon(COMMENTS)); - - // Add the date info and additional hyperlinks - row2.getChildren().addAll(dateAddedLabel, dateLabel, spacer, attachmentHyperlink, commentsHyperlink); - }); - } - - textFlowsBox.getChildren().addAll(row1, row2); - return textFlowsBox; } - private VBox generateDescriptionSemanticRow(DescrName otherName) { - VBox textFlowsBox = new VBox(); - ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); - ConceptEntity caseSigConcept = otherName.getCaseSignificance(); - String casSigText = viewCalculator.languageCalculator().getDescriptionTextOrNid(caseSigConcept.nid()); - ConceptEntity langConcept = otherName.getLanguage(); - - String langText = viewCalculator.languageCalculator().getDescriptionTextOrNid(langConcept.nid()); + private VBox updateThing(ViewCalculator viewCalculator, PublicId semanticPubId,String descrSemanticStr, String nameDescText) { + boolean isACommitedSemantic = semanticPubId != null; - String descrSemanticStr = "%s, %s".formatted(casSigText, langText); + VBox textFlowsBox = new VBox(); // create textflow to hold regular name label TextFlow row1 = new TextFlow(); - // TODO: need to retrieve the description Semantic's Text field (latest version text). - - PublicId semanticPid = otherName.getSemanticPublicId(); - Text otherNameLabel; + Text nameLabel = new Text(nameDescText); + nameLabel.getStyleClass().add("descr-concept-name"); - // the semanticPublicId is null in CREATE mode, so use the nameText that was entered - // instead of the semanticPublicId field value - if (semanticPid != null) { - int nid = EntityService.get().nidForPublicId(semanticPid); - Latest regularDescriptionTextversion = viewCalculator.latest(nid); - otherNameLabel = new Text(regularDescriptionTextversion.get().fieldValues().get(1).toString()); + Text semanticDescrText = new Text(); + if (!descrSemanticStr.isEmpty() || !descrSemanticStr.isBlank()) { + semanticDescrText.setText(" (%s)".formatted(descrSemanticStr)); + semanticDescrText.getStyleClass().add("descr-concept-name"); } else { - otherNameLabel = new Text(otherName.getNameText()); + semanticDescrText.setText(""); } - LOG.info("otherNameLabel : "+otherNameLabel); - - otherNameLabel.getStyleClass().add("descr-concept-name"); - - Text semanticDescrText = new Text(); - semanticDescrText.setText(" (%s)".formatted(descrSemanticStr)); - semanticDescrText.getStyleClass().add("descr-concept-name"); - // add the other name label and description semantic label row1.getStyleClass().add("descr-semantic-container"); // store the public id of this semantic entity version // so that when clicked the event bus can pass it to the form // and the form can populate the data from the publicId -// this.otherNamePublicId = semanticEntityVersion.publicId(); - - row1.getChildren().addAll(otherNameLabel, semanticDescrText); + row1.setUserData(semanticPubId); + row1.getChildren().addAll(nameLabel, semanticDescrText); TextFlow row2 = new TextFlow(); Text dateAddedLabel = new Text("Date Added: "); dateAddedLabel.getStyleClass().add("grey8-12pt-bold"); - if (otherName.getSemanticPublicId() != null) { - Latest semanticVersionLatest = viewCalculator.latest(Entity.nid(otherName.getSemanticPublicId())); + if (isACommitedSemantic) { + Latest semanticVersionLatest = viewCalculator.latest(Entity.nid(semanticPubId)); semanticVersionLatest.ifPresent(entityVersion -> { long rawTime = entityVersion.time(); String dateText = null; @@ -1170,6 +1372,7 @@ private VBox generateDescriptionSemanticRow(DescrName otherName) { row2.getChildren().addAll(dateAddedLabel, dateLabel, spacer, attachmentHyperlink, commentsHyperlink); }); } + textFlowsBox.getChildren().addAll(row1, row2); return textFlowsBox; } @@ -1209,7 +1412,7 @@ private Map> latestDescriptionSemantics(Enti // TODO: This should rely on the view calculator from the parent view properties. Below will always get the actual latest semantic version // ViewCalculator viewCalculator = getViewProperties().calculator(); /* after import this is not getting latest */ - ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); /* returns committed latest from db */ + ViewCalculator viewCalculator = this.conceptViewModelNext.getViewProperties().calculator(); /* returns committed latest from db */ viewCalculator.getDescriptionsForComponent(conceptFacade).stream() .filter(semanticEntity -> { // TODO FIXME - the latest() methods should be relative to the @@ -1217,7 +1420,7 @@ private Map> latestDescriptionSemantics(Enti // This will always return the latest record from the database not the // latest from the view coordinate position data time range. - Latest semanticEntityVersionLatest = conceptViewModel.getViewProperties().calculator().latest(semanticEntity.nid()); + Latest semanticEntityVersionLatest = this.conceptViewModelNext.getViewProperties().calculator().latest(semanticEntity.nid()); if (semanticEntityVersionLatest.isAbsent()) { return false; // No version found } @@ -1244,7 +1447,7 @@ private Map> latestDescriptionSemantics(Enti // This will always return the latest record from the database not the // latest from the view coordinate position data time range. - Latest semanticEntityVersionLatest = conceptViewModel.getViewProperties().calculator().latest(semanticEntity.nid()); + Latest semanticEntityVersionLatest = this.conceptViewModelNext.getViewProperties().calculator().latest(semanticEntity.nid()); if(semanticEntityVersionLatest.isAbsent()) { return; } @@ -1297,29 +1500,30 @@ private static ImmutableList fields(SemanticEntityVersion seman private void updateAxioms() { // do not update ui should be blank - if (getConceptViewModel().getPropertyValue(MODE) == CREATE) { + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + if (isNewConcept) { return; } // clear Axioms areas - ViewCalculator viewCalculator = conceptViewModel.getViewProperties().calculator(); - EntityFacade entityFacade = conceptViewModel.getPropertyValue(CURRENT_ENTITY); + ViewCalculator viewCalculator = this.conceptViewModelNext.getViewProperties().calculator(); + EntityFacade entityFacade = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); viewCalculator.latest(entityFacade).ifPresentOrElse( entityVersion -> { // Create a SheetItem (AXIOM inferred semantic version) // TODO Should this be reused instead of instanciating a new one everytime? - KometPropertySheet inferredPropertySheet = new KometPropertySheet(conceptViewModel.getViewProperties(), true); + KometPropertySheet inferredPropertySheet = new KometPropertySheet(this.conceptViewModelNext.getViewProperties(), true); Latest inferredSemanticVersion = viewCalculator.getInferredAxiomSemanticForEntity(entityFacade.nid()); - makeSheetItem(conceptViewModel.getViewProperties(), inferredPropertySheet, inferredSemanticVersion); + makeSheetItem(this.conceptViewModelNext.getViewProperties(), inferredPropertySheet, inferredSemanticVersion); inferredAxiomPane.setCenter(inferredPropertySheet); // Create a SheetItem (AXIOM stated semantic version) - KometPropertySheet statedPropertySheet = new KometPropertySheet(conceptViewModel.getViewProperties(), true); + KometPropertySheet statedPropertySheet = new KometPropertySheet(this.conceptViewModelNext.getViewProperties(), true); Latest statedSemanticVersion = viewCalculator.getStatedAxiomSemanticForEntity(entityFacade.nid()); - makeSheetItem(conceptViewModel.getViewProperties(), statedPropertySheet, statedSemanticVersion); + makeSheetItem(this.conceptViewModelNext.getViewProperties(), statedPropertySheet, statedSemanticVersion); statedAxiomPane.setCenter(statedPropertySheet); //TODO discuss the blue theme color related to AXIOMs @@ -1339,11 +1543,11 @@ private void makeSheetItem(ViewProperties viewProperties, KometPropertySheet propertySheet, Latest semanticVersion) { semanticVersion.ifPresent(semanticEntityVersion -> { - Latest statedPatternVersion = conceptViewModel.getViewProperties().calculator().latestPatternEntityVersion(semanticEntityVersion.pattern()); - ImmutableList fields = fields(semanticEntityVersion, statedPatternVersion.get(), conceptViewModel.getViewProperties().calculator()); + Latest statedPatternVersion = this.conceptViewModelNext.getViewProperties().calculator().latestPatternEntityVersion(semanticEntityVersion.pattern()); + ImmutableList fields = fields(semanticEntityVersion, statedPatternVersion.get(), this.conceptViewModelNext.getViewProperties().calculator()); fields.forEach(field -> // create a row as a label: editor. For Axioms we hide the left labels. - propertySheet.getItems().add(SheetItem.make(field, semanticEntityVersion, conceptViewModel.getViewProperties()))); + propertySheet.getItems().add(SheetItem.make(field, semanticEntityVersion, this.conceptViewModelNext.getViewProperties()))); }); } @@ -1390,10 +1594,13 @@ private void onStampSelectionChanged() { } if (stampViewControl.isSelected()) { - if (CREATE.equals(conceptViewModel.getPropertyValue(MODE))) { + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + if (isNewConcept) { eventBus.publish(conceptTopic, new StampEvent(stampViewControl, StampEvent.CREATE_STAMP)); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.STAMP); } else { eventBus.publish(conceptTopic, new StampEvent(stampViewControl, StampEvent.ADD_STAMP)); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.STAMP); } if (!propertiesToggleButton.isSelected()) { @@ -1401,6 +1608,7 @@ private void onStampSelectionChanged() { } } else { eventBus.publish(conceptTopic, new ClosePropertiesPanelEvent(stampViewControl, CLOSE_PROPERTIES)); + this.conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NONE); } } @@ -1414,11 +1622,19 @@ private void openPropertiesPanel(ActionEvent event) { updateDraggableNodesForPropertiesPanel(true); - if (CREATE.equals(conceptViewModel.getPropertyValue(MODE)) && !stampViewControl.isSelected()) { + boolean isNewConcept = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + boolean hasValidStamp = this.conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.HAS_VALID_STAMP); + + if (isNewConcept) { // show the Add FQN - eventBus.publish(conceptTopic, new AddFullyQualifiedNameEvent(propertyToggle, - AddFullyQualifiedNameEvent.ADD_FQN, conceptViewModel.getViewProperties())); - } else if (EDIT.equals(conceptViewModel.getPropertyValue(MODE))){ + if (hasValidStamp) { + eventBus.publish(conceptTopic, new AddFullyQualifiedNameEvent(propertyToggle, + AddFullyQualifiedNameEvent.ADD_FQN, this.conceptViewModelNext.getViewProperties())); + } else { + // TODO open stamForm + } + + } else { // show the button form eventBus.publish(conceptTopic, new OpenPropertiesPanelEvent(propertyToggle, OpenPropertiesPanelEvent.OPEN_PROPERTIES_PANEL, fqnPublicId, fqnTitleText.getText())); @@ -1444,12 +1660,12 @@ private void openPropertiesPanel(ActionEvent event) { * @param isOpen {@code true} to add draggable nodes, {@code false} to remove them */ private void updateDraggableNodesForPropertiesPanel(boolean isOpen) { - if (propertiesController != null && propertiesController.getPropertiesTabsPane() != null) { + if (this.propertiesJFXNode.controller() != null && this.propertiesJFXNode.controller().getPropertiesTabsPane() != null) { if (isOpen) { - addDraggableNodes(detailsOuterBorderPane, propertiesController.getPropertiesTabsPane()); + addDraggableNodes(detailsOuterBorderPane, this.propertiesJFXNode.controller().getPropertiesTabsPane()); LOG.debug("Added properties nodes as draggable"); } else { - removeDraggableNodes(detailsOuterBorderPane, propertiesController.getPropertiesTabsPane()); + removeDraggableNodes(detailsOuterBorderPane, this.propertiesJFXNode.controller().getPropertiesTabsPane()); LOG.debug("Removed properties nodes from draggable"); } } @@ -1507,7 +1723,7 @@ private void showChangeViewCoordinateMenu(ActionEvent actionEvent) { * generate the classic Komet coordinate menu */ public void setUpEditCoordinateMenu() { - this.viewMenuModel = new ViewMenuModel(conceptViewModel.getViewProperties(), coordinatesMenuButton, "DetailsController"); + this.viewMenuModel = new ViewMenuModel(this.conceptViewModelNext.getViewProperties(), coordinatesMenuButton, "DetailsController"); } private DateTimeFormatter dateFormatter(String formatString) { @@ -1519,7 +1735,7 @@ private DateTimeFormatter dateFormatter(String formatString) { private int getFieldIndexByMeaning(SemanticEntityVersion entityVersion, EntityFacade ...meaning) { PatternEntity patternEntity = entityVersion.entity().pattern(); - PatternEntityVersion patternEntityVersion = conceptViewModel.getViewProperties().calculator().latest(patternEntity).get(); + PatternEntityVersion patternEntityVersion = this.conceptViewModelNext.getViewProperties().calculator().latest(patternEntity).get(); int index = -1; if (meaning != null && meaning.length > 0){ for (int i=0; i < meaning.length; i++){ @@ -1542,7 +1758,7 @@ private T getFieldValueByMeaning(SemanticEntityVersion entityVersion, Entity private T getFieldValueByPurpose(SemanticEntityVersion entityVersion, EntityFacade ...purpose) { PatternEntity patternEntity = entityVersion.entity().pattern(); - PatternEntityVersion patternEntityVersion = conceptViewModel.getViewProperties().calculator().latest(patternEntity).get(); + PatternEntityVersion patternEntityVersion = this.conceptViewModelNext.getViewProperties().calculator().latest(patternEntity).get(); int index = -1; if (purpose != null && purpose.length > 0){ for (int i=0; i < purpose.length; i++){ diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptNode.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptNode.java index b4bdd10b20..50b2b2399c 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptNode.java +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptNode.java @@ -27,6 +27,7 @@ import dev.ikm.komet.framework.view.ViewProperties; import dev.ikm.komet.kview.mvvm.view.properties.PropertiesController; import dev.ikm.komet.kview.mvvm.view.timeline.TimelineController; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext; import dev.ikm.komet.preferences.KometPreferences; import dev.ikm.tinkar.common.flow.FlowSubscriber; import dev.ikm.tinkar.entity.Entity; @@ -38,9 +39,11 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Pane; import org.carlfx.cognitive.loader.Config; import org.carlfx.cognitive.loader.FXMLMvvmLoader; import org.carlfx.cognitive.loader.JFXNode; +import org.carlfx.cognitive.loader.NamedVm; import org.eclipse.collections.api.list.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,13 +62,13 @@ public class ConceptNode extends ExplorationNodeAbstract { protected static final String STYLE_ID = "kview-details-node"; protected static final String TITLE = "Concept Details"; - private BorderPane conceptDetailsViewBorderPane; - private ConceptController conceptDetailsViewController; +// private BorderPane conceptDetailsViewBorderPane; +// private ConceptController conceptDetailsViewController; /////// Properties slide out ////////////////////////////// protected static final String CONCEPT_PROPERTIES_VIEW_FXML_FILE = "properties.fxml"; private BorderPane propertiesViewBorderPane; - private PropertiesController propertiesViewController; + private ConceptPropertiesController propertiesViewController; ////// Timeline (Time travel) control ////////////////////// protected static final String CONCEPT_TIMELINE_VIEW_FXML_FILE = "timeline.fxml"; @@ -79,7 +82,7 @@ public ConceptNode(ViewProperties viewProperties, KometPreferences nodePreferenc public ConceptNode(ViewProperties viewProperties, KometPreferences nodePreferences, boolean displayOnJournalView) { super(viewProperties, nodePreferences); init(displayOnJournalView); - registerListeners(viewProperties); + //registerListeners(viewProperties); revertPreferences(); } @@ -87,146 +90,149 @@ public ConceptNode(ViewProperties viewProperties, KometPreferences nodePreferenc * Initialization view panel(fxml) and it's view. */ private void init(boolean displayOnJournalView) { - try { - // Let's grab what's inside the properties. Should be the journal window's event topic. - // This details concept window can message events to the current journal window. E.g. progress popup window. - UUID journalWindowTopic = nodePreferences().getUuid(PreferenceKey.CURRENT_JOURNAL_WINDOW_TOPIC).get(); - - // create a unique topic for each concept detail instance - UUID conceptTopic = UUID.randomUUID(); - - // Load Concept Details View Panel (FXML & Controller) - // 1) inside fxml - apply(file, ...view models) - // 2) not in fxml view class - apply(file, view, ...view models) - // 3) not in fxml view instance - apply(file, view instance, ...view models) - Config config = new Config(getClass().getResource(CONCEPT_DETAILS_VIEW_FXML_FILE)) - .controller(new ConceptController(conceptTopic)) - .updateViewModel("conceptViewModel", viewModel -> - viewModel.setPropertyValue(VIEW_PROPERTIES, viewProperties) - .setPropertyValue(CURRENT_JOURNAL_WINDOW_TOPIC, journalWindowTopic)); - JFXNode jfxNode = FXMLMvvmLoader.make(config); - - this.conceptDetailsViewBorderPane = jfxNode.node(); - this.conceptDetailsViewController = jfxNode.controller(); - - // Programmatically change CSS Theme - this.conceptDetailsViewBorderPane.getStylesheets().clear(); - String styleSheet = defaultStyleSheet(); - this.conceptDetailsViewBorderPane.getStylesheets().add(styleSheet); - - if (!displayOnJournalView) { - - // Add the menu drop down for coordinates & activity stream options with Blue Title of concept - Node topPanel = TopPanelFactory.make( - viewProperties, - entityFocusProperty, - activityStreamKeyProperty, - optionForActivityStreamKeyProperty, - false); - this.conceptDetailsViewBorderPane.setTop(topPanel); - } + // Let's grab what's inside the properties. Should be the journal window's event topic. + // This details concept window can message events to the current journal window. E.g. progress popup window. + UUID journalWindowTopic = nodePreferences().getUuid(PreferenceKey.CURRENT_JOURNAL_WINDOW_TOPIC).get(); + +// // create a unique topic for each concept detail instance +// UUID conceptTopic = UUID.randomUUID(); + +// NamedVm conceptViewModelNext = new NamedVm("conceptViewModelNext", new ConceptViewModelNext()); +// conceptViewModelNext.viewModel() +// .setValue(VIEW_PROPERTIES, viewProperties); +// +// Config config = new Config(getClass().getResource(CONCEPT_DETAILS_VIEW_FXML_FILE)) +// .controller(new ConceptController(conceptTopic)) +// .updateViewModel("conceptViewModelNext", viewModel -> +// viewModel.setPropertyValue(VIEW_PROPERTIES, viewProperties) +// .setPropertyValue(CURRENT_JOURNAL_WINDOW_TOPIC, journalWindowTopic)); +// JFXNode jfxNode = FXMLMvvmLoader.make(config); + +// this.conceptDetailsViewBorderPane = jfxNode.node(); +// this.conceptDetailsViewController = jfxNode.controller(); + + // Programmatically change CSS Theme +// this.conceptDetailsViewBorderPane.getStylesheets().clear(); +// String styleSheet = defaultStyleSheet(); +// this.conceptDetailsViewBorderPane.getStylesheets().add(styleSheet); + + + // TODO: REPAIR THIS FUNCTIONALITY, EITHER BY GIVEN THE NODE SOMEHOW THE VIEW PANE OR MOVING THIS THING AWAY (WHICH IS IMPOSSIBLE) +// if (!displayOnJournalView) { +// +// // Add the menu drop down for coordinates & activity stream options with Blue Title of concept +// Node topPanel = TopPanelFactory.make( +// viewProperties, +// entityFocusProperty, +// activityStreamKeyProperty, +// optionForActivityStreamKeyProperty, +// false); +// this.conceptDetailsViewBorderPane.setTop(topPanel); +// } + + // Load Concept Properties View Panel (FXML & Controller) + + +// FXMLLoader propsFXMLLoader = new FXMLLoader(PropertiesController.class.getResource(CONCEPT_PROPERTIES_VIEW_FXML_FILE)); +// propsFXMLLoader.setController(new PropertiesController(conceptTopic)); +// this.propertiesViewBorderPane = propertiesControllerJFXNode.node(); +// this.propertiesViewController = propertiesControllerJFXNode.controller(); +// // style the same as the details view +// this.propertiesViewBorderPane.getStylesheets().add(styleSheet); +// //this.propertiesViewController.updateModel(viewProperties, null); +// +// conceptDetailsViewController.attachPropertiesViewSlideoutTray(this.propertiesViewBorderPane, this.propertiesViewController); +// +// // Load Timeline View Panel (FXML & Controller) +// FXMLLoader timelineFXMLLoader = new FXMLLoader(TimelineController.class.getResource(CONCEPT_TIMELINE_VIEW_FXML_FILE)); +// this.timelineViewBorderPane = timelineFXMLLoader.load(); +// this.timelineViewController = timelineFXMLLoader.getController(); + + // This will highlight with green around the pane when the user selects a date point in the timeline. +// timelineViewController.onDatePointSelected((changeCoordinate) ->{ +// propertiesViewController.getHistoryChangeController().highlightListItemByChangeCoordinate(changeCoordinate); +// }); +// // When Date points are in range (range slider) +// timelineViewController.onDatePointInRange((rangeToggleOn, changeCoordinates) -> { +// if (rangeToggleOn) { +// propertiesViewController.getHistoryChangeController().filterByRange(changeCoordinates); +// propertiesViewController.getHierarchyController().diffNavigationGraph(changeCoordinates); +// } else { +// propertiesViewController.getHistoryChangeController().unfilterByRange(); +// propertiesViewController.getHierarchyController().diffNavigationGraph(Set.of()); +// } +// }); + +// // style the same as the details view +// this.timelineViewBorderPane.getStylesheets().add(styleSheet); +// +// // setup view and view into details view +// conceptDetailsViewController.attachTimelineViewSlideoutTray(this.timelineViewBorderPane); - // Load Concept Properties View Panel (FXML & Controller) - FXMLLoader propsFXMLLoader = new FXMLLoader(PropertiesController.class.getResource(CONCEPT_PROPERTIES_VIEW_FXML_FILE)); - propsFXMLLoader.setController(new PropertiesController(conceptTopic)); - this.propertiesViewBorderPane = propsFXMLLoader.load(); - this.propertiesViewController = propsFXMLLoader.getController(); - // style the same as the details view - this.propertiesViewBorderPane.getStylesheets().add(styleSheet); - this.propertiesViewController.updateModel(viewProperties, null); - - conceptDetailsViewController.attachPropertiesViewSlideoutTray(this.propertiesViewBorderPane, this.propertiesViewController); - - // Load Timeline View Panel (FXML & Controller) - FXMLLoader timelineFXMLLoader = new FXMLLoader(TimelineController.class.getResource(CONCEPT_TIMELINE_VIEW_FXML_FILE)); - this.timelineViewBorderPane = timelineFXMLLoader.load(); - this.timelineViewController = timelineFXMLLoader.getController(); - - // This will highlight with green around the pane when the user selects a date point in the timeline. - timelineViewController.onDatePointSelected((changeCoordinate) ->{ - propertiesViewController.getHistoryChangeController().highlightListItemByChangeCoordinate(changeCoordinate); - }); - // When Date points are in range (range slider) - timelineViewController.onDatePointInRange((rangeToggleOn, changeCoordinates) -> { - if (rangeToggleOn) { - propertiesViewController.getHistoryChangeController().filterByRange(changeCoordinates); - propertiesViewController.getHierarchyController().diffNavigationGraph(changeCoordinates); - } else { - propertiesViewController.getHistoryChangeController().unfilterByRange(); - propertiesViewController.getHierarchyController().diffNavigationGraph(Set.of()); - } - }); - - // style the same as the details view - this.timelineViewBorderPane.getStylesheets().add(styleSheet); - - // setup view and view into details view - conceptDetailsViewController.attachTimelineViewSlideoutTray(this.timelineViewBorderPane); - - } catch (IOException e) { - throw new RuntimeException(e); - } } /** * Wireup listeners(handler code) that will respond on change. E.g. The entityFocusProperty changes when a user selects a concept (in a Navigator tree view). * @param viewProperties */ - private void registerListeners(ViewProperties viewProperties) { - // remove later when closing - this.entityFocusChangeListener = (observable, oldEntityFacade, newEntityFacade) -> { - if (newEntityFacade != null) { - - titleProperty.set(viewProperties.calculator().getPreferredDescriptionTextWithFallbackOrNid(newEntityFacade)); - toolTipTextProperty.set(viewProperties.calculator().getFullyQualifiedDescriptionTextWithFallbackOrNid(newEntityFacade)); - - // Populate Detail View - if (getConceptDetailsViewController() != null) { - getConceptDetailsViewController() - .getConceptViewModel() - .setPropertyValue(CURRENT_ENTITY, newEntityFacade); - getConceptDetailsViewController().updateView(); - } - - // Populate Properties View - if (getPropertiesViewController() != null) { - getPropertiesViewController().updateModel(viewProperties, newEntityFacade); - getPropertiesViewController().updateView(); - } - - // Populate Timeline View - if (getTimelineViewController() != null) { - getTimelineViewController().resetConfigPathAndModules(); - getTimelineViewController().updateModel(viewProperties, newEntityFacade); - getTimelineViewController().updateView(); - } - - } else { - // Show a blank view (nothing selected) - titleProperty.set(EntityLabelWithDragAndDrop.EMPTY_TEXT); - toolTipTextProperty.set(EntityLabelWithDragAndDrop.EMPTY_TEXT); - getConceptDetailsViewController().clearView(); - getPropertiesViewController().clearView(); - getPropertiesViewController().updateModel(viewProperties, newEntityFacade); - } - - }; - - // When a new entity is selected populate the view. An entity has been selected upstream (activity stream) - this.entityFocusProperty.addListener(this.entityFocusChangeListener); - - // If database updates the underlying entity, this will do a force update of the UI. - this.invalidationSubscriber = new FlowSubscriber<>(nid -> { - if (entityFocusProperty.get() != null && entityFocusProperty.get().nid() == nid) { - // component has changed, need to update. - Platform.runLater(() -> entityFocusProperty.set(null)); - Platform.runLater(() -> entityFocusProperty.set(Entity.provider().getEntityFast(nid))); - } - }); - - // Register to the Entity Service - Entity.provider().addSubscriberWithWeakReference(this.invalidationSubscriber); - } + // TODO instead of calling updateView on each individual controller we should update the conceptViewModel once here + // e.g this also tracks the transisition from CREATE -> EDIT mode ??? +// private void registerListeners(ViewProperties viewProperties) { +// // remove later when closing +// this.entityFocusChangeListener = (observable, oldEntityFacade, newEntityFacade) -> { +// if (newEntityFacade != null) { +// +// titleProperty.set(viewProperties.calculator().getPreferredDescriptionTextWithFallbackOrNid(newEntityFacade)); +// toolTipTextProperty.set(viewProperties.calculator().getFullyQualifiedDescriptionTextWithFallbackOrNid(newEntityFacade)); +// +// // Populate Detail View +// if (getConceptDetailsViewController() != null) { +// getConceptDetailsViewController() +// .getConceptViewModel() +// .setPropertyValue(CURRENT_ENTITY, newEntityFacade); +// getConceptDetailsViewController().updateView(); +// } +// +// // Populate Properties View +// // TODO everything should now go through the conceptViewModel - test that +//// if (getPropertiesViewController() != null) { +//// getPropertiesViewController().updateModel(viewProperties, newEntityFacade); +//// getPropertiesViewController().updateView(); +//// } +// +// // Populate Timeline View +// if (getTimelineViewController() != null) { +// getTimelineViewController().resetConfigPathAndModules(); +// getTimelineViewController().updateModel(viewProperties, newEntityFacade); +// getTimelineViewController().updateView(); +// } +// +// } else { +// // Show a blank view (nothing selected) +// titleProperty.set(EntityLabelWithDragAndDrop.EMPTY_TEXT); +// toolTipTextProperty.set(EntityLabelWithDragAndDrop.EMPTY_TEXT); +// getConceptDetailsViewController().clearView(); +// //getPropertiesViewController().clearView(); // does nothing currently +// // getPropertiesViewController().updateModel(viewProperties, newEntityFacade); // TODO: updates history/hirarchy controller +// } +// +// }; +// +// // When a new entity is selected populate the view. An entity has been selected upstream (activity stream) +// this.entityFocusProperty.addListener(this.entityFocusChangeListener); +// +// // If database updates the underlying entity, this will do a force update of the UI. +// this.invalidationSubscriber = new FlowSubscriber<>(nid -> { +// if (entityFocusProperty.get() != null && entityFocusProperty.get().nid() == nid) { +// // component has changed, need to update. +// Platform.runLater(() -> entityFocusProperty.set(null)); +// Platform.runLater(() -> entityFocusProperty.set(Entity.provider().getEntityFast(nid))); +// } +// }); +// +// // Register to the Entity Service +// Entity.provider().addSubscriberWithWeakReference(this.invalidationSubscriber); +// } protected void revertDetailsPreferences() { @@ -236,11 +242,11 @@ protected void revertDetailsPreferences() { * Returns the associated view to update the UI. * @return DetailsController The attached view to the Details view (fxml) */ - public ConceptController getConceptDetailsViewController() { - return conceptDetailsViewController; - } +// public ConceptController getConceptDetailsViewController() { +// return conceptDetailsViewController; +// } - public PropertiesController getPropertiesViewController() { + public ConceptPropertiesController getPropertiesViewController() { return propertiesViewController; } @@ -255,12 +261,14 @@ public String getDefaultTitle() { @Override public void handleActivity(ImmutableList entities) { + LOG.info("handle activiy called ...."); if (entities.isEmpty()) { entityFocusProperty.set(null); } else { EntityFacade entityFacade = entities.get(0); // Only display Concept Details. if (entityFacade instanceof ConceptFacade) { + LOG.info( "GOT OUR FACADE THROUGH handle ACTIVITY ??"); entityFocusProperty.set(entityFacade); } else { entityFocusProperty.set(null); @@ -271,6 +279,7 @@ public void handleActivity(ImmutableList entities) { @Override public final void revertAdditionalPreferences() { if (nodePreferences.hasKey(DetailNodeKey.ENTITY_FOCUS)) { + LOG.info("reverting our entity back through OLD preference system"); nodePreferences.getEntity(DetailNodeKey.ENTITY_FOCUS).ifPresentOrElse(entityFacade -> entityFocusProperty.set(entityFacade), () -> entityFocusProperty.set(null)); } @@ -284,6 +293,7 @@ public String getStyleId() { @Override protected void saveAdditionalPreferences() { + LOG.info("Save current entity through old preference system"); if (entityFocusProperty.get() != null) { nodePreferences.putEntity(DetailNodeKey.ENTITY_FOCUS, entityFocusProperty.get()); } else { @@ -297,9 +307,15 @@ protected void saveDetailsPreferences() { } +// @Override +// public Node getNode() { +// +// return this.conceptDetailsViewBorderPane; +// } + @Override public Node getNode() { - return this.conceptDetailsViewBorderPane; + return new Pane(); } @Override diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesController.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesController.java new file mode 100644 index 0000000000..47e503e61e --- /dev/null +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesController.java @@ -0,0 +1,351 @@ +package dev.ikm.komet.kview.mvvm.view.concept; + +import dev.ikm.komet.framework.view.ViewProperties; +import dev.ikm.komet.kview.mvvm.model.DescrName; +import dev.ikm.komet.kview.mvvm.view.common.StampFormController; +import dev.ikm.komet.kview.mvvm.view.properties.*; +import dev.ikm.komet.kview.mvvm.viewmodel.*; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext.*; +import dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampAddSubmitFormViewModel; +import dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampCreateFormViewModel; +import dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase; +import dev.ikm.tinkar.common.id.PublicId; +import dev.ikm.tinkar.terms.EntityFacade; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import org.carlfx.cognitive.loader.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.UUID; + +import static dev.ikm.komet.kview.fxutils.CssHelper.genText; +import static dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase.Properties.IS_CONFIRMED_OR_SUBMITTED; + +public class ConceptPropertiesController { + private static final Logger LOG = LoggerFactory.getLogger(ConceptPropertiesController.class); + + public static final String CONCEPT_PROPERTIES_FXML_FILE = "concept-properties.fxml"; + + // --- add / edit tab --- + + + // --- history tab --- + + // --- hierarchy tab --- + protected static final String HIERARCHY_VIEW_FXML_FILE = "hierarchy-view.fxml"; + // --- description tab --- + protected static final String EDIT_DESCRIPTIONS_FXML_FILE = "edit-descriptions.fxml"; + + + // --- Properties Tab / Header --- + @FXML private FlowPane propertiesTabsPane; + @FXML private ToggleGroup headerTabToggleButtonGroup; // TODO: rename in FXML + @FXML private ToggleButton editButton; + @FXML private ToggleButton historyButton; + @FXML private ToggleButton hierarchyButton; + @FXML private ToggleButton commentsButton; + + // --- main view --- + @FXML private BorderPane contentBorderPane; + + // All needed state is hold in subsequent ViewModels - DO NOT hold state in this controller + @InjectViewModel + ConceptViewModelNext conceptViewModelNext; + + + // TODO : why did you do that guys :( + private StampAddSubmitFormViewModel stampAddSubmitFormViewModel; + private StampCreateFormViewModel stampCreateFormViewModel; + + private DescrNameViewModelNext descrNameViewModelNext; + + // + + // child JFXNode's created via MVVMLoader in this controller + + // 0 Stamp + private JFXNode stampFormJFXNode; + + // 1 Add/Edit + + private JFXNode menuJFXNode; + + private JFXNode nameMenuJFXNode; + + // fqn / otherName add/edit + private JFXNode nameFormJFXNode; + + + // 2 History + private Pane historyTabsBorderPane; + private HistoryChangeController historyChangeController; + + // 3 Hierarchy + private Pane hierarchyTabBorderPane; + private HierarchyController hierarchyController; + + // 4 Comments + private Pane commentsPane = new StackPane(genText("Comments Pane")); // TODO: nice missing stuff ... + + // + + + public ConceptPropertiesController() { + // we get the Concent_Topic state via ConceptViewModel + + // apperently the StampAddFormViewModel and the StampCreateFormViewModel + } + + @FXML + public void initialize() { + + createAllNodes(); + + setupStampBindings(); + + // ------------------------------------------------------------------------------------------------- + + // bind tab header + + // bind stampForm + + // bind nameForm + + // bind history + + // bind hirarchy + + // general propertiesController logic + + + // -- MainPain -- + SimpleObjectProperty windowToDisplay = conceptViewModelNext.getProperty(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND); + + // mainPaine <-- ViewModel + + // TODO: open respective windows - this sets up the correct values for the property ViewModels + windowToDisplay.subscribe((windowKind) -> { + if (windowKind != null) { // TODO: should neve rbe null ? ThinkingFace + switch (windowKind) { + case STAMP -> { + + EntityFacade thisConceptFacade = conceptViewModelNext.getValue((ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE)); + UUID conceptTopic = conceptViewModelNext.getValue(ConceptPropertyKeys.THIS_UNIQUE_CONCEPT_TOPIC); + ViewProperties viewProperties = conceptViewModelNext.getViewProperties(); + + boolean isValidStamp = conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.HAS_VALID_STAMP); + + if (isValidStamp) { // TODO: test scenario where we "create" a new concept -> than isValidStamp -> special handeld ? + // create can be used if we dont have a EntityFacade ( used if this Concept is newly created ) + stampFormJFXNode.controller().init(stampAddSubmitFormViewModel); + + } else { + // otherwise we can edit the stamp stampCreateFormViewModel + stampFormJFXNode.controller().init(stampCreateFormViewModel); + } + // NOTICE: call update after setting the controller above so we init the "right" ViewModel + stampFormJFXNode.controller().getStampFormViewModel().update(thisConceptFacade, conceptTopic,viewProperties); + + contentBorderPane.setCenter(stampFormJFXNode.node()); + + } + case MENU -> { + LOG.info("MENU: "); + contentBorderPane.setCenter(menuJFXNode.node()); + } + case NAME_MENU -> { + LOG.info("NAME_MENU: "); + + contentBorderPane.setCenter(nameMenuJFXNode.node()); + + // updateDescViewModel save() / restore() cycle + } + case NAME_FORM -> { + + LOG.info("NAME_FORM: "); + SimpleBooleanProperty hasStamp = conceptViewModelNext.getProperty(ConceptPropertyKeys.HAS_VALID_STAMP); + + // either we edit a already existing semantic -> work on semantic PublicID + // we creating a new semantic -> work on a DescName basis ? + // NO! we always work on nid f that + + // in other words we always update the DescNameViewModelNext to either have a) a sémantic PublicID b) a parent Concept Nid + // this is a invariants we can assert + + EntityFacade conceptEntity = conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE); + boolean isNewConcept = conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + + SelectedDescriptionSemantic semanticNameDescr = conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.SELECTED_DESCRIPTION_SEMANTIC); + boolean createNewSemantic = semanticNameDescr == null; + + if (createNewSemantic) { + LOG.info("create new semantic: "); + this.descrNameViewModelNext.createNewSemantic(conceptViewModelNext.getViewProperties()); + } else { + if (semanticNameDescr instanceof SemanticPublicId(PublicId id)) { + LOG.info("update semantic with public id: "); + this.descrNameViewModelNext.updateExistingSemantic(id, conceptViewModelNext.getViewProperties()); + } else if (semanticNameDescr instanceof UncommittedSemanticNameDescr( + DescrName value + )) { + LOG.info("update semantic with descrname: "); + this.descrNameViewModelNext.updateNonCommitedSemantic(value, conceptViewModelNext.getViewProperties()); + } + } + contentBorderPane.setCenter(nameFormJFXNode.node()); + + } + + case HISTORY -> { + contentBorderPane.setCenter(historyTabsBorderPane); + } + case HIERARCHY -> { + contentBorderPane.setCenter(hierarchyTabBorderPane); + } + case COMMENTS -> { + contentBorderPane.setCenter(commentsPane); + } + } + } else { + // TODO: do nothing right? + } + }); + + // TODO: do we need contentCenterPane -> windowToDisplay direction ? + + // -- HeaderTab -- + // TODO: should already be wired up in the FXML + //headerTabToggleButtonGroup.getToggles().addAll(editButton, historyButton, hierarchyButton); // TODO: comments + // headerTab --> ViewModel + headerTabToggleButtonGroup.selectedToggleProperty().subscribe((newToggle) -> { + + if (editButton.equals(newToggle)) windowToDisplay.set(SelectedPropertyWindowKind.MENU); + else if (hierarchyButton.equals(newToggle)) windowToDisplay.set(SelectedPropertyWindowKind.HIERARCHY); + else if (historyButton.equals(newToggle)) windowToDisplay.set(SelectedPropertyWindowKind.HISTORY); + else if (commentsButton.equals(newToggle)) windowToDisplay.set(SelectedPropertyWindowKind.COMMENTS); + }); + + + // TODO: we can/have to remove the onAction handlers in properties-fxml as we have the toggle group now + + // + + + + } + + private void createAllNodes() { + // + + // 0 Stamp + + // does not hold state at this point, e.g it will only get state when something that implements StampFormViewModelBase is + // provided in the StampController on init() call + Config stampConfig = new Config(StampFormController.class.getResource(StampFormController.STAMP_FORM_FXML_FILE)); + stampFormJFXNode = FXMLMvvmLoader.make(stampConfig); + + // one of this viewModels will be put into the Node's controller above depending on the creation state of the conceptViewModel + this.stampAddSubmitFormViewModel = new StampAddSubmitFormViewModel(StampFormViewModelBase.Type.CONCEPT); + this.stampCreateFormViewModel = new StampCreateFormViewModel(StampFormViewModelBase.Type.CONCEPT); + + // 1 Add / Edit Tab + + // --- menu // TODO: no its not one is description + axiom | edit fqn + add otherName + Config menuConfig = new Config(ConceptPropertiesMenuController.class.getResource(ConceptPropertiesMenuController.EDIT_MENU_FXML_FILE)) + .addNamedViewModel(new NamedVm("conceptViewModelNext", conceptViewModelNext)); + this.menuJFXNode = FXMLMvvmLoader.make(menuConfig); + + // --- name menu + Config editDescriptionConfig = new Config(ConceptPropertiesNameMenuController.class.getResource(ConceptPropertiesNameMenuController.EDIT_MENU_FXML_FILE)) + .addNamedViewModel(new NamedVm("conceptViewModelNext", conceptViewModelNext)); + this.nameMenuJFXNode = FXMLMvvmLoader.make(editDescriptionConfig); + + + // --- nameForm to Add/Edit FQN or otherName + // TODO: maybe the same update trick to the viewModel / Controller as in stamp to the descViewModelNext -> it needs + DescrNameViewModelNext descrNameViewModelNext = new DescrNameViewModelNext(conceptViewModelNext.getViewProperties()); + + Config nameConfig = new Config(ConceptPropertiesNameFormController.class.getResource(ConceptPropertiesNameFormController.CONCEPT_PROP_NAMES_FXML_FILE)); + nameConfig.addNamedViewModel(new NamedVm("descrNameViewModelNext", descrNameViewModelNext)); + nameConfig.addNamedViewModel(new NamedVm("conceptViewModelNext", conceptViewModelNext)); +// Config nameConfig = new Config( +// ConceptPropertiesNameFormController.class.getResource(ConceptPropertiesNameFormController.CONCEPT_PROP_NAMES_FXML_FILE), +// new NamedVm("descrNameViewModelNext", descrNameViewModelNext)) +// .addNamedViewModel(new NamedVm("conceptViewModelNext", conceptViewModelNext)); + this.nameFormJFXNode = FXMLMvvmLoader.make(nameConfig); + + + this.descrNameViewModelNext = descrNameViewModelNext; + + + // 2 History Tab TODO: update to MVVM + FXMLLoader loader = new FXMLLoader( HistoryChangeController.class.getResource(HistoryChangeController.HISTORY_CHANGE_FXML_FILE)); + try { historyTabsBorderPane = loader.load();} catch (IOException e) { + throw new RuntimeException(e); + } + + historyChangeController = loader.getController(); + + // 3 Hierarchy TabTODO: update to MVVM + FXMLLoader loader2 = new FXMLLoader(HierarchyController.class.getResource(HIERARCHY_VIEW_FXML_FILE)); + try { hierarchyTabBorderPane = loader2.load(); } catch (IOException e) { + throw new RuntimeException(e); + } + hierarchyController = loader2.getController(); + + // 4 Comments Tab TODO: implement :( + + // + } + + private void setupStampBindings() { + SimpleBooleanProperty confirmedPressedProperty = this.stampCreateFormViewModel.getProperty(IS_CONFIRMED_OR_SUBMITTED); + confirmedPressedProperty.subscribe( isPressed -> { + if (isPressed) { + LOG.info("Confirmed pressed on stampFormMenu"); + conceptViewModelNext.setPropertyValue(ConceptPropertyKeys.HAS_VALID_STAMP, true); + } + }); + + //TODO: bind other FormViewModel + + } + + public HistoryChangeController getHistoryChangeController() { + return historyChangeController; + } + + public HierarchyController getHierarchyController() { + return hierarchyController; + } + + + public StampFormViewModelBase getStampFormViewModel() { + // TODO check if this is equivalent to edit mode or isValidStamp + boolean isNewConcept = conceptViewModelNext.getPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT); + if (isNewConcept) { + return stampCreateFormViewModel; + } else { + return stampAddSubmitFormViewModel; + } + } + + /** + * Returns the propertiesTabsPane to be used as a draggable region. + * @return The FlowPane containing the property tabs + */ + public FlowPane getPropertiesTabsPane() { + return propertiesTabsPane; + } +} diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesMenuController.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesMenuController.java new file mode 100644 index 0000000000..7050e09eee --- /dev/null +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesMenuController.java @@ -0,0 +1,50 @@ +package dev.ikm.komet.kview.mvvm.view.concept; + + + +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext;import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleStringProperty; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import org.carlfx.cognitive.loader.InjectViewModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class ConceptPropertiesMenuController { + private static final Logger LOG = LoggerFactory.getLogger(ConceptPropertiesMenuController.class); + + public static final String EDIT_MENU_FXML_FILE = "concept-prop-menu.fxml"; + + @FXML private Label conceptTitleLabel; + @FXML private Button editDescriptionsButton; + @FXML private Button editAxiomsButton; + + @InjectViewModel + ConceptViewModelNext conceptViewModelNext; + + public ConceptPropertiesMenuController() { + + } + + @FXML + public void initialize() { + + // TODO: + SimpleStringProperty fqnTitleTextProp = conceptViewModelNext.getProperty("add fqn text to conceptModel. E.g get it via NID"); + + conceptTitleLabel.textProperty().bind( + Bindings.concat("Edit: ", fqnTitleTextProp) + ); + + // open the name menu pane + editDescriptionsButton.setOnMouseClicked(mouseEvent -> { + conceptViewModelNext.setValue(ConceptViewModelNext.ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, ConceptViewModelNext.SelectedPropertyWindowKind.NAME_MENU); + + }); + + // TODO: editAxiomsButton when Axiom Controller is implemented + } + +} diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameFormController.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameFormController.java new file mode 100644 index 0000000000..6e0ab4f3b5 --- /dev/null +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameFormController.java @@ -0,0 +1,238 @@ +package dev.ikm.komet.kview.mvvm.view.concept; + +import dev.ikm.komet.framework.view.ViewProperties; +import dev.ikm.komet.kview.common.ViewCalculatorUtils; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext.*; +import dev.ikm.komet.kview.mvvm.viewmodel.DescrNameViewModelNext; +import dev.ikm.komet.kview.mvvm.viewmodel.DescrNameViewModelNext.*; +import dev.ikm.tinkar.terms.ComponentWithNid; +import javafx.beans.binding.BooleanBinding; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.*; +import javafx.scene.layout.VBox; +import org.carlfx.cognitive.loader.InjectViewModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConceptPropertiesNameFormController { + private static final Logger LOG = LoggerFactory.getLogger(ConceptPropertiesNameFormController.class); + + public static final String CONCEPT_PROP_NAMES_FXML_FILE = "concept-prop-name-form.fxml"; + + // + @FXML private Label titleLabel; + + //
+ @FXML private TextField nameTextField; // holds either FQN or otherName + @FXML private ComboBox typeDisplayComboBox; // TODO: check recent commits if still disabled in FXML + @FXML private ComboBox caseSignificanceComboBox; + @FXML private ComboBox statusComboBox; + @FXML private ComboBox moduleComboBox; + @FXML private ComboBox languageComboBox; + + // Dialects (visible once a language is selected) //TODO: make that happen + @FXML private VBox dialectsContainer; + @FXML private Label dialect1Label; + @FXML private Label dialect2Label; + @FXML private Label dialect3Label; + @FXML private ComboBox dialectComboBox1; + @FXML private ComboBox dialectComboBox2; + @FXML private ComboBox dialectComboBox3; + + @FXML private Label commentsLabel; // // TODO remove as unused + + @FXML private TextArea commentsTextArea; + + + // + @FXML private Button submitButton; + @FXML private Button cancelButton; + + + @InjectViewModel + private ConceptViewModelNext conceptViewModelNext; + + @InjectViewModel + private DescrNameViewModelNext descrNameViewModelNext; + + public ConceptPropertiesNameFormController() { + // conceptTopic is available through ConceptViewModel + } + + @FXML + public void initialize() { + + + initFormTitle(); + + initNameText(); + +// initTypeComboBox(); +// initCaseSignificanceComboBox(); +// initStatusComboBox(); +// initModuleComboBox(); +// initLanguageComboBox(); + + bindLanguageSelectionToDialects(); + + // -- bottom buttons -- + submitButton.disableProperty().bind(descrNameViewModelNext.invalidProperty()); // TODO, use validator that already present + cancelButton.setOnMouseClicked(mouseEvent -> { // TODO: conceptViewModel update + closeView(); + }); + } + + private ViewProperties getViewProperties() { + return conceptViewModelNext.getViewProperties(); + } + + private void initFormTitle() { + titleLabel.textProperty().bindBidirectional(descrNameViewModelNext.getProperty(DescrPropKeys.TITLE_TEXT)); + } + + private void initNameText() { + titleLabel.textProperty().bindBidirectional(descrNameViewModelNext.getProperty(DescrPropKeys.NAME)); + } + + private void initTypeComboBox() { + ViewCalculatorUtils.initComboBox( + typeDisplayComboBox, + descrNameViewModelNext.getObservableList(DescrPropKeys.NAME_TYPE_VARIANTS), + this::getViewProperties); + + typeDisplayComboBox.valueProperty().bindBidirectional(descrNameViewModelNext.getProperty(DescrPropKeys.NAME_TYPE_VARIANTS)); + } + + private void initCaseSignificanceComboBox() { + ViewCalculatorUtils.initComboBox( + caseSignificanceComboBox, + descrNameViewModelNext.getObservableList(DescrPropKeys.CASE_SIGNIFICANCE_VARIANTS), + this::getViewProperties); + + caseSignificanceComboBox.valueProperty().bindBidirectional(descrNameViewModelNext.getProperty(DescrPropKeys.SELECTED_CASE_SIGNIFICANCE)); + } + + private void initStatusComboBox() { + ViewCalculatorUtils.initComboBox( + statusComboBox, + descrNameViewModelNext.getObservableList(DescrPropKeys.STATUS_VARIANTS), + this::getViewProperties); + + statusComboBox.valueProperty().bindBidirectional(descrNameViewModelNext.getProperty(DescrPropKeys.STATUS_VARIANTS)); + } + + private void initModuleComboBox() { + ViewCalculatorUtils.initComboBox( + moduleComboBox, + descrNameViewModelNext.getObservableList(DescrPropKeys.MODULE_VARIANTS), + this::getViewProperties); + + moduleComboBox.valueProperty().bindBidirectional(descrNameViewModelNext.getProperty(DescrPropKeys.SELECTED_MODULE)); + } + + private void initLanguageComboBox() { + ViewCalculatorUtils.initComboBox( + languageComboBox, + descrNameViewModelNext.getObservableList(DescrPropKeys.LANGUAGE_VARIANTS), + this::getViewProperties); + + languageComboBox.valueProperty().bindBidirectional(descrNameViewModelNext.getProperty(DescrPropKeys.SELECTED_LANGUAGE)); + } + + private void bindLanguageSelectionToDialects() { + BooleanBinding isLanguageSelected = languageComboBox.valueProperty().isNotNull(); + dialectsContainer.visibleProperty().bind(isLanguageSelected); + } + + + @FXML + private void onCancel(ActionEvent actionEvent) { + actionEvent.consume(); + + closeView(); + } + + @FXML + private void onSubmit(ActionEvent actionEvent) { + actionEvent.consume(); + + // implicitly calls validate +// descrNameViewModelNext.save(); +// +// // check the validation result +// if (descrNameViewModelNext.hasErrorMsgs()) { +// descrNameViewModelNext.getValidationMessages().forEach(msg -> LOG.error("Validation error {}", msg)); +// return; +// } +// +// // validate check ok -> save copied the ViewProperties into ModelProperties +// // we build a record out of the updated ModelProerties +// DescrName record = descrNameViewModelNext.create(); // TODO: check why SMEANTIC_PUB_ID and PARENT_PUB_ID are needed | do they need exist befor this? +// +// // check if we are in create or view/edit mode +// String mode = conceptViewModel.getValue(MODE); +// +// +// PublicId thisNamePublicId = conceptViewModel.getValue(SELECTED_NAME_DESCRIPTION_PUBLIC_ID); +// ConceptViewModel.SelectedNameDescriptionKind thisNameKind = conceptViewModel.getValue(SELECTED_NAME_DESCRIPTION_KIND); +// +// +// +// if (CREATE.equals(mode)) { // than we have no publicID for this name -> but the record itself should track that info +// switch (thisNameKind) { +// case FQN -> { +// ObservableList fqnNames = conceptViewModel.getObservableList(ADDED_BUT_NOT_COMMITED_FQN_NAME_RECORDS); +// fqnNames.add(record); +// } +// case otherName -> { +// ObservableList otherNames = conceptViewModel.getObservableList(ADDED_BUT_NOT_COMMITED_OTHER_NAME_RECORDS); +// otherNames.add(record); // TODO does this trigger an update of the model or do we need to reinsert? +// } +// } +// +// } else { // edit/view mode TODO: is it allowed to add name / kind in edit view mode? +// +// ViewProperties viewProperties = conceptViewModel.getViewProperties(); +// +// if (thisNamePublicId != null) { // this is a Name we Edit +// switch (thisNameKind) { +// case FQN -> { // TODO: investigate if this calls should be decoupled like in Pattern ? +// // THIS CALL DOES NOT UPDATE THE VIEWMODEL BUT THE DB (cache?) +// descrNameViewModelNext.updateFullyQualifiedName(thisNamePublicId,viewProperties); +// } +// case otherName -> { +// // THIS CALL DOES NOT UPDATE THE VIEWMODEL BUT THE DB (cache?) +// descrNameViewModelNext.updateOtherName(thisNamePublicId,viewProperties); +// } +// } +// } else { // this is a Name we Add to existing ones +// +// switch (thisNameKind) { +// case FQN -> { +// ObservableList fqnNames = conceptViewModel.getObservableList(ADDED_BUT_NOT_COMMITED_FQN_NAME_RECORDS); +// fqnNames.add(record); +// } +// case otherName -> { +// ObservableList otherNames = conceptViewModel.getObservableList(ADDED_BUT_NOT_COMMITED_OTHER_NAME_RECORDS); +// otherNames.add(record); // TODO does this trigger an update of the model or do we need to reinsert? +// } +// } +// } +// +// } + + // TODO: should we clear the associate descrViewModel? i think its better to always start with a fresh one for a window. + + closeView(); + + + } + + private void closeView() { + conceptViewModelNext.setValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NONE); + } + +} + diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameMenuController.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameMenuController.java new file mode 100644 index 0000000000..08a8ae39ce --- /dev/null +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/concept/ConceptPropertiesNameMenuController.java @@ -0,0 +1,48 @@ +package dev.ikm.komet.kview.mvvm.view.concept; + + + +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext; +import dev.ikm.komet.kview.mvvm.viewmodel.ConceptViewModelNext.*; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import org.carlfx.cognitive.loader.InjectViewModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConceptPropertiesNameMenuController { + private static final Logger LOG = LoggerFactory.getLogger(ConceptPropertiesNameMenuController.class); + + public static final String EDIT_MENU_FXML_FILE = "concept-prop-name-menu.fxml"; + + @FXML private Button editFullyQualifiedNameButton; + @FXML private Button closePropertiesPanelButton; + @FXML private Button addOtherNameButton; + + @InjectViewModel + ConceptViewModelNext conceptViewModelNext; + + public ConceptPropertiesNameMenuController() { + + } + + @FXML + public void initialize() { + + editFullyQualifiedNameButton.setOnMouseClicked( mouseEvent -> { + conceptViewModelNext.setValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); + + }); + + addOtherNameButton.setOnMouseClicked( mouseEvent -> { + conceptViewModelNext.setValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NAME_FORM); + + }); + + // TODO: make sure slideIn is triggerd correctly? + closePropertiesPanelButton.setOnMouseClicked(mouseEvent -> { + conceptViewModelNext.setValue(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, SelectedPropertyWindowKind.NONE); + }); + + } +} diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/journal/JournalController.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/journal/JournalController.java index 65910d41ad..0f70560dae 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/journal/JournalController.java +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/journal/JournalController.java @@ -1230,8 +1230,7 @@ private void setupWorkspaceWindow(ChapterKlWindow chapterKlWindow) { } // Getting the details node from the concept window - ConceptNode conceptNode = conceptKlWindow.getDetailsNode(); - conceptNode.getConceptDetailsViewController().onReasonerSlideoutTray(reasonerToggleConsumer); + conceptKlWindow.getConceptController().onReasonerSlideoutTray(reasonerToggleConsumer); } } diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/properties/HistoryChangeController.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/properties/HistoryChangeController.java index 6fb101c040..675761e30a 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/properties/HistoryChangeController.java +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/view/properties/HistoryChangeController.java @@ -51,6 +51,9 @@ */ public class HistoryChangeController implements BasicController { private static final Logger LOG = LoggerFactory.getLogger(HistoryChangeController.class); + + public static final String HISTORY_CHANGE_FXML_FILE = "history-change-selection.fxml"; + protected static final String DESCRIPTION_LIST_ITEM_FXML_FILE = "change-list-item.fxml"; @FXML private ChoiceBox changeFilterChoiceBox; diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModel.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModel.java index a9a8c4362d..befa89fd4a 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModel.java +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModel.java @@ -113,7 +113,6 @@ public ConceptViewModel() { /** * Validates the view model and if there are no errors, save to the database. - * Is called everytime user is adding data to ConceptViewModel. * * @return */ @@ -329,6 +328,7 @@ private void saveOtherNameWithinCreateConcept(Transaction transaction, StampEnti }); } + // TODO: since this is not called i assume thats why no new names can be added to existing concept public void addOtherName(EditCoordinateRecord editCoordinateRecord, DescrName otherName) { Transaction transaction = Transaction.make(); diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModelNext.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModelNext.java new file mode 100644 index 0000000000..7fd0c39b5c --- /dev/null +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/ConceptViewModelNext.java @@ -0,0 +1,368 @@ +package dev.ikm.komet.kview.mvvm.viewmodel; + +import dev.ikm.komet.framework.builder.AxiomBuilderRecord; +import dev.ikm.komet.framework.builder.ConceptEntityBuilder; +import dev.ikm.komet.framework.view.ViewProperties; +import dev.ikm.komet.kview.controls.Toast; +import dev.ikm.komet.kview.mvvm.model.DescrName; +import dev.ikm.komet.kview.mvvm.view.journal.JournalController; +import dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase; +import dev.ikm.tinkar.common.id.PublicId; +import dev.ikm.tinkar.common.id.PublicIds; +import dev.ikm.tinkar.common.service.TinkExecutor; +import dev.ikm.tinkar.entity.*; +import dev.ikm.tinkar.entity.graph.DiTreeEntity; +import dev.ikm.tinkar.entity.graph.EntityVertex; +import dev.ikm.tinkar.entity.transaction.CommitTransactionTask; +import dev.ikm.tinkar.entity.transaction.Transaction; +import dev.ikm.tinkar.terms.*; +import org.carlfx.cognitive.viewmodel.SimpleViewModel; +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.list.ImmutableList; +import org.eclipse.collections.api.list.MutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase.Properties.IS_CONFIRMED_OR_SUBMITTED; +import static dev.ikm.komet.kview.mvvm.viewmodel.stamp.StampFormViewModelBase.Properties.STATUS; +import static dev.ikm.tinkar.coordinate.stamp.StampFields.MODULE; +import static dev.ikm.tinkar.coordinate.stamp.StampFields.PATH; + +public class ConceptViewModelNext extends SimpleViewModel { + private static final Logger LOG = LoggerFactory.getLogger(ConceptViewModelNext.class); + + /// ViewModel Property Keys + public enum ConceptPropertyKeys { + /// A newly added concept does not have a nid at the start. + /// + /// If new concept (true) we commit every associated created + /// semantics ( e.g fqn / otherNames) in one go with the "same" stamp that we commit the concept for. This + /// happens currently when the user adds a Axiom. + /// + /// Its also crucial that we first create a ValidStamp, because than we can create a initial stamp record and then + /// use the then created ConceptEntity facades nid to create an set of Semantic NameDescription. Without a Concept + /// Semantic descriptions would not know where to point to. E.g the problem would be that a new Semantic Description + /// would not know what type fields it should provide (language, case_significance, modules etc) for selection as this is depending on the Concept? + /// + /// If new concept (false) for every change to a associated description semantic we create a new semantic record + /// and directly commit it with in that moment generated stamp. + THIS_IS_A_NEW_CONCEPT, + + THIS_CONCEPT_ENTITY_FACADE, + /// On window creation we got a uuid from our parent identifying our window. Only related to this window instance + THIS_UNIQUE_CONCEPT_TOPIC, + + ASOCIATED_FQN_DESCRIPTION_SEMANTICS, + ASOCIATED_OTHER_NAME_DESCRIPTION_SEMANTICS, + + /// Either point to ourselves, to other fqn semantics or otherName (regular Name) semantics. + /// + /// If this is null we know that we need to create a new semantic from scratch in nameForm + SELECTED_DESCRIPTION_SEMANTIC, + + // stuff from god + VIEW_PROPERTIES, + /// Each journal window has a unique topic. E.g a uuid identifying our journal parent + ASOCIATED_JOURNAL_WINDOW_TOPIC, + + SELECTED_PROPERTY_WINDOW_KIND, + PROPERTY_WINDOW_OPEN, + + UNCOMMITED_CHANGES, + + HAS_VALID_STAMP, + + AXIOM, + + } + + // --- Property ValuesTypes --- + + /// The current PaneType in Property View + public enum SelectedPropertyWindowKind{ + STAMP, + MENU, + NAME_MENU, + NAME_FORM, + HISTORY, + HIERARCHY, + COMMENTS, + NONE + } + + public sealed interface SelectedDescriptionSemantic + permits SemanticPublicId, UncommittedSemanticNameDescr {} + + public record SemanticPublicId(PublicId value) implements SelectedDescriptionSemantic {} + public record UncommittedSemanticNameDescr(DescrName value) implements SelectedDescriptionSemantic {} + + + public static String SUFFICIENT_SET = "Sufficient Set"; + public static String NECESSARY_SET = "Necessary Set"; + + public ConceptViewModelNext() { + addProperty(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT, true) + .addProperty(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE, (EntityFacade) null) + .addProperty(ConceptPropertyKeys.THIS_UNIQUE_CONCEPT_TOPIC, (UUID) null) + .addProperty(ConceptPropertyKeys.ASOCIATED_FQN_DESCRIPTION_SEMANTICS, new ArrayList()) + .addProperty(ConceptPropertyKeys.ASOCIATED_OTHER_NAME_DESCRIPTION_SEMANTICS, new ArrayList()) + .addProperty(ConceptPropertyKeys.SELECTED_DESCRIPTION_SEMANTIC, (SelectedDescriptionSemantic) null) + .addProperty(ConceptPropertyKeys.VIEW_PROPERTIES, (ViewProperties) null) + .addProperty(ConceptPropertyKeys.ASOCIATED_JOURNAL_WINDOW_TOPIC, (UUID) null) + .addProperty(ConceptPropertyKeys.SELECTED_PROPERTY_WINDOW_KIND, (SelectedPropertyWindowKind) null) + .addProperty(ConceptPropertyKeys.PROPERTY_WINDOW_OPEN, false) + .addProperty(ConceptPropertyKeys.UNCOMMITED_CHANGES, false) + .addProperty(ConceptPropertyKeys.HAS_VALID_STAMP, false) + .addProperty(ConceptPropertyKeys.AXIOM, (String) null); + + } + + public ViewProperties getViewProperties() { + return getPropertyValue(ConceptPropertyKeys.VIEW_PROPERTIES); + } + + public void updateModel() { + // TODO: entity facade && newConcept? + + // + // identicon effected by, VIEW_PROPERTIES, THIS_CONCEPT_ENTITY_FACADE + // stamp effected by, THIS_IS_A_NEW_CONCEPT HAS_VALID_STAMP THIS_CONCEPT_ENTITY_FACADE + + // + // addButton effected by, HAS_VALID_STAMP + // TODO: fqn displayed and otherName should be reactive to entityFacade changes + // fqn displayed effected by, ASOCIATED_FQN_CONCEPTS, HAS_VALID_STAMP + // otherName displayed effected by, ASOCIATED_OTHER_NAME_CONCEPTS, HAS_VALID_STAMP + + // + + + } + + /** + * Validates the view model and if there are no errors, save to the database. + * + * @return + */ + public boolean createConcept(StampFormViewModelBase stampFormViewModel) { + save(); // View Model xfer values. does not save to the database but validates data and then copies data from properties to model values. + + // Validation errors will not create record. + // TODO: add new validator +// if (!getValidationMessages().isEmpty()) { +// return false; +// } + + // stamp is populated? + if (!(Boolean)stampFormViewModel.getPropertyValue(IS_CONFIRMED_OR_SUBMITTED)) { + return false; + } + + // Create concept + List fqnList = getObservableList(ConceptPropertyKeys.ASOCIATED_FQN_DESCRIPTION_SEMANTICS); // TODO: this still assumes that on creation we can only have a FQN + DescrName fqnDescrName = fqnList.get(0); + Transaction transaction = Transaction.make("New concept for: " + fqnDescrName.getNameText()); + + + // Copy STAMP info + // - status + State status = stampFormViewModel.getValue(STATUS); + // - getAuthor from editCoordinate + ViewProperties viewProperties = getViewProperties(); + EntityFacade authorConcept = viewProperties.nodeView().editCoordinate().getAuthorForChanges(); + // - module + ConceptEntity module = stampFormViewModel.getValue(MODULE); + // - path + ConceptEntity path = stampFormViewModel.getValue(PATH); + + StampEntity stampEntity = transaction.getStamp(status, authorConcept.nid(), + module.nid(), path.nid()); + + ConceptEntityBuilder newConceptBuilder = ConceptEntityBuilder.builder(stampEntity); + + + PublicId conceptPublicId = PublicIds.newRandom(); + ConceptRecord conceptRecord = ConceptRecord.build(conceptPublicId.asUuidList().get(0), stampEntity.lastVersion()); + + ConceptFacade conceptFacade = EntityProxy.Concept.make(conceptRecord.publicId()) ; + + // add the Fully Qualified Name to the new concept + saveFQNwithinCreateConcept(transaction, stampEntity, fqnDescrName, conceptFacade); + + + AxiomBuilderRecord ab = newConceptBuilder.axiomBuilder(); + + // determine sufficient or necessary + if (NECESSARY_SET.equals(getValue(ConceptPropertyKeys.AXIOM))) { + ab.withNecessarySet( +// ab.makeConceptReference(TinkarTerm.LANGUAGE), +// ab.makeConceptReference(TinkarTerm.DESCRIPTION_ASSEMBLAGE), + ab.makeRoleGroup( + ab.makeSome(TinkarTerm.PART_OF, TinkarTerm.ANONYMOUS_CONCEPT) + /*ab.makeSome(TinkarTerm.PART_OF, TinkarTerm.LANGUAGE)*/) + ); + } else if (SUFFICIENT_SET.equals(getValue(ConceptPropertyKeys.AXIOM))) { + ab.withSufficientSet( +// ab.makeConceptReference(TinkarTerm.LANGUAGE), +// ab.makeConceptReference(TinkarTerm.DESCRIPTION_ASSEMBLAGE), + ab.makeRoleGroup( + ab.makeSome(TinkarTerm.PART_OF, TinkarTerm.ANONYMOUS_CONCEPT) + /*ab.makeSome(TinkarTerm.PART_OF, TinkarTerm.LANGUAGE)*/)); + } + + // add the axiom + buildAxiom(ab, conceptRecord, stampEntity, transaction); + + // TODO: clean up this type mess ... + List otherNames = (List) getValueMap().get(ConceptPropertyKeys.ASOCIATED_OTHER_NAME_DESCRIPTION_SEMANTICS); + if (otherNames.size() > 0) { + // if there are other names defined, then add them to the newly created concept + saveOtherNameWithinCreateConcept(transaction, stampEntity, otherNames, conceptFacade); + } + + transaction.addComponent(conceptRecord); + Entity.provider().putEntity(conceptRecord); + + CommitTransactionTask commitTransactionTask = new CommitTransactionTask(transaction); + try { + TinkExecutor.threadPool().submit(commitTransactionTask).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + + String pathText = getViewProperties().calculator().getDescriptionTextOrNid(path.nid()); + String moduleText = getViewProperties().calculator().getDescriptionTextOrNid(module.nid()); + String statusText = getViewProperties().calculator().getDescriptionTextOrNid(status.nid()); + + // alert the user of the concept being created and were it exists + JournalController.toast() + .show( + Toast.Status.SUCCESS, + String.format("Concept created %s, %s, %s", pathText, moduleText, statusText) + ); + + // place inside as current Concept + // TODO: ;( + setValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE, conceptFacade); + setPropertyValue(ConceptPropertyKeys.THIS_CONCEPT_ENTITY_FACADE, conceptFacade); + setValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT, false); + setPropertyValue(ConceptPropertyKeys.THIS_IS_A_NEW_CONCEPT, false); + return true; + } + + private void saveFQNwithinCreateConcept(Transaction transaction, StampEntity stampEntity, DescrName fqnNameDescr, ConceptFacade conceptFacade) { + + // create a new public id for the FQN semantic + PublicId fqnPublicId = PublicIds.of(UUID.randomUUID()); + + // the versions that we will first populate with the existing versions of the semantic + RecordListBuilder versions = RecordListBuilder.make(); + + SemanticRecord descriptionSemantic = SemanticRecord.makeNew(fqnPublicId, TinkarTerm.DESCRIPTION_PATTERN.nid(), + conceptFacade.nid(), versions); + + // we are grabbing the form data + // populating the field values for the new version we are writing + MutableList descriptionFields = Lists.mutable.empty(); + + // get these from the view model + descriptionFields.add(fqnNameDescr.getLanguage()); + descriptionFields.add(fqnNameDescr.getNameText()); + descriptionFields.add(fqnNameDescr.getCaseSignificance()); + descriptionFields.add(TinkarTerm.FULLY_QUALIFIED_NAME_DESCRIPTION_TYPE); + + + // adding the new (edit form) version here + versions.add(SemanticVersionRecordBuilder.builder() + .chronology(descriptionSemantic) + .stampNid(stampEntity.nid()) + .fieldValues(descriptionFields.toImmutable()) + .build()); + + // apply the updated versions to the new semantic record + SemanticRecord newSemanticRecord = SemanticRecordBuilder.builder(descriptionSemantic).versions(versions.toImmutable()).build(); + + // put the new semantic record in the transaction + transaction.addComponent(newSemanticRecord); + + // perform the save + Entity.provider().putEntity(newSemanticRecord); + } + + private void saveOtherNameWithinCreateConcept(Transaction transaction, StampEntity stampEntity, List otherNames, ConceptFacade conceptFacade) { + + otherNames.forEach(descrName -> { + //vm.save(); + + descrName.setParentConcept(conceptFacade.publicId()); + + PublicId otherNamePublicId = PublicIds.of(UUID.randomUUID()); ///// update the VM with our new public ID + descrName.setSemanticPublicId(otherNamePublicId); + + // the versions that we will first populate with the existing versions of the semantic + RecordListBuilder versions = RecordListBuilder.make(); + + SemanticRecord descriptionSemantic = SemanticRecord.makeNew(otherNamePublicId, TinkarTerm.DESCRIPTION_PATTERN.nid(), + conceptFacade.nid(), versions); + + // we are grabbing the form data + // populating the field values for the new version we are writing + MutableList descriptionFields = Lists.mutable.empty(); + descriptionFields.add(descrName.getLanguage()); + descriptionFields.add(descrName.getNameText()); + descriptionFields.add(descrName.getCaseSignificance()); + descriptionFields.add(TinkarTerm.REGULAR_NAME_DESCRIPTION_TYPE); + + // iterating over the existing versions and adding them to a new record list builder + descriptionSemantic.versions().forEach(version -> versions.add(version)); + + // adding the new (edit form) version here + versions.add(SemanticVersionRecordBuilder.builder() + .chronology(descriptionSemantic) + .stampNid(stampEntity.nid()) + .fieldValues(descriptionFields.toImmutable()) + .build()); + + // apply the updated versions to the new semantic record + SemanticRecord newSemanticRecord = SemanticRecordBuilder.builder(descriptionSemantic).versions(versions.toImmutable()).build(); + + // put the new semantic record in the transaction + transaction.addComponent(newSemanticRecord); + + // perform the save + Entity.provider().putEntity(newSemanticRecord); + }); + } + + private void buildAxiom(AxiomBuilderRecord axiomBuilder, ConceptRecord conceptRecord, StampEntity stampEntity, Transaction transaction) { + DiTreeEntity.Builder axiomTreeEntityBuilder = DiTreeEntity.builder(); + EntityVertex rootVertex = EntityVertex.make(axiomBuilder); + axiomTreeEntityBuilder.setRoot(rootVertex); + recursiveAddChildren(axiomTreeEntityBuilder, rootVertex, axiomBuilder); + + ImmutableList axiomField = Lists.immutable.of(axiomTreeEntityBuilder.build()); + SemanticRecord statedAxioms = SemanticRecord.build(UUID.randomUUID(), + TinkarTerm.EL_PLUS_PLUS_STATED_AXIOMS_PATTERN.nid(), + conceptRecord.nid(), + stampEntity.lastVersion(), + axiomField); + transaction.addComponent(statedAxioms); + Entity.provider().putEntity(statedAxioms); + } + + private void recursiveAddChildren(DiTreeEntity.Builder axiomTreeBuilder, EntityVertex parentVertex, AxiomBuilderRecord parentAxiom) { + for (AxiomBuilderRecord child : parentAxiom.children()) { + EntityVertex childVertex = EntityVertex.make(child); + axiomTreeBuilder.addVertex(childVertex); + axiomTreeBuilder.addEdge(childVertex, parentVertex); + recursiveAddChildren(axiomTreeBuilder, childVertex, child); + } + } + + +} diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModel.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModel.java index cba1e63f0e..b8fb861f3e 100644 --- a/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModel.java +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModel.java @@ -31,6 +31,7 @@ import dev.ikm.tinkar.entity.StampEntity; import dev.ikm.tinkar.entity.transaction.CommitTransactionTask; import dev.ikm.tinkar.entity.transaction.Transaction; +import dev.ikm.tinkar.terms.EntityProxy; import dev.ikm.tinkar.terms.State; import dev.ikm.tinkar.terms.TinkarTerm; import javafx.beans.property.ReadOnlyObjectProperty; @@ -124,7 +125,7 @@ public Set findAllCaseSignificants(ViewProperties viewProperties) return CASE_SIGNIFICANCE_OPTIONS; } - public void updateFullyQualifiedName(PublicId publicId, ViewProperties viewProperties) { + private void updateSemanticName(PublicId publicId, ViewProperties viewProperties, EntityProxy.Concept semanticType) { Transaction transaction = Transaction.make(); StampEntity stampEntity = transaction.getStamp( @@ -151,7 +152,7 @@ public void updateFullyQualifiedName(PublicId publicId, ViewProperties viewPrope descriptionFields.add(getValue(LANGUAGE)); descriptionFields.add(getValue(NAME_TEXT)); descriptionFields.add(getValue(CASE_SIGNIFICANCE)); - descriptionFields.add(TinkarTerm.FULLY_QUALIFIED_NAME_DESCRIPTION_TYPE); + descriptionFields.add(semanticType); // iterating over the existing versions and adding them to a new record list builder theSemantic.versions().forEach(version -> versions.add(version)); @@ -177,6 +178,20 @@ public void updateFullyQualifiedName(PublicId publicId, ViewProperties viewPrope TinkExecutor.threadPool().submit(commitTransactionTask); } + public void updateFullyQualifiedName(PublicId publicId, ViewProperties viewProperties) { + EntityProxy.Concept semanticType = TinkarTerm.FULLY_QUALIFIED_NAME_DESCRIPTION_TYPE; + + updateSemanticName(publicId, viewProperties , semanticType); + LOG.info("transaction complete"); + } + + public void updateOtherName(PublicId publicId, ViewProperties viewProperties) { + EntityProxy.Concept semanticType = TinkarTerm.REGULAR_NAME_DESCRIPTION_TYPE; + + updateSemanticName(publicId, viewProperties , semanticType); + LOG.info("transaction complete"); + } + public DescrName create() { return new DescrName(getValue(PARENT_PUBLIC_ID), getValue(NAME_TEXT), @@ -200,56 +215,5 @@ public void updateData(DescrName editDescrName) { editDescrName.setSemanticPublicId(getValue(SEMANTIC_PUBLIC_ID)); } - public void updateOtherName(PublicId publicId, ViewProperties viewProperties) { - Transaction transaction = Transaction.make(); - StampEntity stampEntity = transaction.getStamp( - State.fromConcept(getValue(STATUS)), // active, inactive, etc - System.currentTimeMillis(), - viewProperties.nodeView().editCoordinate().getAuthorForChanges().nid(), - ((ConceptEntity)getValue(MODULE)).nid(), // SNOMED CT, LOINC, etc - TinkarTerm.DEVELOPMENT_PATH.nid()); //TODO should this path come from the parent concept's path? - - // existing semantic - SemanticEntity theSemantic = EntityService.get().getEntityFast(publicId.asUuidList()); - - - // the versions that we will first populate with the existing versions of the semantic - RecordListBuilder versions = RecordListBuilder.make(); - - SemanticRecord descriptionSemantic = SemanticRecord.makeNew(publicId, TinkarTerm.DESCRIPTION_PATTERN.nid(), - theSemantic.referencedComponentNid(), versions); - - // we grabbing the form data - // populating the field values for the new version we are writing - MutableList descriptionFields = Lists.mutable.empty(); - descriptionFields.add(getValue(LANGUAGE)); - descriptionFields.add(getValue(NAME_TEXT)); - descriptionFields.add(getValue(CASE_SIGNIFICANCE)); - descriptionFields.add(TinkarTerm.REGULAR_NAME_DESCRIPTION_TYPE); - - // iterating over the existing versions and adding them to a new record list builder - theSemantic.versions().forEach(version -> versions.add(version)); - - // adding the new (edit form) version here - versions.add(SemanticVersionRecordBuilder.builder() - .chronology(descriptionSemantic) - .stampNid(stampEntity.nid()) - .fieldValues(descriptionFields.toImmutable()) - .build()); - // apply the updated versions to the new semantic record - SemanticRecord newSemanticRecord = SemanticRecordBuilder.builder(descriptionSemantic).versions(versions.toImmutable()).build(); - - // put the new semantic record in the transaction - transaction.addComponent(newSemanticRecord); - - // perform the save - Entity.provider().putEntity(newSemanticRecord); - - // commit the transaction - CommitTransactionTask commitTransactionTask = new CommitTransactionTask(transaction); - TinkExecutor.threadPool().submit(commitTransactionTask); - - LOG.info("transaction complete"); - } } diff --git a/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModelNext.java b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModelNext.java new file mode 100644 index 0000000000..600cc802c4 --- /dev/null +++ b/kview/src/main/java/dev/ikm/komet/kview/mvvm/viewmodel/DescrNameViewModelNext.java @@ -0,0 +1,250 @@ +package dev.ikm.komet.kview.mvvm.viewmodel; + +import dev.ikm.komet.framework.view.ViewProperties; +import dev.ikm.komet.kview.mvvm.model.DescrName; +import dev.ikm.tinkar.common.id.PublicId; +import dev.ikm.tinkar.common.service.PublicIdService; +import dev.ikm.tinkar.common.service.TinkExecutor; +import dev.ikm.tinkar.coordinate.stamp.calculator.Latest; +import dev.ikm.tinkar.coordinate.view.calculator.ViewCalculator; +import dev.ikm.tinkar.entity.*; +import dev.ikm.tinkar.entity.transaction.CommitTransactionTask; +import dev.ikm.tinkar.entity.transaction.Transaction; +import dev.ikm.tinkar.terms.*; +import org.carlfx.cognitive.viewmodel.ValidationViewModel; + +import dev.ikm.tinkar.terms.ComponentWithNid; +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.list.MutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Set; + +import static dev.ikm.komet.kview.mvvm.model.DataModelHelper.fetchDescendentsOfConcept; +import static dev.ikm.tinkar.terms.TinkarTerm.*; + + +public class DescrNameViewModelNext extends ValidationViewModel { + private static final Logger LOG = LoggerFactory.getLogger(DescrNameViewModelNext.class); + + public enum DescrPropKeys { + /// displayable Name + NAME, + + SELECTED_NAME_TYPE, + /// all possible associated name types + NAME_TYPE_VARIANTS, // ( FQN or otherName ? ) + + SELECTED_CASE_SIGNIFICANCE, + /// all possible associated case types + CASE_SIGNIFICANCE_VARIANTS, + + SELECTED_STATUS, + /// all possible associated statuses + STATUS_VARIANTS, + + SELECTED_MODULE, + /// all possible associated modules + MODULE_VARIANTS, + + SELECTED_LANGUAGE, + /// all possible associated languages + LANGUAGE_VARIANTS, + + // TODO: check if the following are needed in this model + /// the public ID of the description semantic that this class represents + SEMANTIC_PUBLIC_ID, + /// the public ID of the parent concept of this description semantic + PARENT_CONCEPT_ID, + + // TODO: this are old ones used before + // used to display the title of the add/edit screen? + TITLE_TEXT, + + VIEW_PROPERTIES, + } + + public DescrNameViewModelNext (ViewProperties viewProperties) { + + + addProperty(DescrPropKeys.NAME, (String) null) + .addProperty(DescrPropKeys.SELECTED_NAME_TYPE, (ComponentWithNid) null) + .addProperty(DescrPropKeys.NAME_TYPE_VARIANTS, Collections.emptyList(), true) + + .addProperty(DescrPropKeys.SELECTED_CASE_SIGNIFICANCE, (ComponentWithNid) null) + .addProperty(DescrPropKeys.CASE_SIGNIFICANCE_VARIANTS, Collections.emptyList(), true) + + .addProperty(DescrPropKeys.SELECTED_STATUS, (ComponentWithNid) null) + .addProperty(DescrPropKeys.STATUS_VARIANTS, Collections.emptyList(), true) + + .addProperty(DescrPropKeys.SELECTED_MODULE, (ComponentWithNid) null) + .addProperty(DescrPropKeys.MODULE_VARIANTS, Collections.emptyList(), true) + + .addProperty(DescrPropKeys.SELECTED_LANGUAGE, (ComponentWithNid) null) + .addProperty(DescrPropKeys.LANGUAGE_VARIANTS, Collections.emptyList(), true) + + .addProperty(DescrPropKeys.VIEW_PROPERTIES, (ViewProperties) viewProperties) + .addProperty(DescrPropKeys.TITLE_TEXT, (String) "hello world"); + + + + // run validators when the following properties change. + doOnChange(this::validate, + DescrPropKeys.NAME, + DescrPropKeys.SELECTED_NAME_TYPE, + DescrPropKeys.SELECTED_CASE_SIGNIFICANCE, + DescrPropKeys.SELECTED_STATUS, + DescrPropKeys.SELECTED_MODULE, + DescrPropKeys.SELECTED_LANGUAGE + ); + + + + } + + /// Copy View values into the Model + @Override + public DescrNameViewModelNext save() { + LOG.info("Copy: View State --> Model state"); + super.save(); + + return this; + } + + /// Copy Model values into the View + @Override + public DescrNameViewModelNext reset() { + LOG.info("Copy: View State <-- Model state"); + super.reset(); + + return this; + } + + + private void fetchSemanticFieldChoicesViaParentConcept(ViewProperties viewProperties) { + Set nameTypes = fetchDescendentsOfConcept(viewProperties, TinkarTerm.DESCRIPTION_TYPE.publicId()); + Set caseSignificances = fetchDescendentsOfConcept(viewProperties, DESCRIPTION_CASE_SIGNIFICANCE.publicId()); + Set states = fetchDescendentsOfConcept(viewProperties, TinkarTerm.STATUS_VALUE.publicId()); + Set languages = fetchDescendentsOfConcept(viewProperties, TinkarTerm.LANGUAGE.publicId()); + Set modules = fetchDescendentsOfConcept(viewProperties, TinkarTerm.MODULE.publicId()); + + setPropertyValues(DescrPropKeys.NAME_TYPE_VARIANTS, nameTypes); + setPropertyValues(DescrPropKeys.CASE_SIGNIFICANCE_VARIANTS, caseSignificances); + setPropertyValues(DescrPropKeys.STATUS_VARIANTS, states); + setPropertyValues(DescrPropKeys.LANGUAGE_VARIANTS, languages); + setPropertyValues(DescrPropKeys.MODULE_VARIANTS, modules); + } + + + /// Create a new semantic that points to a Concept derived from the provided ViewProperties + /// + /// Notice: should only be used on ViewProperties that actually include a "parent" Concept + public void createNewSemantic(ViewProperties viewProperties) { + fetchSemanticFieldChoicesViaParentConcept(viewProperties); + + setPropertyValue(DescrPropKeys.NAME, (String) null); + setPropertyValue(DescrPropKeys.SELECTED_NAME_TYPE, (ComponentWithNid) null); + setPropertyValue(DescrPropKeys.SELECTED_CASE_SIGNIFICANCE, (ComponentWithNid) null); + setPropertyValue(DescrPropKeys.SELECTED_STATUS, (ComponentWithNid) null); + setPropertyValue(DescrPropKeys.SELECTED_MODULE, (ComponentWithNid) null); + setPropertyValue(DescrPropKeys.SELECTED_LANGUAGE, (ComponentWithNid) null); + } + + /// Update a semantic + /// + /// Useful when creating a new Concept and the Concept was not commited + /// + /// @param descrName provides the semantic that wants to be updated + public void updateNonCommitedSemantic(DescrName descrName, ViewProperties viewProperties) { + fetchSemanticFieldChoicesViaParentConcept(viewProperties); + + setPropertyValue(DescrPropKeys.NAME, (String) descrName.getNameText()); + setPropertyValue(DescrPropKeys.SELECTED_NAME_TYPE, (ComponentWithNid) descrName.getNameType()); + setPropertyValue(DescrPropKeys.SELECTED_CASE_SIGNIFICANCE, (ComponentWithNid) descrName.getCaseSignificance()); + setPropertyValue(DescrPropKeys.SELECTED_STATUS, (ComponentWithNid) descrName.getStatus()); + setPropertyValue(DescrPropKeys.SELECTED_MODULE, (ComponentWithNid) descrName.getModule()); + setPropertyValue(DescrPropKeys.SELECTED_LANGUAGE, (ComponentWithNid) descrName.getLanguage()); + } + + + public void updateExistingSemantic(PublicId semanticNameId, ViewProperties viewProperties) { + int semanticNid = EntityService.get().nidForPublicId(semanticNameId); + Latest semanticEntityVersionLatest = viewProperties.calculator().latest(semanticNid); + SemanticEntityVersion bla = semanticEntityVersionLatest.get(); + LOG.info("SemanticEntityVersion we got on updatingExistingSemantic call"); + LOG.info(bla.toString()); + } + + + public void updateFullyQualifiedName(PublicId publicId, ViewProperties viewProperties) { + EntityProxy.Concept semanticType = TinkarTerm.FULLY_QUALIFIED_NAME_DESCRIPTION_TYPE; + + updateSemanticName(publicId, viewProperties , semanticType); + LOG.info("transaction complete"); + } + + public void updateOtherName(PublicId publicId, ViewProperties viewProperties) { + EntityProxy.Concept semanticType = TinkarTerm.REGULAR_NAME_DESCRIPTION_TYPE; + + updateSemanticName(publicId, viewProperties , semanticType); + LOG.info("transaction complete"); + } + + private void updateSemanticName(PublicId publicId, ViewProperties viewProperties, EntityProxy.Concept semanticType) { + Transaction transaction = Transaction.make(); + + StampEntity stampEntity = transaction.getStamp( + State.fromConcept(getValue(DescrPropKeys.SELECTED_STATUS)), // active, inactive, etc + System.currentTimeMillis(), + viewProperties.nodeView().editCoordinate().getAuthorForChanges().nid(), + ((ConceptEntity)getValue(DescrPropKeys.SELECTED_MODULE)).nid(), // SNOMED CT, LOINC, etc + TinkarTerm.DEVELOPMENT_PATH.nid()); //TODO should this path come from the parent concept's path? + + + // existing semantic + SemanticEntity theSemantic = EntityService.get().getEntityFast(publicId.asUuidList()); + + + // the versions that we will first populate with the existing versions of the semantic + RecordListBuilder versions = RecordListBuilder.make(); + + SemanticRecord descriptionSemantic = SemanticRecord.makeNew(publicId, TinkarTerm.DESCRIPTION_PATTERN.nid(), + theSemantic.referencedComponentNid(), versions); + + // we are grabbing the form data + // populating the field values for the new version we are writing + MutableList descriptionFields = Lists.mutable.empty(); + descriptionFields.add(getValue(DescrPropKeys.SELECTED_LANGUAGE)); + descriptionFields.add(getValue(DescrPropKeys.NAME)); + descriptionFields.add(getValue(DescrPropKeys.SELECTED_CASE_SIGNIFICANCE)); + descriptionFields.add(semanticType); + + // iterating over the existing versions and adding them to a new record list builder + theSemantic.versions().forEach(version -> versions.add(version)); + + // adding the new (edit form) version here + versions.add(SemanticVersionRecordBuilder.builder() + .chronology(descriptionSemantic) + .stampNid(stampEntity.nid()) + .fieldValues(descriptionFields.toImmutable()) + .build()); + + // apply the updated versions to the new semantic record + SemanticRecord newSemanticRecord = SemanticRecordBuilder.builder(descriptionSemantic).versions(versions.toImmutable()).build(); + + // put the new semantic record in the transaction + transaction.addComponent(newSemanticRecord); + + // perform the save + Entity.provider().putEntity(newSemanticRecord); + + // commit the transaction + CommitTransactionTask commitTransactionTask = new CommitTransactionTask(transaction); + TinkExecutor.threadPool().submit(commitTransactionTask); + } + + + +} diff --git a/kview/src/main/java/module-info.java b/kview/src/main/java/module-info.java index 1604ef4873..f7c9afee95 100644 --- a/kview/src/main/java/module-info.java +++ b/kview/src/main/java/module-info.java @@ -42,7 +42,9 @@ requires javafx.controls; requires org.slf4j; requires javafx.base; - + requires dev.ikm.tinkar.entity; + requires dev.ikm.tinkar.terms; + requires dev.ikm.tinkar.common; exports dev.ikm.komet.kview.state; exports dev.ikm.komet.kview.state.pattern; diff --git a/kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-details.fxml b/kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-details.fxml index 727dd53bbe..4bad7c03c2 100644 --- a/kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-details.fxml +++ b/kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-details.fxml @@ -41,7 +41,7 @@ - + diff --git a/kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-prop-menu.fxml b/kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-prop-menu.fxml new file mode 100644 index 0000000000..e4299c2e12 --- /dev/null +++ b/kview/src/main/resources/dev/ikm/komet/kview/mvvm/view/concept/concept-prop-menu.fxml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + +