/** * Copyright [2015] [Christian Loehnert] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.ks.blogging.grav.ui.post.edit; import de.ks.BaseController; import de.ks.blogging.grav.pages.GravPages; import de.ks.blogging.grav.posts.BasePost; import de.ks.gallery.ui.thumbnail.Thumbnail; import de.ks.gallery.ui.thumbnail.ThumbnailGallery; import de.ks.i18n.Localized; import de.ks.markdown.editor.MarkdownEditor; import de.ks.validation.validators.IntegerRangeValidator; import de.ks.validation.validators.NotEmptyValidator; import de.ks.validation.validators.TimeHHMMValidator; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.FXCollections; import javafx.fxml.FXML; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.layout.GridPane; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.stage.FileChooser; import org.controlsfx.validation.ValidationResult; import javax.inject.Inject; import java.io.File; import java.net.URL; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.Optional; import java.util.ResourceBundle; import java.util.function.Predicate; public class CreateEditPostController extends BaseController<BasePost> { @FXML protected GridPane root; @FXML protected DatePicker date; @FXML protected Button cancel; @FXML protected StackPane mediaContainer; @FXML protected ScrollPane headerContainer; @FXML protected Button post; @FXML protected Button selectFilePath; @FXML protected TextField filePath; @FXML protected TextField time; @FXML protected TextField title; @FXML protected ChoiceBox<PostType> type; @FXML protected StackPane contentContainer; @FXML protected TextField tags; @FXML protected TextField pageIndex; @FXML protected Accordion mediaPane; protected MarkdownEditor editor; protected TimeHHMMValidator validator; protected final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); protected final SimpleBooleanProperty knownContent = new SimpleBooleanProperty(false); @Inject GravPages gravPages; private ThumbnailGallery thumbnailGallery; @Override public void initialize(URL location, ResourceBundle resources) { Node view = activityInitialization.loadAdditionalController(AdvancedHeader.class).getView(); headerContainer.setContent(view); thumbnailGallery = activityInitialization.loadAdditionalController(ThumbnailGallery.class).getController(); mediaContainer.getChildren().add(thumbnailGallery.getRoot()); thumbnailGallery.setEnhancer(this::enhanceThumbnail); MarkdownEditor.load(pane -> contentContainer.getChildren().addAll(pane), ctrl -> editor = ctrl); editor.textProperty().bindBidirectional(store.getBinding().getStringProperty(BasePost.class, b -> b.getContent())); title.textProperty().bindBidirectional(store.getBinding().getStringProperty(BasePost.class, b -> b.getHeader().getTitle())); tags.textProperty().bindBidirectional(store.getBinding().getStringProperty(BasePost.class, b -> b.getHeader().getTagString())); validator = new TimeHHMMValidator(); validationRegistry.registerValidator(time, validator); validationRegistry.registerValidator(title, new NotEmptyValidator()); validationRegistry.registerValidator(pageIndex, new IntegerRangeValidator(0, Integer.MAX_VALUE)); validationRegistry.registerValidator(pageIndex, new NotEmptyValidator() { @Override public ValidationResult apply(Control control, String s) { if (type.getValue() == PostType.PAGE) { return super.apply(control, s); } else { return null; } } }); validationRegistry.registerValidator(filePath, new NotEmptyValidator() { @Override public ValidationResult apply(Control control, String s) { if (type.getValue() == PostType.UNKNOWN) { return super.apply(control, s); } else { return null; } } }); type.disableProperty().bind(knownContent); post.disableProperty().bind(validationRegistry.invalidProperty()); type.setItems(FXCollections.observableArrayList(PostType.values())); BooleanBinding isPage = type.getSelectionModel().selectedItemProperty().isEqualTo(PostType.PAGE); BooleanBinding isUnknown = type.getSelectionModel().selectedItemProperty().isEqualTo(PostType.UNKNOWN); filePath.disableProperty().bind(knownContent.or(isUnknown.not())); selectFilePath.disableProperty().bind(knownContent.or(isUnknown.not())); pageIndex.disableProperty().bind(knownContent.or(isPage.not())); mediaPane.expandedPaneProperty().addListener((p, o, n) -> { Predicate<Node> filter = c -> { boolean hasRowIndex = GridPane.getRowIndex(c) != null; boolean isUpperRow = hasRowIndex && GridPane.getRowIndex(c) < 4; return (hasRowIndex && isUpperRow) || !hasRowIndex;//first row is null in fx }; if (n == null) { root.getRowConstraints().get(0).setPrefHeight(Region.USE_COMPUTED_SIZE); root.getRowConstraints().get(1).setPrefHeight(Region.USE_COMPUTED_SIZE); root.getRowConstraints().get(2).setPrefHeight(Region.USE_COMPUTED_SIZE); root.getChildren().stream().filter(filter).forEach(c -> c.setVisible(true)); } else { root.getRowConstraints().get(0).setPrefHeight(0); root.getRowConstraints().get(1).setPrefHeight(0); root.getRowConstraints().get(2).setPrefHeight(0); root.getChildren().stream().filter(filter).forEach(c -> c.setVisible(false)); } }); } private Node enhanceThumbnail(Control node, Thumbnail thumbnail) { StackPane box = new StackPane(); box.setPrefSize(node.getWidth(), node.getHeight()); box.maxWidthProperty().bind(node.widthProperty()); box.maxHeightProperty().bind(node.heightProperty()); box.getChildren().add(node); String name = thumbnail.getItem().getName(); Button insertAtCursor = new Button(Localized.get("grav.post.insertAtCursor", name)); insertAtCursor.setOnAction(e -> { TextArea textArea = this.editor.getEditor(); int pos = textArea.getCaretPosition(); String text = "![" + name + "](" + name + ")"; if (pos > 0) { textArea.insertText(pos, text); } else { textArea.appendText(text); } }); box.getChildren().add(insertAtCursor); StackPane.setAlignment(insertAtCursor, Pos.TOP_RIGHT); return box; } @FXML public void onPost() { Optional<String> result = Optional.empty(); if (gravPages.hasGitRepository()) { TextInputDialog dialog = new TextInputDialog(); dialog.setTitle(Localized.get("grav.post.enter.commitMsg")); dialog.setHeaderText(Localized.get("grav.post.enter.commitMsg")); dialog.setContentText(Localized.get("grav.post.git.commitMsg:")); result = dialog.showAndWait(); } if (result.isPresent()) { store.getDatasource().setLoadingHint(result.get()); } controller.save(); controller.stopCurrent(); } @FXML public void onCancel() { store.getDatasource().setLoadingHint(null); controller.stopCurrent(); } @Override public void duringSave(BasePost model) { LocalDate localDate = this.date.getValue(); LocalTime localTime = validator.getTime(); model.getHeader().setLocalDateTime(LocalDateTime.of(localDate, localTime)); if (model instanceof UIPostWrapper) { UIPostWrapper wrapper = (UIPostWrapper) model; wrapper.setFilePath(filePath.getText()); wrapper.setPostType(type.getValue()); if (type.getValue() == PostType.PAGE) { wrapper.setPageIndex(Integer.parseInt(pageIndex.textProperty().getValueSafe().trim())); } } } @Override protected void onRefresh(BasePost model) { Optional<LocalDate> dateOptional = model.getHeader().getLocalDate(); dateOptional.ifPresent(d -> this.date.setValue(d)); Optional<LocalDateTime> localDateTime = model.getHeader().getLocalDateTime(); localDateTime.ifPresent(ldt -> time.setText(formatter.format(ldt.toLocalTime()))); File file = model.getFile(); if (file != null) { File parentFile = file.getParentFile(); editor.setFile(file); thumbnailGallery.setFolder(parentFile, true); } if (model instanceof UIPostWrapper) { UIPostWrapper wrapper = (UIPostWrapper) model; filePath.setText(wrapper.getFilePath()); type.setValue(PostType.BLOGITEM); knownContent.set(false); } else { knownContent.set(true); } } @FXML public void onFilePathSelection() { FileChooser chooser = new FileChooser(); chooser.setInitialDirectory(new File(gravPages.getBlog().getPagesDirectory())); chooser.setTitle(Localized.get("choose.dir")); File file = chooser.showSaveDialog(contentContainer.getScene().getWindow()); if (file != null) { filePath.setText(file.getAbsolutePath()); } } }