All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.examples.viewer.gui.JavaFxGUI Maven / Gradle / Ivy

There is a newer version: 20151002
Show newest version
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/support/
 *
 * (C) Copyright 1997-2017 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * JavaFxGUI.java
 * ---------------
 */
package org.jpedal.examples.viewer.gui;

import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.*;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.Transition;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.*;
import javafx.scene.CacheHint;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
import javax.imageio.ImageIO;
import org.jpedal.FileAccess;
import org.jpedal.PdfDecoderFX;
import org.jpedal.PdfDecoderInt;
import org.jpedal.display.Display;
import org.jpedal.display.GUIModes;
import org.jpedal.display.GUIThumbnailPanel;
import org.jpedal.display.PageOffsets;
import org.jpedal.display.javafx.PageFlowDisplayFX;
import org.jpedal.display.javafx.SingleDisplayFX;
import org.jpedal.examples.viewer.*;
import org.jpedal.examples.viewer.commands.OpenFile;
import org.jpedal.examples.viewer.commands.javafx.JavaFXOpenFile;
import org.jpedal.examples.viewer.gui.generic.*;
import org.jpedal.examples.viewer.gui.javafx.*;
import org.jpedal.examples.viewer.gui.javafx.FXViewerTransitions.TransitionDirection;
import org.jpedal.examples.viewer.gui.javafx.FXViewerTransitions.TransitionType;
import org.jpedal.examples.viewer.gui.javafx.dialog.FXInputDialog;
import org.jpedal.examples.viewer.gui.javafx.dialog.FXMessageDialog;
import org.jpedal.examples.viewer.gui.javafx.dialog.FXOptionDialog;
import org.jpedal.examples.viewer.paper.PaperSizes;
import org.jpedal.examples.viewer.utils.PropertiesFile;
import org.jpedal.external.ExternalHandlers;
import org.jpedal.external.Options;
import org.jpedal.fonts.tt.TTGlyph;
import org.jpedal.gui.GUIFactory;
import org.jpedal.io.StatusBarFX;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.acroforms.AcroRenderer;
import org.jpedal.objects.acroforms.actions.ActionHandler;
import org.jpedal.objects.layers.PdfLayerList;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.utils.BrowserLauncher;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.Messages;
import org.w3c.dom.Node;

