package com.noticeditorteam.noticeditor.controller; import com.noticeditorteam.noticeditor.Main; import com.noticeditorteam.noticeditor.exceptions.DismissException; import com.noticeditorteam.noticeditor.exceptions.ExportException; import com.noticeditorteam.noticeditor.io.DocumentFormat; import com.noticeditorteam.noticeditor.io.ExportStrategy; import com.noticeditorteam.noticeditor.io.ExportStrategyHolder; import com.noticeditorteam.noticeditor.io.importers.FileImporter; import com.noticeditorteam.noticeditor.model.NoticeStatusList; import com.noticeditorteam.noticeditor.model.Prefs; import com.noticeditorteam.noticeditor.model.PreviewStyles; import com.noticeditorteam.noticeditor.model.Themes; import com.noticeditorteam.noticeditor.view.Chooser; import com.noticeditorteam.noticeditor.view.Notification; import com.noticeditorteam.noticeditor.view.PasswordDialog; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.geometry.Orientation; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.WindowEvent; import javafx.util.Duration; import java.io.File; import java.io.IOException; import java.util.Locale; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; public class NoticeController { private static final Logger logger = Logger.getLogger(NoticeController.class.getName()); @FXML private VBox noticeTreeView; @FXML private NoticeTreeViewController noticeTreeViewController; @FXML private CheckMenuItem wordWrapItem; @FXML private Menu recentFilesMenu, previewStyleMenu, themesMenu, localesMenu; @FXML private SplitPane noticeView; @FXML private NoticeViewController noticeViewController; @FXML private VBox notificationBox; @FXML private Label notificationLabel; @FXML private ResourceBundle resources; private static NoticeController instance; private Main main; private File fileSaved; private boolean isEncryptedZip; public NoticeController() { instance = this; isEncryptedZip = false; } public void setApplication(Main main) { this.main = main; } public static NoticeController getController() { return instance; } public static NoticeViewController getNoticeViewController() { return instance.noticeViewController; } public static NoticeTreeViewController getNoticeTreeViewController() { return instance.noticeTreeViewController; } public static Logger getLogger() { return logger; } public void setIsEncryptedZip(boolean isEncryptedZip) { this.isEncryptedZip = isEncryptedZip; } /** * Initializes the controller class. */ @FXML private void initialize() { Notification.init(notificationBox, notificationLabel); // Restore initial directory File initialDirectory = new File(Prefs.getLastDirectory()); if (initialDirectory.isDirectory() && initialDirectory.exists()) { Chooser.setInitialDirectory(initialDirectory); } rebuildRecentFilesMenu(); // Set preview styles menu items ToggleGroup previewStyleGroup = new ToggleGroup(); for (PreviewStyles style : PreviewStyles.values()) { final String cssPath = style.getCssPath(); RadioMenuItem item = new RadioMenuItem(style.getName()); item.setUserData(style.ordinal()); item.setToggleGroup(previewStyleGroup); if (cssPath == null) { item.setSelected(true); } item.setOnAction(noticeViewController.onPreviewStyleChange); previewStyleMenu.getItems().add(item); } // Set themes menu items ToggleGroup themesGroup = new ToggleGroup(); for (Themes theme : Themes.values()) { final String cssPath = theme.getCssPath(); RadioMenuItem item = new RadioMenuItem(theme.getName()); item.setUserData(theme.ordinal()); item.setToggleGroup(themesGroup); if (cssPath == null) { item.setSelected(true); } item.setOnAction(noticeViewController.onThemeChange); themesMenu.getItems().add(item); } // Set locales menu items final Locale currentLocale = Locale.getDefault(); ToggleGroup localeGroup = new ToggleGroup(); for (String lang : new String[] {"en", "ru", "uk"}) { final Locale locale = new Locale(lang); RadioMenuItem item = new RadioMenuItem(beautify(locale.getDisplayLanguage(locale))); item.setToggleGroup(localeGroup); if (lang.equals(currentLocale.getLanguage())) { item.setSelected(true); } item.setOnAction(a -> { Prefs.setLocale(locale); Notification.show(resources.getString("messages.restart_is_needed"), Duration.seconds(5)); }); localesMenu.getItems().add(item); } noticeViewController.getEditor().wrapTextProperty().bind(wordWrapItem.selectedProperty()); noticeTreeViewController.rebuildTree(resources.getString("help")); } private void rebuildRecentFilesMenu() { recentFilesMenu.getItems().clear(); Prefs.getRecentFiles().stream() .distinct() .map(File::new) .filter(File::exists) .filter(File::isFile) .forEach(file -> { MenuItem item = new MenuItem(file.getAbsolutePath()); item.setOnAction(e -> { fileSaved = file; openDocument(file); }); recentFilesMenu.getItems().add(item); }); recentFilesMenu.setDisable(recentFilesMenu.getItems().isEmpty()); } @FXML private void handleNew(ActionEvent event) { noticeTreeViewController.rebuildTree(resources.getString("help")); fileSaved = null; NoticeStatusList.restore(); } @FXML private void handleOpen(ActionEvent event) { fileSaved = Chooser.file().open() .filter(Chooser.SUPPORTED, Chooser.ALL) .title(resources.getString("opennotice")) .show(main.getPrimaryStage()); if (fileSaved == null) return; final boolean isOpened = openDocument(fileSaved); if (isOpened) { Prefs.addToRecentFiles(fileSaved.getAbsolutePath()); rebuildRecentFilesMenu(); } } @FXML private void handleKey(KeyEvent event) { if (event.isControlDown()) { switch (event.getCode()) { case O: handleOpen(null); case S: if (event.isShiftDown()) handleSaveAs(null); else handleSave(null); case N: if (event.isShiftDown()) handleNew(null); } } } private boolean openDocument(File file) { try { noticeTreeViewController.rebuildTree(DocumentFormat.open(file)); return true; } catch (IOException e) { logger.log(Level.SEVERE, null, e); Notification.error(resources.getString("errors.cantopen") + " " + fileSaved.getName()); } catch (DismissException dismiss) { // no error message } return false; } @FXML private void handleSave(ActionEvent event) { if (fileSaved == null) { handleSaveAs(event); } else { saveDocument(fileSaved); } } @FXML private void handleSaveAs(ActionEvent event) { isEncryptedZip = false; fileSaved = Chooser.file().save() .filter(Chooser.ZIP, Chooser.ENC_ZIP, Chooser.JSON) .title(resources.getString("savenotice")) .show(main.getPrimaryStage()); if (fileSaved == null) { return; } saveDocument(fileSaved); } private void saveDocument(File file) { ExportStrategy strategy; if (Chooser.JSON.equals(Chooser.getLastSelectedExtensionFilter()) || file.getName().toLowerCase().endsWith(".json")) { strategy = ExportStrategyHolder.JSON; } else if (isEncryptedZip || Chooser.ENC_ZIP.equals(Chooser.getLastSelectedExtensionFilter())) { strategy = ExportStrategyHolder.ENC_ZIP; } else { strategy = ExportStrategyHolder.ZIP; isEncryptedZip = false; } try { if (DocumentFormat.save(file, noticeTreeViewController.getNoticeTree(), strategy)) { Notification.success(resources.getString("save.success")); } } catch (ExportException e) { logger.log(Level.SEVERE, null, e); Notification.error(resources.getString("save.error")); } } @FXML private void handleExportHtml(ActionEvent event) { File destDir = Chooser.directory() .title(resources.getString("exporthtml")) .show(main.getPrimaryStage()); if (destDir == null) { return; } try { ExportStrategyHolder.HTML.setProcessor(noticeViewController.processor); ExportStrategyHolder.HTML.export(destDir, noticeTreeViewController.getNoticeTree()); Notification.success(resources.getString("exporthtml.success")); } catch (ExportException e) { logger.log(Level.SEVERE, null, e); Notification.error(resources.getString("exporthtml.fail")); } } @FXML private void handleExit(ActionEvent event) { Platform.exit(); } @FXML private void handleSwitchOrientation(ActionEvent event) { noticeView.setOrientation(noticeView.getOrientation() == Orientation.HORIZONTAL ? Orientation.VERTICAL : Orientation.HORIZONTAL); } @FXML private void handleAbout(ActionEvent event) { Notification.show("NoticEditor\n==========\n\nhttps://github.com/NoticEditorTeam/NoticEditor"); } @FXML private void handleImportUrl(ActionEvent event) { try { final ResourceBundle resource = ResourceBundle.getBundle("resources.i18n.WebImport", Locale.getDefault()); Stage stage = new Stage(); stage.setTitle(resource.getString("import")); stage.initOwner(main.getPrimaryStage()); stage.initModality(Modality.WINDOW_MODAL); FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/WebImport.fxml"), resource); Scene scene = new Scene(loader.load()); stage.setScene(scene); WebImportController controller = (WebImportController) loader.getController(); controller.setImportCallback((html, ex) -> { if (ex != null) { Notification.error(ex.toString()); } else if (html != null) { noticeViewController.getEditor().setText(html); } stage.close(); }); stage.show(); } catch (Exception e) { logger.log(Level.SEVERE, null, e); } } @FXML private void handleImportFile(ActionEvent event) { File file = Chooser.file().open() .filter(Chooser.SUPPORTED, Chooser.ALL) .title(resources.getString("importfile")) .show(main.getPrimaryStage()); if (file == null) return; FileImporter.content().importFrom(file, null, (text, ex) -> { if (ex != null) { Notification.error(ex.toString()); } else if (text != null) { noticeViewController.getEditor().setText(text); } }); } public void onExit(WindowEvent we) { if (Chooser.getLastDirectory() != null) { Prefs.setLastDirectory(Chooser.getLastDirectory().getAbsolutePath()); } } private String beautify(String source) { StringBuilder builder = new StringBuilder(); char[] word; word = source.toCharArray(); word[0] = Character.toUpperCase(word[0]); builder.append(word); return builder.toString(); } public PasswordDialog newPasswordDialog(String defaultValue) { final PasswordDialog dialog = new PasswordDialog(defaultValue); dialog.setTitle(resources.getString("dialogs.passworddialog.title")); dialog.setHeaderText(resources.getString("dialogs.passworddialog.headertext")); dialog.initOwner(main.getPrimaryStage()); dialog.initModality(Modality.WINDOW_MODAL); return dialog; } }