package mj.ocraptor.javafx.controllers;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import mj.ocraptor.MainController;
import mj.ocraptor.configuration.Config;
import mj.ocraptor.database.error.TableEmptyException;
import mj.ocraptor.database.search.LuceneResult;
import mj.ocraptor.javafx.GUITemplate;
import mj.ocraptor.javafx.Icon;
public class SearchDialog extends GUITemplate {
private final Logger LOG = LoggerFactory.getLogger(getClass());
// *INDENT-OFF*
public static double INIT_WIDTH = 550;
public static double INIT_HEIGHT = 235;
public static final String FXML = "SearchDialog.fxml";
// *INDENT-ON*
// ------------------------------------------------ //
// --
// ------------------------------------------------ //
@FXML
private Button cancelButton;
@FXML
private Button searchButton;
@FXML
private TextArea searchField;
@FXML
private AnchorPane fullTextPane;
@FXML
private HBox fullTextHbox;
@FXML
private Label searchLabel;
@FXML
void cancelButtonClicked(ActionEvent event) {
this.setProgress(-1, "");
this.gotoPage(EditDatabase.FXML, EditDatabase.INIT_WIDTH, EditDatabase.INIT_HEIGHT);
}
@FXML
void searchButtonClicked(ActionEvent event) {
this.searchDatabase();
}
/**
*
*
*/
private void searchDatabase() {
try {
if (!this.searchButton.isDisabled()) {
boolean dbIncomplete = false;
// ------------------------------------------------ //
try {
this.g.getParentController().getDb().databaseIncomplete();
} catch (Exception e) {
dbIncomplete = true;
}
// ------------------------------------------------ //
if (!dbIncomplete) {
this.setProgress(-1, g.getText("SEARCH.SEARCHING"));
this.outTransition();
this.executeWorker(searchWorker());
}
// ------------------------------------------------ //
}
} catch (Exception e) {
LOG.error("Search failed", e);
}
}
/**
*
*
* @param exception
*/
private void luceneExceptionThrown(final Throwable exception) {
// TODO: translation of most common lucene exceptions
final List<Node> textNodes = new ArrayList<Node>();
textNodes.add(new Text(g.getText("ERROR.LUCENE_ERROR")));
textNodes.add(new Text("\n\n"));
final Text rootCause = new Text(ExceptionUtils.getRootCauseMessage(exception));
rootCause.setFill(Color.DARKRED);
textNodes.add(rootCause);
Platform.runLater(new Runnable() {
@Override
public void run() {
g.showMessage(450d, 200d, 20d, g.getText("ERROR.LUCENE_TITLE"), Color.DARKRED, true,
textNodes.toArray(new Node[textNodes.size()]));
}
});
}
/**
*
*
* @return
*/
private Task<Object> searchWorker() {
return new Task<Object>() {
@Override
protected Object call() throws Exception {
String searchString = searchField.getText().trim();
MainController.inst().searchDatabase(searchString);
Platform.runLater(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName(Config.APP_NAME + "JavaFX: User search");
g.setLastContentSearch(searchField.getText().trim());
final LuceneResult result = g.getSearchResult();
final Throwable throwable = result.getThrowable();
if (throwable != null) {
luceneExceptionThrown(throwable);
inTransitionAsWorker(100);
} else if (result.getFileEntries().isEmpty()) {
g.showConfirmationDialog(g.getText("SEARCH.NO_RESULTS"));
inTransitionAsWorker(100);
focusFullTextField();
} else {
// show result page to user
gotoPage(SearchResult.FXML, SearchResult.INIT_WIDTH, SearchResult.INIT_HEIGHT);
}
}
});
return true;
}
};
}
// ------------------------------------------------ //
// --
// ------------------------------------------------ //
@Override
protected void initVisibility() {
this.helpButton.setDisable(false);
this.searchButton.setDisable(true);
this.addImageIcon(this.searchButton, Icon.SEARCH, 0);
}
@Override
protected void initLabels() {
this.searchField.setText("");
this.title.setText(g.getText("SEARCH.TITLE"));
this.cancelButton.setText(g.getText("BACK_BUTTON"));
this.searchButton.setText(g.getText("SEARCH_BUTTON"));
this.searchLabel.setText(g.getText("SEARCH.FULLTEXT"));
}
@Override
public void initCustomComponents() {
ChangeListener<Object> listener = new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<? extends Object> arg0, Object oldPropertyValue,
Object newPropertyValue) {
checkIfInputIsValid();
}
};
this.searchField.textProperty().addListener(listener);
String lastContentSearch = this.g.getLastContentSearch();
this.searchField.setPromptText(g.getText("SEARCH.FULLTEXT_PROMPT"));
if (lastContentSearch != null) {
this.searchField.setText(lastContentSearch);
}
if (!this.searchField.getText().trim().isEmpty()) {
this.focusFullTextField();
}
this.checkIfInputIsValid();
}
/**
*
*
*/
private void removeText() {
if (this.searchField.isFocused()) {
this.searchField.setText("");
}
}
/**
*
*
*/
private void focusFullTextField() {
Platform.runLater(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName(Config.APP_NAME + "JavaFX: focus search textfield");
searchField.requestFocus();
searchField.positionCaret(searchField.getText().length());
}
});
}
@Override
protected void initListeners() {
//
}
@Override
protected void asserts() {
// TODO: javafx asserts
}
@Override
protected double getWindowWidth() {
return INIT_WIDTH;
}
@Override
protected double getWindowHeight() {
return INIT_HEIGHT;
}
// ------------------------------------------------ //
// --
// ------------------------------------------------ //
/**
*
*
*/
private void checkIfInputIsValid() {
this.searchField.setText(this.searchField.getText().replace("\t", ""));
final boolean fullTextEmpty = this.searchField.getText().trim().isEmpty();
if (!fullTextEmpty) {
this.searchButton.setDisable(false);
this.addImageIcon(this.searchButton, Icon.SEARCH, 0);
} else {
this.searchButton.setDisable(true);
this.addImageIcon(this.searchButton, Icon.SEARCH, 0);
}
}
// ------------------------------------------------ //
// --
// ------------------------------------------------ //
/**
*
*
* @param text
*/
private void addText(String text, Integer pos) {
if (this.searchField.isFocused()) {
int position = this.searchField.getCaretPosition();
if (pos != null) {
position = pos;
}
if (position < 0) {
position = this.searchField.getText().length();
}
final String currentText = this.searchField.getText().trim();
final String leftText = //
StringUtils.substring(currentText, 0, position).trim();
final String rightText = //
StringUtils.substring(currentText, position, currentText.length()).trim();
this.searchField.setText((leftText + text + rightText).trim() + " ");
this.searchField.positionCaret(leftText.length() + text.length());
}
}
@Override
protected void initEventHandlers() {
this.pane.getScene().addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.ESCAPE) {
if (searchField.isFocused()) {
pane.requestFocus();
} else {
cancelButtonClicked(null);
}
} else if (event.getCode() == KeyCode.TAB) {
focusFullTextField();
} else if (event.isControlDown() && event.getCode() == KeyCode.ENTER) {
searchDatabase();
} else if (event.isControlDown() && event.getCode() == KeyCode.D) {
removeText();
} else if (event.isControlDown() && event.getCode() == KeyCode.J) {
addText(" AND ", null);
} else if (event.isControlDown() && event.getCode() == KeyCode.K) {
addText(" OR ", null);
} else if (event.isControlDown() && event.getCode() == KeyCode.L) {
addText(" AND NOT ", null);
}
}
});
}
}