/** * FreeDesktopSearch - A Search Engine for your Desktop * Copyright (C) 2013 Mirko Sertic * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if not, see * <http://www.gnu.org/licenses/>. */ package de.mirkosertic.desktopsearch; import javafx.application.Platform; import javafx.concurrent.Worker.State; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Scene; import javafx.scene.control.MenuItem; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.scene.web.WebView; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.stage.Window; import netscape.javascript.JSObject; import org.apache.log4j.Logger; import java.io.IOException; import java.net.URL; import java.util.Objects; import java.util.ResourceBundle; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicLong; public class DesktopSearchController implements Initializable { private static final Logger LOGGER = Logger.getLogger(DesktopSearchController.class); @FXML MenuItem menuItemConfigure; @FXML MenuItem menuItemRecrawl; @FXML MenuItem menuItemClose; @FXML WebView webView; @FXML VBox statusBar; @FXML TextField statusText; @FXML MenuItem searchDocumentItem; private DesktopSearch application; private Backend backend; private Window window; class ProgressWatcherThread extends Thread { private final AtomicLong lastActivity; public ProgressWatcherThread() { super("Progress Watcher Thread"); lastActivity = new AtomicLong(); setName("UI Progress watcher thread"); } void notifyProgress() { lastActivity.set(System.currentTimeMillis()); } @Override public void start() { lastActivity.set(System.currentTimeMillis()); super.start(); } @Override public void run() { Platform.runLater(() -> { statusBar.setVisible(true); menuItemRecrawl.setDisable(true); }); while (!isInterrupted()) { if (lastActivity.get() < System.currentTimeMillis() - 5000) { // Longer than five seconds nothing happened interrupt(); } else { try { sleep(5000); } catch (InterruptedException e) { } } } Platform.runLater(() -> { statusBar.setVisible(false); statusBar.setManaged(false); menuItemRecrawl.setDisable(false); }); } } class FXProgressListener implements ProgressListener { private void wakeupThread() { if (!watcherThread.isAlive()) { watcherThread = new ProgressWatcherThread(); watcherThread.start(); } } public void newFileFound(final String aFilename) { wakeupThread(); watcherThread.notifyProgress(); Platform.runLater(() -> statusText.setText(aFilename)); } public void crawlingFinished() { Platform.runLater(() -> statusText.setText("")); } } private ProgressWatcherThread watcherThread; private String searchURL; public void configure(DesktopSearch aApplication, Backend aBackend, String aSearchURL, Window aWindow) { window = aWindow; application = aApplication; backend = aBackend; searchURL = aSearchURL; backend.setProgressListener(new FXProgressListener()); watcherThread = new ProgressWatcherThread(); webView.getEngine().setJavaScriptEnabled(true); webView.getEngine().getLoadWorker().stateProperty().addListener((ov, t, t1) -> { if (t1 == State.SUCCEEDED) { JSObject window1 = (JSObject) webView.getEngine().executeScript("window"); window1.setMember("desktop", new DesktopGateway(aApplication)); } }); webView.setContextMenuEnabled(false); webView.getEngine().load(aSearchURL); webView.getEngine().setJavaScriptEnabled(true); if (aApplication.getConfigurationManager().getConfiguration().isCrawlOnStartup()) { // Scedule a crawl run 5 seconds after startup... Timer theTimer = new Timer(); theTimer.schedule(new TimerTask() { @Override public void run() { Platform.runLater(() -> { recrawl(); }); } }, 5000); } } public void initialize(URL aUrl, ResourceBundle aResourceBundle) { Objects.requireNonNull(menuItemConfigure); Objects.requireNonNull(menuItemRecrawl); Objects.requireNonNull(menuItemClose); Objects.requireNonNull(webView); Objects.requireNonNull(statusBar); Objects.requireNonNull(statusText); Objects.requireNonNull(searchDocumentItem); menuItemConfigure.setOnAction(actionEvent -> configure()); menuItemRecrawl.setOnAction(actionEvent -> recrawl()); menuItemClose.setOnAction(actionEvent -> close()); searchDocumentItem.setOnAction(actionEvent -> webView.getEngine().load(searchURL)); statusBar.setManaged(false); statusBar.setVisible(false); } void close() { application.shutdown(); } void recrawl() { // Check if there is already a crawl run in progress // this might happen due to the crawl on startup feature if (!menuItemRecrawl.isDisable()) { statusBar.setVisible(true); statusBar.setManaged(true); menuItemRecrawl.setDisable(true); statusText.setText(""); try { backend.crawlLocations(); } catch (Exception e) { LOGGER.error("Error crawling locations", e); } } } void configure() { try { Stage stage = new Stage(); stage.setResizable(false); stage.initStyle(StageStyle.UTILITY); FXMLLoader theLoader = new FXMLLoader(getClass().getResource("/scenes/configuration.fxml")); AnchorPane theConfigurationRoot = theLoader.load(); stage.setScene(new Scene(theConfigurationRoot)); stage.setTitle("Configuration"); stage.initModality(Modality.APPLICATION_MODAL); ConfigurationController theConfigController = theLoader.getController(); theConfigController.initialize(application.getConfigurationManager(), stage); stage.initOwner(window); stage.show(); } catch (IOException e) { LOGGER.error("Error running configuration dialog", e); } } }