/**
 * 
Description: GUI functions in Viewer implemented in JavaFX */ @SuppressWarnings("MagicConstant") public class JavaFxGUI extends GUI { private RefreshLayout viewListener; protected final JavaFXButtons fxButtons; //GUICursor object that holds everything todo with Cursor for SwingGUI private final SwingCursor guiCursor = new SwingCursor(); //all mouse actions private GUIMouseHandler mouseHandler; private Timeline memoryMonitor; //flag for marks new thumbnail preview private boolean debugThumbnail; private boolean sideTabBarOpenByDefault; private String startSelectedTab = "Pages"; //use new GUI layout // public static String windowTitle; private boolean hasListener; private boolean isSetup; private int lastTabSelected = -1; private boolean tabsExpanded; private PaperSizes paperSizes; /* * Multibox for new GUI Layout Component to contain memory, cursor and * loading bars IMPORTANT : the width of the multibox needs to be the same * size as the width of pagesToolBar to ensure that it is centered on the * page. */ private HBox multiboxfx; private FXProgressBarWithText memoryBarFX; //visual display of current cursor co-ords on page private TextField coordsFX; //Track whether both pages are properly displayed private static final boolean pageTurnScalingAppropriate = true; //holds back/forward buttons at bottom of page private HBox navButtons; //displayed on left to hold thumbnails, bookmarks private TabPane navOptionsPanel; //tell user on first form change it can be saved private static final boolean firstTimeFormMessage = true; private Label pageCounter1; public TextField pageCounter2; private Label pageCounter3; private final JavaFXSignaturesPanel signaturesPanel; private final JavaFXLayersPanel layersPanel; //stop user forcing open tab before any pages loaded private boolean tabsNotInitialised = true; //private boolean thumbnailsInitialised; private boolean hasPageChanged; private ToolBar navToolBar; private ToolBar pagesToolBar; //The main JavaFX Stage private final Stage stage; //The main JavaFX Scene private Scene scene; //BorderPane lays out children in top, left, right, bottom, and center positions. private final BorderPane root; private static final int dropshadowDepth = 40; private Group group; // Scrollpane to contain the PDF page private ScrollPane pageContainer; private SplitPane center; private final VBox topPane; private StatusBarFX downloadBar; private boolean searchInMenu; private TextField searchText; private GUISearchList results; private MenuBar options; private boolean cursorOverPage; private TransitionType transitionType = TransitionType.None; // Scrolls between pages when the pdf is not zoomed in private ScrollBar pageScroll; private ChangeListener fxChangeListener; public JavaFxGUI(final Stage stage, final PdfDecoderInt decode_pdf, final Values commonValues, final GUIThumbnailPanel thumbnails, final PropertiesFile properties) { super(decode_pdf, commonValues, thumbnails, properties); isJavaFX = true; if (debugFX) { System.out.println("JavaFxGUI"); } this.stage = stage; root = new BorderPane(); topPane = new VBox(); pageCounter2 = new TextField(); navButtons = new HBox(); fxButtons = new JavaFXButtons(); pageCounter3 = new Label(); navToolBar = new ToolBar(); pagesToolBar = new ToolBar(); tree = new JavaFXOutline(); layersPanel = new JavaFXLayersPanel(); signaturesPanel = new JavaFXSignaturesPanel(); setLookAndFeel(); setupDisplay(); } /* For the following methods getThumbnailScrollBar, setThumbnailScrollBarVisibility and setThumbnailScrollBarValue: These methods have not been implemented yet as they do not have the equivalent to SwingGUI.thumbscroll implemented in FX. */ @Override public Object getThumbnailScrollBar() { if (debugFX) { System.out.println("getThumbnailScrollBar - Implemented"); } return pageScroll; } @Override public void setThumbnailScrollBarVisibility(final boolean isVisible) { if (debugFX) { System.out.println("setThumbnailScrollBarVisibility - Implemented"); } pageScroll.setVisible(isVisible); //#making invisible still shows the gap so iam padding negative to hide it if(isVisible){ pageScroll.setStyle("-fx-padding:0px"); }else{ pageScroll.setStyle("-fx-padding:-10px"); } } @Override public void setThumbnailScrollBarValue(final int pageNum) { if (debugFX) { System.out.println("setThumbnailScrollBarValue - Implemented"); } pageScroll.setValue(pageNum); } /** * set the look and feel for the GUI components to be the default for the * system it is running on */ private void setLookAndFeel() { if (debugFX) { System.out.println("setLookAndFeel - Not yet implemented in JavaFX"); } /* * JavaFX has no look and feel option, 2.2 has a theme called Caspian * (the default theme) only. FX8 has Moderna and Caspian, which can be * set in the class which overrides Application with * setUserAgentStylesheet(url) * * We can also define our own rules on top of this with: * root.getStylesheets().clear(); * root.getStylesheets().add("stylesheetlocation"); Also see: * http://blog.idrsolutions.com/2014/04/use-external-css-files-javafx/ */ root.getStylesheets().clear(); root.getStylesheets().add(getClass().getResource("/org/jpedal/examples/viewer/res/css/JavaFXForms.css").toExternalForm()); root.getStylesheets().add(getClass().getResource("/org/jpedal/examples/viewer/res/css/JavaFXPages.css").toExternalForm()); } private void setupDisplay() { setDisplayView(Display.SINGLE_PAGE, Display.DISPLAY_CENTERED); //pass in SwingGUI so we can call via callback decode_pdf.addExternalHandler(this, Options.GUIContainer); menuItems = new JavaFXMenuItems(properties); } @Override public SplitPane getDisplayPane() { return center; } @Override public Object getMultiViewerFrames() { if (debugFX) { System.out.println("getMultiViewerFrames"); } return null; } @Override public String getPropertiesFileLocation() { if (debugFX) { System.out.println("getPropertiesFileLocation"); } return properties.getConfigFile(); } @Override public String getBookmark(final String bookmark) { if (debugFX) { System.out.println("getBookmark"); } return tree.getPage(bookmark); } @Override public void reinitialiseTabs(final boolean showVisible) { if (debugFX) { System.out.println("reinitialiseTabs"); } //not needed if (commonValues.getModeOfOperation() == Values.RUNNING_PLUGIN) { return; } if (properties.getValue("ShowSidetabbar").equalsIgnoreCase("true")) { if (!showVisible && !properties.getValue("consistentTabBar").equalsIgnoreCase("true")) { if (sideTabBarOpenByDefault) { setSplitDividerLocation(expandedSize); tabsExpanded = true; } else { setSplitDividerLocation(collapsedSize); tabsExpanded = false; } } lastTabSelected = -1; if (!commonValues.isPDF()) { navOptionsPanel.setVisible(false); } else { navOptionsPanel.setVisible(true); /* * add/remove optional tabs */ if (!decode_pdf.hasOutline()) { int outlineTab = -1; //see if there is an outlines tab for (int jj = 0; jj < navOptionsPanel.getTabs().size(); jj++) { if (navOptionsPanel.getTabs().get(jj).getText().equals(bookmarksTitle)) { outlineTab = jj; } } if (outlineTab != -1) { navOptionsPanel.getTabs().remove(outlineTab); } } else if (properties.getValue("Bookmarkstab").equalsIgnoreCase("true")) { int outlineTab = -1; //see if there is an outlines tab for (int jj = 0; jj < navOptionsPanel.getTabs().size(); jj++) { if (navOptionsPanel.getTabs().get(jj).getText().equals(bookmarksTitle)) { outlineTab = jj; } } if (outlineTab == -1){ navOptionsPanel.getTabs().add(navOptionsPanel.getTabs().size() - 1, (Tab) tree); } } /* * handle signatures pane */ final AcroRenderer currentFormRenderer = decode_pdf.getFormRenderer(); Iterator signatureObjects = null; if (currentFormRenderer != null) { signatureObjects = currentFormRenderer.getSignatureObjects(); } if (signatureObjects != null) { signaturesPanel.reinitialise(decode_pdf, signatureObjects); checkTabShown(signaturesTitle); } else { removeTab(signaturesTitle); } /* * add a control Panel to enable/disable layers */ //layers object layersObject = (PdfLayerList) decode_pdf.getJPedalObject(PdfDictionary.Layer); if (layersObject != null && layersObject.getLayersCount() > 0) { //some files have empty Layers objects checkTabShown(layersTitle); layersPanel.reinitialise(layersObject, decode_pdf, null, commonValues.getCurrentPage()); } else { removeTab(layersTitle); } setBookmarks(false); } if (tabsNotInitialised) { for (int i = 0; i != navOptionsPanel.getTabs().size(); i++) { if (navOptionsPanel.getTabs().get(i).getText().equals(startSelectedTab)) { navOptionsPanel.getSelectionModel().select(i); break; } } } } } private void checkTabShown(final String title) { if (debugFX) { System.out.println("checkTabShown"); } int outlineTab = -1; //see if there is an outlines tab for (int jj = 0; jj < navOptionsPanel.getTabs().size(); jj++) { if (navOptionsPanel.getTabs().get(jj).getText().equals(title)) { outlineTab = jj; } } if (outlineTab == -1) { if (title.equals(signaturesTitle) && properties.getValue("Signaturestab").equalsIgnoreCase("true")) { //stop spurious display of Sig tab navOptionsPanel.getTabs().add(navOptionsPanel.getTabs().size() - 1, signaturesPanel); } else if (title.equals(layersTitle) && properties.getValue("Layerstab").equalsIgnoreCase("true")) { navOptionsPanel.getTabs().add(navOptionsPanel.getTabs().size() - 1, layersPanel); } } } private void removeTab(final String title) { if (debugFX) { System.out.println("removeTab"); } int outlineTab = -1; //see if there is an outlines tab for (int jj = 0; jj < navOptionsPanel.getTabs().size(); jj++) { if (navOptionsPanel.getTabs().get(jj).getText().equals(title)) { outlineTab = jj; } } if (outlineTab != -1){ navOptionsPanel.getTabs().remove(outlineTab); } } /** * Stop thumbnail generation in the pages thumbnail tab */ @Override public void stopThumbnails() { if (debugFX) { System.out.println("stopThumbnails"); } if (thumbnails.isShownOnscreen()) { thumbnails.terminateDrawing(); thumbnails.removeAllListeners(); } } /** * Flags the pages thumbnail tab as requiring reinitialisation */ @Override public void reinitThumbnails() { if (debugFX) { System.out.println("reinitThumbnails"); } isSetup = false; } /** * reset the navigation bar so that it appears closed */ @Override public void resetNavBar() { if (debugFX) { System.out.println("resetNavBar"); } if (!properties.getValue("consistentTabBar").equalsIgnoreCase("true")) { setSplitDividerLocation(collapsedSize); tabsExpanded = false; tabsNotInitialised = true; } //disable page view buttons until we know we have multiple pages fxButtons.setPageLayoutButtonsEnabled(false); } /* (non-Javadoc) * @see org.jpedal.examples.viewer.gui.swing.GUIFactory#setNoPagesDecoded() * * Called when new file opened so we set flags here */ @Override public void setNoPagesDecoded() { if (debugFX) { System.out.println("setNoPagesDecoded"); } bookmarksGenerated = false; resetNavBar(); } @Override public void setDisplayMode(final Integer mode) { if (debugFX) { System.out.println("setDisplayMode not implemented for JavaFX in JavaFxGUI"); } } @Override public boolean isSingle() { if (debugFX) { System.out.println("isSingle"); } return true; } @Override public Object getThumbnailPanel() { if (debugFX) { System.out.println("getThumbnailPanel"); } return thumbnails; } @Override public Object getOutlinePanel() { if (debugFX) { System.out.println("getOutlinePanel"); } return tree; } /** * Can't get the scrollbar from a scroll pane, other methods should be * implemented to circumvent this when needed */ @Override public Object getVerticalScrollBar() { if (debugFX) { System.out.println("getVerticalScrollBar"); } // Altered function due to above comments, return thumbscroll only when shown if (pageScroll.isVisible()) { return pageScroll; } else { return null; } } /* *Allows user to embed viewer into own application */ @Override public void setRootContainer(final Object rawValue) { if (rawValue == null) { throw new RuntimeException("Null containers not allowed."); } final Parent parentPane = (Parent) rawValue; //We add the Viewer to the parent pane depending on its type. if (parentPane instanceof Pane) { ((Pane) parentPane).getChildren().add(root); } else if (parentPane instanceof ScrollPane) { ((ScrollPane) parentPane).setContent(root); } //Load width and height from properties file int width = Integer.parseInt(properties.getValue("startViewerWidth")); int height = Integer.parseInt(properties.getValue("startViewerHeight")); //Used to prevent infinite scroll issue as a preferred size has been set final Rectangle2D d = Screen.getPrimary().getVisualBounds(); if (width < 0) { width = (int) (d.getWidth() / 2); if (width < 700) { width = 700; } properties.setValue("startViewerWidth", String.valueOf(width)); } if (height < 0) { height = (int) (d.getHeight() / 2); properties.setValue("startViewerHeight", String.valueOf(height)); } //allow user to alter size final String customWindowSize = System.getProperty("org.jpedal.startWindowSize"); if (customWindowSize != null) { final StringTokenizer values = new StringTokenizer(customWindowSize, "x"); if (values.countTokens() != 2) { throw new RuntimeException("Unable to use value for org.jpedal.startWindowSize=" + customWindowSize + "\nValue should be in format org.jpedal.startWindowSize=200x300"); } } } /* (non-Javadoc) * @see org.jpedal.examples.viewer.gui.swing.GUIFactory#resetRotationBox() */ @Override public void resetRotationBox() { if (debugFX) { System.out.println("resetRotationBox"); } final PdfPageData currentPageData = decode_pdf.getPdfPageData(); if (decode_pdf.getDisplayView() == Display.SINGLE_PAGE) { rotation = currentPageData.getRotation(commonValues.getCurrentPage()); } if (getSelectedComboIndex(Commands.ROTATION) != (rotation / 90)) { setSelectedComboIndex(Commands.ROTATION, (rotation / 90)); } else if (!Values.isProcessing()) { // ((PdfDecoder)decode_pdf).repaint(); } } /* (non-Javadoc) * @see org.jpedal.examples.viewer.gui.swing.GUIFactory#getProperties() */ @Override public PropertiesFile getProperties() { return properties; } /** * Set Search Bar to be in the Left hand Tabbed pane */ @Override public void searchInTab(final GUISearchWindow searchFrame) { if (debugFX) { System.out.println("searchInTab"); } this.searchFrame = searchFrame; this.searchFrame.init(decode_pdf, commonValues); } /** * Adds Drop-Down Search Menu Options to the In-Menu Search Field. * @return */ private MenuBar createMenuBarSearchOptions() { //Setup Search In Menu Options. final CheckMenuItem cb1 = new CheckMenuItem(Messages.getMessage("PdfViewerSearch.WholeWords")); final CheckMenuItem cb2 = new CheckMenuItem(Messages.getMessage("PdfViewerSearch.CaseSense")); final CheckMenuItem cb3 = new CheckMenuItem(Messages.getMessage("PdfViewerSearch.MultiLine")); //Add Search 0ptions listeners. cb1.selectedProperty().addListener(new ChangeListener() { @Override public void changed(final ObservableValue ov, final Boolean old_val, final Boolean new_val) { searchFrame.setWholeWords(cb1.isSelected()); } }); cb2.selectedProperty().addListener(new ChangeListener() { @Override public void changed(final ObservableValue ov, final Boolean old_val, final Boolean new_val) { searchFrame.setCaseSensitive(cb2.isSelected()); } }); cb3.selectedProperty().addListener(new ChangeListener() { @Override public void changed(final ObservableValue ov, final Boolean old_val, final Boolean new_val) { searchFrame.setMultiLine(cb3.isSelected()); } }); //Setup Menu Apply Image, and Add Items. final Menu menu = new Menu(); final Image image = new Image(guiCursor.getURLForImage("menuSearchOptions.png").toString()); menu.setGraphic(new ImageView(image)); menu.getItems().addAll(cb1, cb2, cb3); //Setup Menu Bar and Add Menu. options = new MenuBar(); options.setBackground(Background.EMPTY); options.setFocusTraversable(false); options.setTooltip(new Tooltip(Messages.getMessage("PdfViewerSearch.Options"))); options.getMenus().add(menu); return options; } /* * Set Search Bar to be in the Top Button Bar */ private void searchInMenu(final GUISearchWindow searchFrame) { this.searchFrame = searchFrame; searchInMenu = true; searchFrame.find(decode_pdf, commonValues); fxButtons.getTopButtons().getItems().add(searchText); fxButtons.getTopButtons().getItems().add(createMenuBarSearchOptions()); fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerSearch.Previous"), "search_previous.gif", Commands.PREVIOUSRESULT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerSearch.Next"), "search_next.gif", Commands.NEXTRESULT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); fxButtons.getButton(Commands.NEXTRESULT).setEnabled(false); fxButtons.getButton(Commands.PREVIOUSRESULT).setEnabled(false); fxButtons.getButton(Commands.NEXTRESULT).setVisible(true); fxButtons.getButton(Commands.PREVIOUSRESULT).setVisible(true); } @Override public void setPropertiesFileLocation(final String file) { properties.loadProperties(file); } @Override public Commands getCommand() { return currentCommands; } /* (non-Javadoc) * @see org.jpedal.examples.viewer.gui.swing.GUIFactory#init(java.lang.String[], org.jpedal.examples.viewer.Commands, org.jpedal.examples.viewer.utils.Printer) */ @Override public void init(final Commands currentCommands) { if (debugFX) { System.out.println("init"); } updateTransitionType(); mouseHandler = new JavaFXMouseListener((PdfDecoderFX) decode_pdf, this, commonValues, currentCommands); mouseHandler.setupMouse(); //single listener to execute all commands currentCommandListener = new CommandListenerFX(currentCommands); super.init(currentCommands); //Set up from properties try { //Set whether to use hinting String propValue = properties.getValue("useHinting"); final String propValue2 = System.getProperty("org.jpedal.useTTFontHinting"); //check JVM flag first if (propValue2 != null) { //check if properties file conflicts if (!propValue.isEmpty() && !propValue2.equalsIgnoreCase(propValue.toLowerCase())) { showMessageDialog(Messages.getMessage("PdfCustomGui.hintingFlagFileConflict")); } TTGlyph.useHinting = propValue2.equalsIgnoreCase("true"); //check properties file } else { TTGlyph.useHinting = !propValue.isEmpty() && propValue.equalsIgnoreCase("true"); } //Set icon location propValue = properties.getValue("iconLocation"); if (!propValue.isEmpty() && GUI.debugFX) { System.out.println("guiCursor is not set to JavaFXCursor in method init() in class JavaFxGUI.java"); } //guiCursor.setIconLocation(propValue); } catch (final Exception e) { e.printStackTrace(); } //setup combo boxes setupComboBoxes(); if(LogWriter.isRunningFromIDE || org.jpedal.DevFlags.GUITESTINGINPROGRESS){ nameElements(); } //Initialise the Swing Buttons * fxButtons.init(true); //create a menu bar and add to display createTopMenuBar(); //create other tool bars and add to display createOtherToolBars(); //set colours on display boxes and add listener to page number setupBottomToolBarItems(); //sets up all the toolbar items fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.openFile"), "open.gif", Commands.OPENFILE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); if (searchFrame != null && searchFrame.getViewStyle() == GUISearchWindow.SEARCH_EXTERNAL_WINDOW) { searchFrame.setViewStyle(GUISearchWindow.SEARCH_EXTERNAL_WINDOW); fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.search"), "find.gif", Commands.FIND, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); } fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.properties"), "properties.gif", Commands.DOCINFO, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); if (commonValues.getModeOfOperation() == Values.RUNNING_PLUGIN) { fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.about"), "about.gif", Commands.ABOUT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); } //snapshot screen function fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.snapshot"), "snapshot.gif", Commands.SNAPSHOT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); final Separator sep = new Separator(); sep.setPrefSize(5, 32); //combo boxes on toolbar addCombo(Messages.getMessage("PdfViewerToolbarTooltip.zoomin"), Commands.SCALING); addCombo(Messages.getMessage("PdfViewerToolbarTooltip.rotation"), Commands.ROTATION); //rotation buttons fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.rotateLeft"), "rotateLeft.gif", Commands.ROTATELEFT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.rotateRight"), "rotateRight.gif", Commands.ROTATERIGHT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.mouseMode"), "mouse_select.png", Commands.MOUSEMODE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); fxButtons.getTopButtons().getItems().add(sep); if(includeExtraMenus && commonValues.getModeOfOperation() != Values.RUNNING_PLUGIN) { fxButtons.addButton(GUIFactory.BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.help"), "help.png", Commands.HELP, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); } //addButton(GUIFactory.BUTTONBAR,Messages.getMessage("PdfViewerToolbarTooltip.buy"),iconLocation+"buy.png",Commands.BUY); if(includeExtraMenus && commonValues.getModeOfOperation() != Values.RUNNING_PLUGIN) { fxButtons.addButton(GUIFactory.BUTTONBAR, "RSS", "rss.gif", Commands.RSS, menuItems, this, currentCommandListener, pagesToolBar, navToolBar); } //add cursor location initMultiBox(); //navigation toolbar for moving between pages createNavbar(); if (searchFrame != null && searchFrame.getViewStyle() == GUISearchWindow.SEARCH_MENU_BAR) { searchInMenu(searchFrame); } //status object on toolbar showing 0 -100 % completion initStatus(); /* * Ensure all gui sections are displayed correctly * Issues found when removing some sections */ if (GUI.debugFX) { System.out.println("Swing specific invalidate(), validate(), repaint() not implemented for JavaFX in JavaFxGUI"); } /* * set display to occupy half screen size and display, add listener and * make sure appears in centre */ if (commonValues.getModeOfOperation() != Values.RUNNING_APPLET) { //Load width and height from properties file int width = Integer.parseInt(properties.getValue("startViewerWidth")); int height = Integer.parseInt(properties.getValue("startViewerHeight")); //Used to prevent infinite scroll issue as a preferred size has been set //Used to prevent infinite scroll issue as a preferred size has been set final Rectangle2D d = Screen.getPrimary().getVisualBounds(); if (width < 0) { width = ((int) d.getWidth()) / 2; if (width < minimumScreenWidth) { width = minimumScreenWidth; } properties.setValue("startViewerWidth", String.valueOf(width)); } if (height < 0) { height = ((int) d.getHeight()) / 2; properties.setValue("startViewerHeight", String.valueOf(height)); } //allow user to alter size final String customWindowSize = System.getProperty("org.jpedal.startWindowSize"); if (customWindowSize != null) { final StringTokenizer values = new StringTokenizer(customWindowSize, "x"); System.out.println(values.countTokens()); if (values.countTokens() != 2) { throw new RuntimeException("Unable to use value for org.jpedal.startWindowSize=" + customWindowSize + "\nValue should be in format org.jpedal.startWindowSize=200x300"); } try { width = Integer.parseInt(values.nextToken().trim()); height = Integer.parseInt(values.nextToken().trim()); } catch (final Exception ee) { throw new RuntimeException("Unable to use value for org.jpedal.startWindowSize=" + customWindowSize + "\nValue should be in format org.jpedal.startWindowSize=200x300 " + ee); } } setupPDFDisplayPane(); // add the pdf display left and right panes setupBorderPanes(); createMainViewerWindow(width, height); } //Add Background color to the JavaFX panel to help break up view setupCenterPanelBackground(); setupPDFBorder(); //Set side tab bar state at start up if (sideTabBarOpenByDefault) { setSplitDividerLocation(expandedSize); tabsExpanded = true; } else { setSplitDividerLocation(collapsedSize); tabsExpanded = false; } //Load properties try { GUIModifier.load(properties, this); } catch (final Exception e) { e.printStackTrace(); } //DELETE This code block once all items are implemented. //This code is here so the loading of properties does not override //the disabling of unimplemented items in the FXViewer. final int ALL = -10; ((JavaFXMenuItems) menuItems).disableUnimplementedItems(ALL, false); fxButtons.disableUnimplementedItems(ALL); } private void nameElements() { ((JavaFXMenuItems) menuItems).getCurrentMenuFX().setId("menuBar"); pageCounter2.setId("pageCounter"); if (org.jpedal.DevFlags.GUITESTINGINPROGRESS) { rotationBox.setName("rotationBox"); scalingBox.setName("scalingBox"); } } /** * method being called from within init to create other tool bars to add to * display */ @Override protected void createOtherToolBars() { if (GUI.debugFX) { System.out.println("createOtherToolBars almost finished for JavaFX"); } //This is where we setup the top most menu menuItems.createMainMenu(true, currentCommandListener, true, commonValues, currentCommands, fxButtons); topPane.getChildren().add(((JavaFXMenuItems) menuItems).getCurrentMenuFX()); //This is where we add the Buttons for the top ToolBar topPane.getChildren().add(fxButtons.getTopButtons()); } private void handleTabbedPanes() { if (tabsNotInitialised) { return; } if (debugFX) { System.out.println("handleTabbedPanes"); } final int tabSelected; tabSelected = navOptionsPanel.getSelectionModel().getSelectedIndex(); if (tabSelected == -1) { return; } if (!tabsExpanded) { //workout selected tab final String tabName = navOptionsPanel.getTabs().get(tabSelected).getText(); if (tabName != null && tabName.equals(pageTitle)) { thumbnails.setIsDisplayedOnscreen(true); } else { thumbnails.setIsDisplayedOnscreen(false); } // Don't expand on dummy tab if (!navOptionsPanel.getSelectionModel().getSelectedItem().isDisable()) { setSplitDividerLocation(expandedSize); tabsExpanded = true; } } else if (lastTabSelected == tabSelected && !hasPageChanged) { setSplitDividerLocation(collapsedSize); tabsExpanded = false; thumbnails.setIsDisplayedOnscreen(false); } lastTabSelected = tabSelected; hasPageChanged = false; } private void initMultiBox() { multiboxfx = new HBox(); initCoordBox(); initMemoryBar(); initDownloadBar(); } @Override public void setMultibox(final int[] flags) { //debug code for formclicktests if (GUI.alwaysShowMouse) { multiboxfx.getChildren().clear(); multiboxfx.getChildren().add(coordsFX); return; } //deal with flags if (flags.length > 1 && flags[0] == CURSOR) { //if no change, return if (cursorOverPage != (flags[1] == 1)) { cursorOverPage = flags[1] == 1; } else { return; } } //LOAD_PROGRESS: // if (statusBar.isEnabled() && statusBar.isVisible() && !statusBar.isDone()) { // multibox.removeAll(); // statusBar.getStatusObject().setSize(multibox.getSize()); // multibox.add(statusBar.getStatusObject(), BorderLayout.CENTER); // // multibox.repaint(); // return; // } //CURSOR: if (cursorOverPage && decode_pdf.isOpen()) { multiboxfx.getChildren().clear(); multiboxfx.getChildren().add(coordsFX); return; } //DOWNLOAD_PROGRESS: if (!downloadBar.isDisable() && downloadBar.isVisible() && !downloadBar.isDone() && (decode_pdf.isLoadingLinearizedPDF() || !decode_pdf.isOpen())) { multiboxfx.getChildren().clear(); multiboxfx.getChildren().add(downloadBar.getStatusObject()); return; } //MEMORY: if (memoryBarFX.isVisible()) { multiboxfx.getChildren().clear(); multiboxfx.getChildren().add(memoryBarFX); } } private void initDownloadBar() { downloadBar = new StatusBarFX(new Color((185.0d / 255.0d), (209.0d / 255.0d), 0, 1)); downloadBar.getStatusObject().prefHeightProperty().bind(multiboxfx.heightProperty()); downloadBar.getStatusObject().prefWidthProperty().bind(multiboxfx.widthProperty()); downloadBar.setVisible(false); } private void initMemoryBar() { memoryBarFX = new FXProgressBarWithText(); memoryBarFX.prefHeightProperty().bind(multiboxfx.heightProperty()); memoryBarFX.prefWidthProperty().bind(multiboxfx.widthProperty()); if (memoryMonitor == null) { //ensure only 1 instance memoryMonitor = new Timeline(new KeyFrame(Duration.seconds(0.5), new EventHandler() { @Override public void handle(final ActionEvent event) { final int free = (int) (Runtime.getRuntime().freeMemory() / (1024 * 1024)); final int total = (int) (Runtime.getRuntime().totalMemory() / (1024 * 1024)); final double used = (total - free) / total; //this broke the image saving when it was run every time if (finishedDecoding) { finishedDecoding = false; } if (Platform.isFxApplicationThread()) { memoryBarFX.setProgress(used); memoryBarFX.setText((total - free) + "M of " + total + 'M'); } else { // Needs to be done on the FX Thread Platform.runLater(new Runnable() { @Override public void run() { memoryBarFX.setProgress(used); memoryBarFX.setText((total - free) + "M of " + total + 'M'); } }); } } })); memoryMonitor.setCycleCount(Timeline.INDEFINITE); memoryMonitor.play(); } } /** * Overrides method from GUI.java, see GUI.java for DOCS. */ @Override protected void setViewerIcon() { //Check if file location provided final URL path = guiCursor.getURLForImage("icon.png"); if (stage != null && path != null) { try { //Converting Swing BufferedImage to FX WritableImage final BufferedImage bi = ImageIO.read(path); WritableImage fontIcon = new WritableImage(bi.getWidth(), bi.getHeight()); fontIcon = SwingFXUtils.toFXImage(bi, fontIcon); stage.getIcons().add(fontIcon); } catch (final Exception e) { LogWriter.writeLog("Exception attempting to set Icon " + e); } } } private boolean setDisplayView2(final int displayView, final int orientation) { final DecoderOptions options = decode_pdf.getDecoderOptions(); Display pages = decode_pdf.getPages(); final int lastDisplayView = decode_pdf.getDisplayView(); final FileAccess fileAccess = (FileAccess) decode_pdf.getExternalHandler(Options.FileAccess); final int pageNumber = decode_pdf.getPageNumber(); final ExternalHandlers externalHandlers = decode_pdf.getExternalHandler(); final PdfDecoderFX comp = (PdfDecoderFX) decode_pdf; final Pane highlightsPane = comp.highlightsPane; options.setPageAlignment(orientation); if (pages != null) { pages.stopGeneratingPage(); } if (Platform.isFxApplicationThread()) { if (highlightsPane != null && highlightsPane.getParent() != null) { ((Group) highlightsPane.getParent()).getChildren().remove(highlightsPane); } } else { final Runnable doPaintComponent = new Runnable() { @Override public void run() { if (highlightsPane != null && highlightsPane.getParent() != null) { ((Group) highlightsPane.getParent()).getChildren().remove(highlightsPane); } } }; Platform.runLater(doPaintComponent); } final boolean hasChanged = displayView != lastDisplayView; options.setDisplayView(displayView); if (lastDisplayView != displayView && lastDisplayView == Display.PAGEFLOW) { pages.dispose(); } final Object customFXHandle = externalHandlers.getExternalHandler(Options.MultiPageUpdate); switch (displayView) { case Display.SINGLE_PAGE: if (pages == null || hasChanged) { final DynamicVectorRenderer currentDisplay = decode_pdf.getDynamicRenderer(); pages = new SingleDisplayFX(pageNumber, currentDisplay, comp, options); } break; case Display.PAGEFLOW: if (pages instanceof PageFlowDisplayFX) { return hasChanged; } if (lastDisplayView != Display.SINGLE_PAGE) { setDisplayView(Display.SINGLE_PAGE, 0); setDisplayView(Display.PAGEFLOW, 0); return hasChanged; } pages = new PageFlowDisplayFX((GUIFactory) customFXHandle, comp); break; } /* * setup once per page getting all page sizes and working out settings * for views */ if (fileAccess.getOffset() == null) { fileAccess.setOffset(new PageOffsets(decode_pdf.getPageCount(), decode_pdf.getPdfPageData())); } pages.setup(options.useHardwareAcceleration(), fileAccess.getOffset()); final DynamicVectorRenderer currentDisplay = decode_pdf.getDynamicRenderer(); if (decode_pdf.isOpen()) { pages.init(scaling, decode_pdf.getDisplayRotation(), pageNumber, currentDisplay, true); } // force redraw pages.forceRedraw(); pages.refreshDisplay(); //needs to be realigned (in longer term refactored out) comp.pages = pages; return hasChanged; } /** * set view mode used in panel and redraw in new mode * SINGLE_PAGE,CONTINUOUS,FACING,CONTINUOUS_FACING delay is the time in * milli-seconds which scrolling can stop before background page drawing * starts Multipage views not in OS releases */ @Override public void setDisplayView(final int displayView, final int orientation) { // remove listener if setup if (viewListener != null) { removeComponentListener(viewListener); viewListener.dispose(); viewListener = null; } final boolean hasChanged = setDisplayView2(displayView, orientation); //move to correct page final int pageNumber = decode_pdf.getPageNumber(); if (pageNumber > 0) { if (hasChanged && displayView == Display.SINGLE_PAGE && decode_pdf.isOpen()) { try { decode_pdf.getPages().getYCordForPage(pageNumber, scaling); decode_pdf.setPageParameters(scaling, pageNumber, decode_pdf.getDisplayRotation()); //@swing // invalidate(); // updateUI(); decode_pdf.decodePage(pageNumber); } catch (final Exception e) { LogWriter.writeLog("Exception: " + e.getMessage()); } } else if (displayView == Display.CONTINUOUS || displayView == Display.CONTINUOUS_FACING) { try { decode_pdf.getPages().getYCordForPage(pageNumber, scaling); decode_pdf.setPageParameters(scaling, pageNumber, decode_pdf.getDisplayRotation()); decode_pdf.decodePage(pageNumber); scrollToPage(pageNumber); } catch (final Exception ex) { ex.printStackTrace(); } } } //Only all search in certain modes if (displayView != Display.SINGLE_PAGE && displayView != Display.CONTINUOUS && displayView != Display.CONTINUOUS_FACING) { enableSearchItems(false); } else { enableSearchItems(true); } // add listener if one not already there if (viewListener == null) { viewListener = new RefreshLayout(decode_pdf); addComponentListener(viewListener); } } /** * Overrides method from GUI.java, see GUI.java for DOCS. */ @Override protected void setTitle(final String title) { if (stage != null) { stage.setTitle(title); } } /* (non-Javadoc) * @see org.jpedal.examples.viewer.gui.swing.GUIFactory#resetComboBoxes(boolean) */ @Override public void resetComboBoxes(final boolean value) { if (debugFX) { System.out.println("resetComboBoxes"); } scalingBox.setEnabled(value); rotationBox.setEnabled(value); } /* (non-Javadoc) * @see org.jpedal.examples.viewer.gui.swing.GUIFactory#getCombo(int) */ @Override public GUICombo getCombo(final int ID) { if (debugFX) { System.out.println("getCombo"); } switch (ID) { case Commands.SCALING: return scalingBox; case Commands.ROTATION: return rotationBox; } return null; } /** * all scaling and rotation should go through this. */ @Override public void scaleAndRotate() { if (decode_pdf.getDisplayView() == Display.PAGEFLOW) { decode_pdf.setPageParameters(scaling, commonValues.getCurrentPage(), rotation); return; } //ignore if called too early if (!decode_pdf.isOpen() && OpenFile.isPDf) { return; } final double width = pageContainer.getViewportBounds().getWidth() * 0.95; final double height = pageContainer.getViewportBounds().getHeight() * 0.95; float tempScaling = 1.5f; // if(sudaDebug){ // System.out.println("scaleAndRotate"); // System.out.println("\tScaling "+scaling); // System.out.println("\tbar: "+pageContainer.getViewportBounds()); // System.out.println("\tGL: "+group.getBoundsInLocal()); // System.out.println("\tGP: "+group.getBoundsInParent()); // } //update value and GUI if (decode_pdf != null) { for(int i=0;i commonValues.getPageCount() || leftPage == 1) { cw *= 2; } else { cw += pageData.getCropBoxHeight(leftPage + 1); } ch = pageData.getCropBoxWidth(leftPage); if (leftPage + 1 <= commonValues.getPageCount() && ch < pageData.getCropBoxWidth(leftPage + 1)) { ch = pageData.getCropBoxWidth(leftPage + 1); } } else { cw = pageData.getCropBoxWidth(leftPage); //if first or last page double the width, otherwise add other page width if (leftPage + 1 > commonValues.getPageCount()) { cw *= 2; } else { cw += pageData.getCropBoxWidth(leftPage + 1); } ch = pageData.getCropBoxHeight(leftPage); if (leftPage + 1 <= commonValues.getPageCount() && ch < pageData.getCropBoxHeight(leftPage + 1)) { ch = pageData.getCropBoxHeight(leftPage + 1); } } break; default: if (isRotated) { cw = pageData.getCropBoxHeight(commonValues.getCurrentPage()); ch = pageData.getCropBoxWidth(commonValues.getCurrentPage()); } else { cw = pageData.getCropBoxWidth(commonValues.getCurrentPage()); ch = pageData.getCropBoxHeight(commonValues.getCurrentPage()); } } float x_factor; float y_factor; final float window_factor; x_factor = width / cw; y_factor = height / ch; if (x_factor < y_factor) { window_factor = x_factor; x_factor = -1; } else { window_factor = y_factor; y_factor = -1; } if (getSelectedComboIndex(Commands.SCALING) != 0 && ((newScaling < window_factor * 1.1 && newScaling > window_factor * 0.91) || ((window_factor > scaling && window_factor < newScaling) || (window_factor < scaling && window_factor > newScaling)))) { setSelectedComboIndex(Commands.SCALING, 0); scaling = window_factor; } else if (y_factor != -1 && getSelectedComboIndex(Commands.SCALING) != 1 && ((newScaling < y_factor * 1.1 && newScaling > y_factor * 0.91) || ((y_factor > scaling && y_factor < newScaling) || (y_factor < scaling && y_factor > newScaling)))) { setSelectedComboIndex(Commands.SCALING, 1); scaling = y_factor; } else if (x_factor != -1 && getSelectedComboIndex(Commands.SCALING) != 2 && ((newScaling < x_factor * 1.1 && newScaling > x_factor * 0.91) || ((x_factor > scaling && x_factor < newScaling) || (x_factor < scaling && x_factor > newScaling)))) { setSelectedComboIndex(Commands.SCALING, 2); scaling = x_factor; } else { setSelectedComboItem(Commands.SCALING, String.valueOf((int) decode_pdf.getDPIFactory().removeScaling(newScaling * 100))); scaling = newScaling; } } /* (non-Javadoc) * @see org.jpedal.examples.viewer.gui.swing.GUIFactory#rotate() */ @Override public void rotate() { rotation = Integer.parseInt((String) getSelectedComboItem(Commands.ROTATION)); scaleAndRotate(); } @Override public void scrollToPage(final int page) { if (debugFX) { System.out.println("scrollToPage"); } commonValues.setCurrentPage(page); if (commonValues.getCurrentPage() > 0) { //int xCord = 0; final int pageCount = decode_pdf.getPageCount(); double yCord;// = decode_pdf.getPages().getYCordForPage(page, scaling); //$$$$$$$$$$$$$ call it and ignore please dont delete as it has affect in continuous mode decode_pdf.getPages().getYCordForPage(page, scaling); // if (decode_pdf.getDisplayView() != Display.SINGLE_PAGE) { // xCord = 0; //} //System.out.println("Before="+decode_pdf.getVisibleRect()+" "+decode_pdf.getPreferredSize()); final PdfPageData pageData = decode_pdf.getPdfPageData(); //final int ch = (int) (pageData.getCropBoxHeight(page) * scaling); //final int cw = (int) (pageData.getCropBoxWidth(page) * scaling); double totalHeight = 0; //int pw = pageData.getScaledCropBoxWidth(page); //int ph = pageData.getScaledCropBoxHeight(page); //double localH = pageContainer.getContent().getBoundsInLocal().getHeight(); //double localW = pageContainer.getContent().getBoundsInLocal().getWidth(); //double viewH = pageContainer.getViewportBounds().getHeight(); //double viewW = pageContainer.getViewportBounds().getWidth(); final double vv; switch (decode_pdf.getDisplayView()) { case Display.SINGLE_PAGE: break; case Display.CONTINUOUS: yCord = 0; for (int i = 1; i < pageCount; i++) { totalHeight+= pageData.getScaledCropBoxHeight(i); } for (int i = 1; i < page; i++) { yCord+= pageData.getScaledCropBoxHeight(i); } vv = yCord/totalHeight; pageContainer.setVvalue(vv);// break; case Display.CONTINUOUS_FACING: yCord = 0; for (int i = 1; i < pageCount; i+=2) { totalHeight+= pageData.getScaledCropBoxHeight(i); } for (int i = 1; i < page; i+=2) { yCord+= pageData.getScaledCropBoxHeight(i); } vv = yCord/totalHeight; pageContainer.setVvalue(vv);// break; } } if (decode_pdf.getPageCount() > 1) { fxButtons.setPageLayoutButtonsEnabled(true); } } @Override public void setupThumbnailPanel() { if (debugFX) { System.out.println("setupThumbnailPanel"); } decode_pdf.addExternalHandler(thumbnails, Options.ThumbnailHandler); if (isSetup) { return; } isSetup = true; if (thumbnails.isShownOnscreen()) { //setup and add to display thumbnails.setupThumbnails(decode_pdf.getPageCount(), null, decode_pdf.getPdfPageData()); //add listener so clicking on button changes to page - has to be in Viewer so it can update it final Button[] buttons = (Button[]) thumbnails.getButtons(); for (int i = 0; i < commonValues.getPageCount(); i++) { buttons[i].setOnAction(new JavaFXPageChanger(this, commonValues, i)); } //add global listener thumbnails.addComponentListener(); } } @Override public void setBookmarks(final boolean alwaysGenerate) { if (debugFX) { System.out.println("setBookmarks"); //ignore if not opened System.out.println("currentSize in setBookmarks() in JavaFXGUI requires JavaFX implementation"); } // int currentSize=center.getDividerPosition(); // // if((currentSize==startSize)&& !alwaysGenerate) // return; //ignore if already done and flag if (bookmarksGenerated) { return; } bookmarksGenerated = true; final org.w3c.dom.Document doc = decode_pdf.getOutlineAsXML(); Node rootNode = null; if (doc != null) { rootNode = doc.getFirstChild(); } if (rootNode != null) { tree.reset(rootNode); // Allows an already selected node to be clicked again final EventHandler reselectionListener = new EventHandler() { @Override public void handle(final MouseEvent event) { final String ref = ((JavaFXOutline.OutlineNode) ((javafx.scene.Node) event.getSource()).getUserData()).getObjectRef(); gotoPageByRef(ref); } }; ((TreeView