/******************************************************************************* * Copyright (C) 2015 Connor Lanigan (email: dev@connorlanigan.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package de.norvos.gui.windows; import static de.norvos.i18n.Translations.translate; import java.io.IOException; import java.net.URL; import javax.swing.JOptionPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.norvos.account.SettingsService; import de.norvos.utils.Constants; import de.norvos.utils.Errors; import de.norvos.utils.ResourceUtils; import de.norvos.utils.UnreachableCodeException; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Modality; import javafx.stage.Stage; /** * An abstract class providing functionality for loading a Window containing an * FXML file. * * @author Connor Lanigan */ public abstract class Window { private final static Logger LOGGER = LoggerFactory.getLogger(Window.class); private final URL FXML; private Boolean hasQuit; private final Object hasQuitLock = new Object(); private final double initialHeight; private final double initialWidth; private final URL LOCATION; private final boolean minimizeOnClose; private final Modality modality; private Stage stage; /** * Creates a Window which loads the given FXML file. It is important to * provide the <code>includeLocation</code>, which must contain all custom * components that are used in the given FXML file. * * @param fxml * the FXML file to load * @param includeLocation * the location from which to load custom components (must end * with a slash: "/") * @param minimizeOnClose * if the window should be minimized instead of closed if it is * requested to close */ protected Window(final String fxml, final String includeLocation, final boolean minimizeOnClose, final double initialWidth, final double initialHeight, final Modality modality) { if (fxml == null || includeLocation == null) { throw new NullPointerException( "A Window needs both an FXML and a location to include its containted components from."); } this.initialHeight = initialHeight; this.initialWidth = initialWidth; FXML = getClass().getResource(Constants.FXML_LOCATION + fxml); LOCATION = getClass().getResource(Constants.FXML_LOCATION + includeLocation); this.minimizeOnClose = minimizeOnClose; hasQuit = false; this.modality = modality; } public void closeWindow() { stage.close(); } /** * Sets the focus to the application window. This will on most platforms * remove the focus from the currently focused input element. */ public void focusWindow() { stage.requestFocus(); } public boolean hasQuit() { return hasQuit; } private void initWindow() { stage.setTitle(Constants.WINDOW_TITLE); stage.centerOnScreen(); if(modality != null) { stage.initModality(modality); } stage.setOnCloseRequest(event -> { if (minimizeOnClose) { stage.setIconified(true); event.consume(); } else { releaseWindowQuitLock(); } }); } private void loadWindowContent() { final FXMLLoader loader = new FXMLLoader(); loader.setLocation(LOCATION); loader.setResources(ResourceUtils.getLocalizedStringsBundle()); Parent parent; try { LOGGER.debug("Loading [{}] in location [{}] with language [{}].", FXML, LOCATION, SettingsService.getLanguage().name()); parent = loader.load(FXML.openStream()); final Scene scene = new Scene(parent, initialWidth, initialHeight); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); stage.setScene(scene); stage.show(); } catch (final IOException e) { LOGGER.error("FXML could not be loaded.", e); JOptionPane.showMessageDialog(null, translate("unexpected_quit"), "Norvos – Error", JOptionPane.WARNING_MESSAGE); Errors.stopApplication(); throw new UnreachableCodeException(); } } public void releaseWindowQuitLock() { synchronized (hasQuitLock) { hasQuit = true; hasQuitLock.notifyAll(); } } public void start(final Stage primaryStage) { stage = primaryStage; initWindow(); loadWindowContent(); } /** * Brings the window to the front. */ public void toFront() { stage.toFront(); } public void waitForClose() { if (minimizeOnClose) { throw new IllegalStateException("Can't wait for close on a window with \"minimizeOnClose\" enabled"); } synchronized (hasQuitLock) { while (!hasQuit) { try { hasQuitLock.wait(); } catch (final InterruptedException e) { } } } } }