/**
* 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.manage;
import de.ks.BaseController;
import de.ks.activity.ActivityHint;
import de.ks.blogging.grav.entity.GravBlog;
import de.ks.blogging.grav.posts.BasePost;
import de.ks.blogging.grav.ui.post.edit.CreateEditPostActivity;
import de.ks.executor.group.LastTextChange;
import de.ks.i18n.Localized;
import de.ks.javafx.event.ClearTextOnEscape;
import de.ks.markdown.viewer.MarkdownContent;
import de.ks.markdown.viewer.MarkdownViewer;
import de.ks.persistence.PersistentWork;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.util.Callback;
import javafx.util.StringConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class ManagePostsController extends BaseController<List<BasePost>> {
private static final Logger log = LoggerFactory.getLogger(ManagePostsController.class);
@FXML
protected ChoiceBox<GravBlog> blogSelection;
@FXML
protected TableView<BasePost> postTable;
@FXML
protected TableColumn<BasePost, String> titleColumn;
@FXML
protected TableColumn<BasePost, LocalDateTime> dateColumn;
@FXML
protected Button edit;
@FXML
protected Button create;
@FXML
protected Button delete;
@FXML
protected Button showFolder;
@FXML
protected StackPane previewContainer;
@FXML
protected TextField titleSearch;
protected final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Localized.get("fullDate"));
protected final java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
protected LastTextChange lastTextChange;
protected ObservableList<BasePost> items;
protected MarkdownViewer markdownViewer;
@Override
public void initialize(URL location, ResourceBundle resources) {
MarkdownViewer.load(node -> previewContainer.getChildren().add(node), ctrl -> markdownViewer = ctrl);
titleColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getHeader().getTitle()));
dateColumn.setCellValueFactory(param -> new SimpleObjectProperty<LocalDateTime>(param.getValue().getHeader().getLocalDateTime().orElse(LocalDateTime.of(1970, 1, 1, 12, 42, 0))));
dateColumn.setCellFactory(new Callback<TableColumn<BasePost, LocalDateTime>, TableCell<BasePost, LocalDateTime>>() {
@Override
public TableCell<BasePost, LocalDateTime> call(TableColumn<BasePost, LocalDateTime> param) {
TableCell<BasePost, LocalDateTime> cell = new TableCell<BasePost, LocalDateTime>() {
@Override
protected void updateItem(LocalDateTime item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(formatter.format(item));
} else {
setText("");
}
}
};
return cell;
}
});
titleSearch.setOnKeyReleased(new ClearTextOnEscape());
lastTextChange = new LastTextChange(titleSearch, controller.getExecutorService());
lastTextChange.registerHandler(cf -> cf.thenRunAsync(() -> applyItems(), controller.getJavaFXExecutor()));
postTable.getSortOrder().add(dateColumn);
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
dateColumn.setSortable(true);
postTable.sort();
BooleanBinding nothingSelected = postTable.getSelectionModel().selectedItemProperty().isNull();
edit.disableProperty().bind(nothingSelected);
showFolder.disableProperty().bind(nothingSelected);
// delete.disableProperty().bind(nothingSelected);
delete.setDisable(true);
postTable.getSelectionModel().selectedItemProperty().addListener((p, o, n) -> {
if (n == null) {
markdownViewer.reset();
} else {
markdownViewer.show(createMarkdownContent(n));
}
});
blogSelection.getSelectionModel().selectedItemProperty().addListener((p, o, n) -> {
store.getDatasource().setLoadingHint(n);
controller.reload();
});
blogSelection.setConverter(new StringConverter<GravBlog>() {
@Override
public String toString(GravBlog object) {
return object.getName();
}
@Override
public GravBlog fromString(String string) {
return blogSelection.getItems().stream().filter(i -> i.getName().equals(string)).findFirst().get();
}
});
postTable.setOnMouseClicked(e -> {
if (e.getClickCount() > 1) {
if (!edit.isDisabled()) {
onEdit();
}
}
});
}
@FXML
public void onCreate() {
ActivityHint hint = new ActivityHint(CreateEditPostActivity.class);
hint.returnToCurrent();
hint.setDataSourceHint(() -> null);
controller.startOrResume(hint);
}
@FXML
public void onEdit() {
ActivityHint hint = new ActivityHint(CreateEditPostActivity.class);
hint.returnToCurrent();
Supplier supplier = () -> postTable.getSelectionModel().getSelectedItem();
hint.setReturnToDatasourceHint(supplier);
hint.setDataSourceHint(supplier);
controller.startOrResume(hint);
}
@FXML
public void onShowFolder() {
BasePost post = postTable.getSelectionModel().getSelectedItem();
Thread thread = new Thread(() -> {
try {
desktop.open(post.getFile().getParentFile());
} catch (Exception e) {
log.error("Could not open {}", post.getFile(), e);
}
});
thread.setDaemon(true);
thread.start();
}
@FXML
public void onDelete() {
}
@Override
public void onStart() {
onResume();
}
@Override
public void onResume() {
List<GravBlog> blogs = PersistentWork.from(GravBlog.class);
controller.getJavaFXExecutor().submit(() -> {
SingleSelectionModel<GravBlog> selectionModel = blogSelection.getSelectionModel();
GravBlog selectedItem = selectionModel.getSelectedItem();
blogSelection.setItems(FXCollections.observableList(blogs));
if (blogs.size() > 0) {
if (selectedItem != null) {
selectionModel.select(selectedItem);
} else {
selectionModel.select(0);
}
}
});
}
@Override
protected void onRefresh(List<BasePost> model) {
markdownViewer.preload(model.stream().map(this::createMarkdownContent).collect(Collectors.toList()));
controller.getJavaFXExecutor().submit(() -> {
items = FXCollections.observableArrayList(model);
applyItems();
});
}
protected MarkdownContent createMarkdownContent(BasePost m) {
if (m.getFile() != null) {
return new MarkdownContent(m.getFile().getAbsolutePath(), m.getFile());
} else {
return new MarkdownContent(m.getHeader().getTitle(), m.getContent());
}
}
protected void applyItems() {
ObservableList<TableColumn<BasePost, ?>> oldOrder = FXCollections.observableArrayList(postTable.getSortOrder());
postTable.getSortOrder().clear();
TableColumn.SortType dateSortType = dateColumn.getSortType();
TableColumn.SortType titleSortType = titleColumn.getSortType();
List<BasePost> filtered = items.stream().filter(basePost -> basePost.getHeader().getTitle().toLowerCase(Locale.ROOT).contains(titleSearch.getText().toLowerCase(Locale.ROOT))).collect(Collectors.toList());
postTable.setItems(FXCollections.observableList(filtered));
postTable.getSortOrder().addAll(oldOrder);
dateColumn.setSortType(dateSortType);
titleColumn.setSortType(titleSortType);
dateColumn.setSortable(true);
titleColumn.setSortable(true);
postTable.sort();
}
}