diff --git a/.gitignore b/.gitignore index d3c9b53..013be40 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,5 @@ dependency-reduced-pom.xml /BugReporter/src/META-INF/ BugReporter/src/META-INF/ -Everest/ +Everest/ out/ \ No newline at end of file diff --git a/src/main/java/com/rohitawate/everest/controllers/AbstractSearchablePaneController.java b/src/main/java/com/rohitawate/everest/controllers/AbstractSearchablePaneController.java new file mode 100644 index 0000000..b015970 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/controllers/AbstractSearchablePaneController.java @@ -0,0 +1,186 @@ +package com.rohitawate.everest.controllers; + +import java.io.IOException; +import java.net.URL; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.ResourceBundle; +import java.util.concurrent.ExecutionException; + +import com.jfoenix.controls.JFXButton; +import com.rohitawate.everest.misc.Services; + +import javafx.application.Platform; +import javafx.concurrent.Task; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.control.SplitPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; + +public abstract class AbstractSearchablePaneController implements Initializable { + + @FXML + private StackPane searchPromptLayer, searchLayer, searchFailedLayer; + + @FXML + private JFXButton clearSearchFieldButton; + + @FXML + private TextField searchTextField; + + @FXML + private VBox searchTab, searchBox, searchPane; + + private List> searchableItems; + + + protected static class SearchEntry { + private final Parent fxmlItem; + private final Searchable searchable; + public SearchEntry(Parent fxmlItem, Searchable searchable) { + super(); + this.fxmlItem = fxmlItem; + this.searchable = searchable; + } + public Parent getFxmlItem() { + return fxmlItem; + } + public Searchable getSearchable() { + return searchable; + } + + } + + @Override + public void initialize(URL arg0, ResourceBundle arg1) { + searchableItems = new ArrayList<>(); + searchLayer.visibleProperty().bind(searchTextField.textProperty().isNotEmpty()); + + searchTextField.textProperty().addListener(((observable, oldValue, newValue) -> { + searchBox.getChildren().remove(0, searchBox.getChildren().size()); + searchFailedLayer.setVisible(false); + List> searchResults = getSearchResults(searchTextField.getText()); + + //TODO: this is calculating relativityIndex again + searchResults.sort((controller1, controller2) -> { + int relativity1 = controller1.getRelativityIndex(searchTextField.getText()); + int relativity2 = controller2.getRelativityIndex(searchTextField.getText()); + return relativity2 - relativity1; + }); + + if (searchResults.size() != 0) { + for (Searchable controller : searchResults) { + addSearchItem(controller.getState()); + } + } else { + searchFailedLayer.setVisible(true); + } + })); + + clearSearchFieldButton.setOnAction(e -> searchTextField.clear()); + + Platform.runLater(this::loadInitialItemsAsync); + } + + + private void loadInitialItemsAsync() { + Task> entryLoader = new Task>() { + @Override + protected List call() { + return loadInitialEntries(); + } + }; + + entryLoader.setOnSucceeded(e -> { + try { + List entries = entryLoader.get(); + if (entries.size() == 0) { + searchPromptLayer.setVisible(true); + return; + } + + for (T state : entries) + addHistoryItem(state); + } catch (InterruptedException | ExecutionException E) { + Services.loggingService.logSevere("Task thread interrupted while populating HistoryTab.", E, + LocalDateTime.now()); + } + }); + entryLoader.setOnFailed(e -> Services.loggingService.logWarning("Failed to load history.", + (Exception) entryLoader.getException(), LocalDateTime.now())); + new Thread(entryLoader).start(); + + } + + private void addSearchItem(T state) { + appendToList(state, searchBox, false); + } + + protected abstract List loadInitialEntries(); + + public void focusSearchField() { + searchTextField.requestFocus(); + } + + private Searchable appendToList(T state, VBox layer, boolean appendToStart) { + searchPromptLayer.setVisible(false); + try { + SearchEntry searchEntry = createEntryFromState(state); + + if (appendToStart) + layer.getChildren().add(0, searchEntry.getFxmlItem()); + else + layer.getChildren().add(searchEntry.getFxmlItem()); + + return searchEntry.getSearchable(); + } catch (IOException e) { + Services.loggingService.logSevere("Could not append HistoryItem to list.", e, LocalDateTime.now()); + } + + return null; + } + + + protected abstract SearchEntry createEntryFromState(T state) throws IOException; + + + public void addHistoryItem(T state) { + Searchable controller = appendToList(state, searchTab, true); + searchableItems.add(controller); + } + + private List> getSearchResults(String searchString) { + List> filteredList = new ArrayList<>(); + + for (Searchable controller : searchableItems) { + + int relativityIndex = controller.getRelativityIndex(searchString); + + // Split the string into words and get total relativity index as sum of + // individual indices. + String words[] = searchString.split("\\s"); + for (String word : words) + relativityIndex += controller.getRelativityIndex(word); + + if (relativityIndex != 0) + filteredList.add(controller); + } + + return filteredList; + } + + + public void toggleVisibilityIn(SplitPane splitPane) { + if (searchPane.isVisible()) { + searchPane = (VBox) splitPane.getItems().remove(0); + } else { + splitPane.getItems().add(0, searchPane); + } + + searchPane.setVisible(!searchPane.isVisible()); + } +} diff --git a/src/main/java/com/rohitawate/everest/controllers/HistoryItemController.java b/src/main/java/com/rohitawate/everest/controllers/HistoryItemController.java index 0d264d9..95eee8d 100644 --- a/src/main/java/com/rohitawate/everest/controllers/HistoryItemController.java +++ b/src/main/java/com/rohitawate/everest/controllers/HistoryItemController.java @@ -16,21 +16,23 @@ package com.rohitawate.everest.controllers; -import com.rohitawate.everest.controllers.state.DashboardState; -import com.rohitawate.everest.controllers.state.FieldState; -import com.rohitawate.everest.misc.Services; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; - -import javax.ws.rs.core.MediaType; import java.net.MalformedURLException; import java.net.URL; import java.time.LocalDateTime; import java.util.ResourceBundle; -public class HistoryItemController implements Initializable { +import javax.ws.rs.core.MediaType; + +import com.rohitawate.everest.controllers.state.DashboardState; +import com.rohitawate.everest.controllers.state.FieldState; +import com.rohitawate.everest.misc.Services; + +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; + +public class HistoryItemController implements Initializable, Searchable { @FXML private Label requestType, address; @FXML diff --git a/src/main/java/com/rohitawate/everest/controllers/HomeWindowController.java b/src/main/java/com/rohitawate/everest/controllers/HomeWindowController.java index 5f787a7..8b394ad 100644 --- a/src/main/java/com/rohitawate/everest/controllers/HomeWindowController.java +++ b/src/main/java/com/rohitawate/everest/controllers/HomeWindowController.java @@ -16,17 +16,27 @@ package com.rohitawate.everest.controllers; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.ResourceBundle; + import com.fasterxml.jackson.core.type.TypeReference; -import com.jfoenix.controls.JFXButton; import com.rohitawate.everest.controllers.state.DashboardState; import com.rohitawate.everest.misc.EverestUtilities; import com.rohitawate.everest.misc.KeyMap; import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.ThemeManager; + import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.StringProperty; -import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; @@ -35,19 +45,11 @@ import javafx.scene.Scene; import javafx.scene.control.SplitPane; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; -import javafx.scene.control.TextField; import javafx.scene.input.MouseButton; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.time.LocalDateTime; -import java.util.*; -import java.util.concurrent.ExecutionException; - public class HomeWindowController implements Initializable { @FXML private StackPane homeWindowSP; @@ -55,55 +57,19 @@ public class HomeWindowController implements Initializable { private SplitPane splitPane; @FXML private TabPane homeWindowTabPane; + @FXML - private TextField historyTextField; - @FXML - private VBox historyTab, searchBox, historyPane; - @FXML - private StackPane historyPromptLayer, searchLayer, searchFailedLayer; - @FXML - private JFXButton clearSearchFieldButton; + private SearchPaneController searchPaneController; private HashMap tabControllerMap; - private List historyItemControllers; @Override public void initialize(URL location, ResourceBundle resources) { // Using LinkedHashMap because they retain order tabControllerMap = new LinkedHashMap<>(); - historyItemControllers = new ArrayList<>(); recoverState(); - searchLayer.visibleProperty().bind(historyTextField.textProperty().isNotEmpty()); - - historyTextField.textProperty().addListener(((observable, oldValue, newValue) -> { - searchBox.getChildren().remove(0, searchBox.getChildren().size()); - searchFailedLayer.setVisible(false); - List searchResults = getSearchResults(historyTextField.getText()); - - // Method of sorting the HistoryItemControllers - searchResults.sort((controller1, controller2) -> { - int relativity1 = controller1.getRelativityIndex(historyTextField.getText()); - int relativity2 = controller2.getRelativityIndex(historyTextField.getText()); - if (relativity1 < relativity2) - return 1; - else if (relativity1 > relativity2) - return -1; - else - return 0; - }); - - if (searchResults.size() != 0) { - for (HistoryItemController controller : searchResults) { - addSearchItem(controller.getState()); - } - } else { - searchFailedLayer.setVisible(true); - } - })); - - clearSearchFieldButton.setOnAction(e -> historyTextField.clear()); - + searchPaneController.addItemClickHandler(this::addTab); homeWindowSP.setFocusTraversable(true); Platform.runLater(() -> { @@ -114,32 +80,8 @@ public class HomeWindowController implements Initializable { Stage thisStage = (Stage) homeWindowSP.getScene().getWindow(); thisStage.setOnCloseRequest(e -> saveState()); - // Loads the history - Task> historyLoader = new Task>() { - @Override - protected List call() { - return Services.historyManager.getHistory(); - } - }; + - // Appends the history items to the HistoryTab - historyLoader.setOnSucceeded(e -> { - try { - List history = historyLoader.get(); - if (history.size() == 0) { - historyPromptLayer.setVisible(true); - return; - } - - for (DashboardState state : history) - addHistoryItem(state); - } catch (InterruptedException | ExecutionException E) { - Services.loggingService.logSevere("Task thread interrupted while populating HistoryTab.", E, LocalDateTime.now()); - } - }); - historyLoader.setOnFailed(e -> Services.loggingService.logWarning( - "Failed to load history.", (Exception) historyLoader.getException(), LocalDateTime.now())); - new Thread(historyLoader).start(); }); } @@ -167,7 +109,7 @@ public class HomeWindowController implements Initializable { homeWindowTabPane.getTabs().remove(activeTab); tabControllerMap.remove(activeTab); } else if (KeyMap.searchHistory.match(e)) { - historyTextField.requestFocus(); + searchPaneController.focusSearchField(); } else if (KeyMap.focusParams.match(e)) { Tab activeTab = getActiveTab(); DashboardController controller = tabControllerMap.get(activeTab); @@ -198,13 +140,7 @@ public class HomeWindowController implements Initializable { } private void toggleHistoryPane() { - if (historyPane.isVisible()) { - historyPane = (VBox) splitPane.getItems().remove(0); - } else { - splitPane.getItems().add(0, historyPane); - } - - historyPane.setVisible(!historyPane.isVisible()); + searchPaneController.toggleVisibilityIn(splitPane); } private void addTab() { @@ -291,57 +227,9 @@ public class HomeWindowController implements Initializable { } - public void addHistoryItem(DashboardState state) { - HistoryItemController controller = appendToList(state, historyTab, true); - historyItemControllers.add(controller); - } + public void addHistoryItem(DashboardState state) { + searchPaneController.addHistoryItem(state); + } - private void addSearchItem(DashboardState state) { - appendToList(state, searchBox, false); - } - - private HistoryItemController appendToList(DashboardState state, VBox layer, boolean appendToStart) { - historyPromptLayer.setVisible(false); - HistoryItemController controller = null; - try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/homewindow/HistoryItem.fxml")); - Parent historyItem = loader.load(); - - controller = loader.getController(); - controller.setState(state); - - if (appendToStart) - layer.getChildren().add(0, historyItem); - else - layer.getChildren().add(historyItem); - - // Clicking on HistoryItem opens it up in a new tab - historyItem.setOnMouseClicked(mouseEvent -> { - if (mouseEvent.getButton() == MouseButton.PRIMARY) - addTab(state); - }); - } catch (IOException e) { - Services.loggingService.logSevere("Could not append HistoryItem to list.", e, LocalDateTime.now()); - } - return controller; - } - - private List getSearchResults(String searchString) { - List filteredList = new ArrayList<>(); - - for (HistoryItemController controller : historyItemControllers) { - - int relativityIndex = controller.getRelativityIndex(searchString); - - // Split the string into words and get total relativity index as the sum of individual indices. - String words[] = searchString.split("\\s"); - for (String word : words) - relativityIndex += controller.getRelativityIndex(word); - - if (relativityIndex != 0) - filteredList.add(controller); - } - - return filteredList; - } + } diff --git a/src/main/java/com/rohitawate/everest/controllers/SearchPaneController.java b/src/main/java/com/rohitawate/everest/controllers/SearchPaneController.java new file mode 100644 index 0000000..d106925 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/controllers/SearchPaneController.java @@ -0,0 +1,53 @@ +package com.rohitawate.everest.controllers; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; + +import com.rohitawate.everest.controllers.state.DashboardState; +import com.rohitawate.everest.misc.Services; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.input.MouseButton; +import javafx.scene.layout.VBox; + +public class SearchPaneController extends AbstractSearchablePaneController { + + private List> stateClickHandler = new LinkedList<>(); + + @Override + protected List loadInitialEntries() { + return Services.historyManager.getHistory(); + } + + protected SearchEntry createEntryFromState(DashboardState state) throws IOException { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/homewindow/HistoryItem.fxml")); + Parent historyItem = loader.load(); + + HistoryItemController controller = loader.getController(); + + controller = loader.getController(); + controller.setState(state); + + // Clicking on HistoryItem opens it up in a new tab + historyItem.setOnMouseClicked(mouseEvent -> { + if (mouseEvent.getButton() == MouseButton.PRIMARY) + handleClick(state); + }); + return new SearchEntry(historyItem, controller); + } + + private void handleClick(DashboardState state) { + for (Consumer consumer : stateClickHandler) { + consumer.accept(state); + } + } + + public void addItemClickHandler(Consumer handler) { + stateClickHandler.add(handler); + } + + +} diff --git a/src/main/java/com/rohitawate/everest/controllers/Searchable.java b/src/main/java/com/rohitawate/everest/controllers/Searchable.java new file mode 100644 index 0000000..2fa7186 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/controllers/Searchable.java @@ -0,0 +1,15 @@ +package com.rohitawate.everest.controllers; + +/** + * a searchable item that is used in a search-pane. + * @author pmucha + * + * @param + */ +public interface Searchable { + + int getRelativityIndex(String searchString); + + T getState(); + +} \ No newline at end of file diff --git a/src/main/java/com/rohitawate/everest/history/HistoryManager.java b/src/main/java/com/rohitawate/everest/history/HistoryManager.java index 849ae41..eafe3ec 100644 --- a/src/main/java/com/rohitawate/everest/history/HistoryManager.java +++ b/src/main/java/com/rohitawate/everest/history/HistoryManager.java @@ -16,6 +16,21 @@ package com.rohitawate.everest.history; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.core.MediaType; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.rohitawate.everest.controllers.state.DashboardState; @@ -24,16 +39,6 @@ import com.rohitawate.everest.misc.EverestUtilities; import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.settings.Settings; -import javax.ws.rs.core.MediaType; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.sql.*; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - public class HistoryManager { private Connection conn; private JsonNode queries; @@ -468,4 +473,4 @@ public class HistoryManager { } } } -} +} \ No newline at end of file diff --git a/src/main/resources/css/Adreana.css b/src/main/resources/css/Adreana.css index 4fd703f..89e2ab2 100644 --- a/src/main/resources/css/Adreana.css +++ b/src/main/resources/css/Adreana.css @@ -264,7 +264,7 @@ -fx-background-color: #303030; } -#historyTextField { +.searchTextField { -fx-background-color: transparent; -fx-text-fill: white; } diff --git a/src/main/resources/fxml/homewindow/HomeWindow.fxml b/src/main/resources/fxml/homewindow/HomeWindow.fxml index 63b6580..2812b97 100644 --- a/src/main/resources/fxml/homewindow/HomeWindow.fxml +++ b/src/main/resources/fxml/homewindow/HomeWindow.fxml @@ -1,20 +1,13 @@ - + @@ -22,131 +15,20 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/src/main/resources/fxml/homewindow/SearchPane.fxml b/src/main/resources/fxml/homewindow/SearchPane.fxml new file mode 100644 index 0000000..0589e01 --- /dev/null +++ b/src/main/resources/fxml/homewindow/SearchPane.fxml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file