/* * FXDesktopSearch Copyright 2013 Mirko Sertic * * 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.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.Parent; import javafx.scene.Scene; import javafx.scene.control.TextField; 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 lombok.extern.slf4j.Slf4j; import netscape.javascript.JSObject; 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; @Slf4j public class DesktopSearchController implements Initializable { @FXML WebView webView; @FXML VBox statusBar; @FXML TextField statusText; private DesktopSearchMain 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)); while (!isInterrupted()) { if (lastActivity.get() < System.currentTimeMillis() - 5000) { // Longer than five seconds nothing happened interrupt(); } else { try { sleep(5000); } catch (final InterruptedException e) { } } } Platform.runLater(() -> { statusBar.setVisible(false); statusBar.setManaged(false); }); } } class FXProgressListener implements ProgressListener { private void wakeupThread() { if (!watcherThread.isAlive()) { watcherThread = new ProgressWatcherThread(); watcherThread.start(); } } @Override public void infotext(final String aInfoText) { wakeupThread(); watcherThread.notifyProgress(); Platform.runLater(() -> statusText.setText(aInfoText)); } @Override public void crawlingFinished() { Platform.runLater(() -> statusText.setText("")); } } private ProgressWatcherThread watcherThread; private DesktopGateway gateway; public void configure(final DesktopSearchMain aApplication, final Backend aBackend, final String aSearchURL, final Window aWindow) { gateway = new DesktopGateway(aApplication, DesktopSearchController.this); window = aWindow; application = aApplication; backend = aBackend; backend.setProgressListener(new FXProgressListener()); watcherThread = new ProgressWatcherThread(); webView.getEngine().setJavaScriptEnabled(true); webView.getEngine().getLoadWorker().stateProperty().addListener((ov, t, t1) -> { if (t1 == State.SUCCEEDED) { final var windowGlobalJSNamespace = (JSObject) webView.getEngine().executeScript("window"); windowGlobalJSNamespace.setMember("desktop", gateway); } }); webView.setContextMenuEnabled(false); webView.getEngine().load(aSearchURL); webView.getEngine().setJavaScriptEnabled(true); if (aApplication.getConfigurationManager().getConfiguration().isCrawlOnStartup()) { // Scedule a crawl run 5 seconds after startup... final var theTimer = new Timer(); theTimer.schedule(new TimerTask() { @Override public void run() { Platform.runLater(() -> recrawl()); } }, 5000); } } @Override public void initialize(final URL aUrl, final ResourceBundle aResourceBundle) { Objects.requireNonNull(webView); Objects.requireNonNull(statusBar); Objects.requireNonNull(statusText); statusBar.setManaged(false); statusBar.setVisible(false); } public void close() { application.shutdown(); } public void recrawl() { statusBar.setVisible(true); statusBar.setManaged(true); statusText.setText(""); try { backend.crawlLocations(); } catch (final Exception e) { log.error("Error crawling locations", e); } } public void configure() { try { final var stage = new Stage(); stage.setResizable(false); stage.initStyle(StageStyle.UTILITY); final var theLoader = new FXMLLoader(getClass().getResource("/scenes/configuration.fxml")); final Parent theConfigurationRoot = theLoader.load(); stage.setScene(new Scene(theConfigurationRoot)); stage.setTitle("Configuration"); stage.initModality(Modality.APPLICATION_MODAL); final ConfigurationController theConfigController = theLoader.getController(); theConfigController.initialize(application.getConfigurationManager(), stage); stage.initOwner(window); stage.show(); } catch (final IOException e) { log.error("Error running configuration dialog", e); } } }