/*
* Copyright (c) 1997-2025 IDRsolutions (https://www.idrsolutions.com)
*/
package org.jpedal.examples.viewer.gui;
import com.idrsolutions.image.JDeli;
import org.jpedal.FileAccess;
import org.jpedal.FileAccessHelper;
import org.jpedal.PdfDecoder;
import org.jpedal.PdfDecoderInt;
import org.jpedal.constants.JPedalSettings;
import org.jpedal.display.Display;
import org.jpedal.display.GUIDisplay;
import org.jpedal.display.PageOffsets;
import org.jpedal.display.swing.MultiDisplay;
import org.jpedal.display.swing.SingleDisplay;
import org.jpedal.examples.viewer.Commands;
import org.jpedal.examples.viewer.RecentDocuments;
import org.jpedal.examples.viewer.Values;
import org.jpedal.examples.viewer.ViewerCommands;
import org.jpedal.examples.viewer.commands.OpenFile;
import org.jpedal.examples.viewer.commands.PageNavigator;
import org.jpedal.examples.viewer.gui.popups.PrintPanel;
import org.jpedal.examples.viewer.gui.swing.ColorListCellRenderer;
import org.jpedal.examples.viewer.gui.swing.FrameCloser;
import org.jpedal.examples.viewer.gui.swing.SearchList;
import org.jpedal.examples.viewer.gui.swing.SwingAnnotationPanel;
import org.jpedal.examples.viewer.gui.swing.SwingAttachmentsPanel;
import org.jpedal.examples.viewer.gui.swing.SwingCombo;
import org.jpedal.examples.viewer.gui.swing.SwingCommandListener;
import org.jpedal.examples.viewer.gui.swing.SwingLayersPanel;
import org.jpedal.examples.viewer.gui.swing.SwingMenuItems;
import org.jpedal.examples.viewer.gui.swing.SwingMouseListener;
import org.jpedal.examples.viewer.gui.swing.SwingMouseUtils;
import org.jpedal.examples.viewer.gui.swing.SwingPageChanger;
import org.jpedal.examples.viewer.gui.swing.SwingPortfolioDetail;
import org.jpedal.examples.viewer.gui.swing.SwingPortfolioTile;
import org.jpedal.examples.viewer.gui.swing.SwingScrollListener;
import org.jpedal.examples.viewer.gui.swing.SwingSearchWindow;
import org.jpedal.examples.viewer.gui.swing.SwingSignaturesPanel;
import org.jpedal.examples.viewer.gui.swing.SwingThumbnailPanel;
import org.jpedal.examples.viewer.paper.PaperSizes;
import org.jpedal.examples.viewer.utils.LimitDecodeTrackerNoSwing;
import org.jpedal.examples.viewer.utils.PropertiesFile;
import org.jpedal.exception.PdfException;
import org.jpedal.external.AnnotationHandler;
import org.jpedal.external.CustomMessageHandler;
import org.jpedal.external.ExternalHandlers;
import org.jpedal.external.ImageNameCollector;
import org.jpedal.external.OffsetOptions;
import org.jpedal.external.Options;
import org.jpedal.external.RenderChangeListener;
import org.jpedal.fonts.tt.TTGlyph;
import org.jpedal.gui.CommandWindow;
import org.jpedal.io.LimitDecodeTracker;
import org.jpedal.io.PageLabels;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.io.StatusBar;
import org.jpedal.linear.LinearThread;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.acroforms.AcroRenderer;
import org.jpedal.objects.acroforms.actions.ActionHandler;
import org.jpedal.objects.acroforms.creation.FormFactory;
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.CommandParser;
import org.jpedal.parser.DecodeStatus;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.parser.text.Tj;
import org.jpedal.render.BaseDisplay;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.Messages;
import org.jpedal.utils.StringUtils;
import org.w3c.dom.Node;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.RootPaneContainer;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.border.AbstractBorder;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.NumberFormatter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.StringSelection;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URL;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimerTask;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <br>Description: Swing GUI functions in Viewer
*/
public class SwingGUI {
public static final int BUTTONBAR = 0;
public static final int NAVBAR = 1;
public static final int PAGES = 2;
public static final String version;
public static final int CURSOR = 1;
/**
* grabbing cursor
*/
public static final int GRAB_CURSOR = 1;
public static final int GRABBING_CURSOR = 2;
public static final int DEFAULT_CURSOR = 3;
public static final int PAN_CURSOR = 4;
public static final int PAN_CURSORL = 5;
public static final int PAN_CURSORTL = 6;
public static final int PAN_CURSORT = 7;
public static final int PAN_CURSORTR = 8;
public static final int PAN_CURSORR = 9;
public static final int PAN_CURSORBR = 10;
public static final int PAN_CURSORB = 11;
public static final int PAN_CURSORBL = 12;
/**
* Constants for glowing border
*/
private static final int glowThickness = 11;
/**
* minimum screen width to ensure menu buttons are visible
*/
private static final int minimumScreenWidth = 700;
public static boolean deletePropertiesOnExit;
public static boolean alwaysShowMouse;
/**
* control if messages appear
*/
public static boolean showMessages = true;
static int expandedSize = 190;
private static int collapsedSize = 30;
/**
* padding so that the pdf is not right at the edge
*/
private static int inset = 25;
/**
* default scaling on the combobox scalingValues
*/
private static int defaultSelection;
private static String windowTitle;
static {
final Properties props = new Properties();
try (InputStream is = SwingGUI.class.getResourceAsStream("/version.num")) {
if (is != null) {
try {
props.load(is);
} catch (final IOException ex) {
LogWriter.writeLog("Exception: " + ex.getMessage());
}
}
} catch (final IOException e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
final String versionSet = props.getProperty("release");
if (versionSet != null) {
version = versionSet;
} else {
version = "@VERSION@";
}
}
private final Color glowOuterColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
private final Color glowInnerColor = new Color(0.8f, 0.75f, 0.45f, 0.8f);
private final Values commonValues;
private final SwingThumbnailPanel thumbnails;
private final PropertiesFile properties;
private RecentDocuments recent;
/**
* listener on buttons, menus, combboxes to execute options (one instance on all objects)
*/
private SwingCommandListener currentCommandListener;
private Commands currentCommands;
//allow user to control messages in Viewer
private CustomMessageHandler customMessageHandler;
//setup in init so we can pass in some objects
private SwingMenuItems menuItems;
//layers tab
private PdfLayerList layersObject;
private boolean finishedDecoding;
private Font textFont = new Font("Serif", Font.PLAIN, 12);
//flag if generated so we setup once for each file
private boolean bookmarksGenerated;
private SwingSearchWindow searchFrame;
private String pageTitle;
private String bookmarksTitle;
private String signaturesTitle;
private String layersTitle;
private String annotationTitle;
private String attachmentsTitle;
/**
* handle for internal use
*/
private PdfDecoderInt decode_pdf;
/**
* XML structure of bookmarks
*/
private SwingOutline tree;
/**
* scaling values as floats to save conversion
*/
private float[] scalingFloatValues = {1.0f, 1.0f, 1.0f, .25f, .5f, .75f, 1.0f, 1.25f, 1.5f, 2.0f, 2.5f, 5.0f, 7.5f, 10.0f};
/**
* page scaling to use 1=100%
*/
private float scaling = 1;
/**
* store page rotation
*/
private int rotation;
/**
* scaling factors on the page
*/
private SwingCombo rotationBox;
/**
* scaling factors on the page
*/
private SwingCombo scalingBox;
private RefreshLayout viewListener;
private final Buttons swButtons = new Buttons();
//GUICursor object that holds everything to do with Cursor for SwingGUI
private final SwingCursor guiCursor = new SwingCursor();
//all mouse actions
private SwingMouseListener mouseHandler;
private Timer memoryMonitor;
private SwingScrollListener scrollListener;
//This scroll bar allows for page navigation via the scroll bar/wheel when the page
//fits within the dimensions of the viewable area
private JScrollBar thumbscroll;
//Creates the top toolbar
private final JPanel top;
private boolean sideTabBarOpenByDefault;
private String startSelectedTab = "";
//use new GUI layout
private boolean hasListener;
private boolean isSetup;
private int lastTabSelected = -1;
private boolean tabsExpanded;
private PaperSizes paperSizes;
//Multibox for new GUI Layout to contain memory, cursor and loading bars
private final JPanel multibox;
//Track whether both pages are properly displayed
private boolean pageTurnScalingAppropriate = true;
//holds back/forward buttons at bottom of page
private JToolBar navButtons;
//hold the thumbnails for display panes
private final JPanel containerForThumbnails;
// visual display of current cursor co-ords on page
private JLabel coords;
//root element to hold display
private Container frame;
//element to add portfolio display to
private Container portfolioParent;
//displayed on left to hold thumbnails, bookmarks
private JTabbedPane navOptionsPanel;
//split display between PDF and thumbnails/bookmarks
private JSplitPane displayPane;
//Scrollpane for pdf panel
private JScrollPane scrollPane;
//Interactive display object - needs to be added to PdfDecoder
private StatusBar statusBar;
private StatusBar downloadBar;
private JLabel pageCounter1;
private JTextField pageCounter2;
private JLabel pageCounter3;
private SwingSignaturesPanel signaturesTree;
private SwingLayersPanel layersPanel;
private SwingAnnotationPanel annotationsPanel;
private SwingPortfolioDetail detailView;
private SwingPortfolioTile tileView;
private SwingAttachmentsPanel attachmentsPanel;
//stop user forcing open tab before any pages loaded
private boolean tabsNotInitialised = true;
private JToolBar navToolBar;
private JToolBar pagesToolBar;
//Progress bar on nav bar
private final JProgressBar memoryBar;
//Component to display cursor position on page
private final JToolBar cursor;
private PrintPanel printPanel;
private JPanel glassPane;
private boolean searchInMenu;
private JTextField searchText;
private JToggleButton options;
private JPopupMenu menu;
private boolean cursorOverPage;
private final JLayeredPane lpane;
private Map<FormObject, String> objs;
private long start = System.currentTimeMillis();
/**
* stops autoscrolling at screen edge
*/
private boolean allowScrolling = true;
/**
* confirms exit when closing the window
*/
private boolean confirmClose;
private boolean debugMode;
private ImageNameCollector imageNameCollector = new ImageNameCollector() {};
/**
* Create AWT listener to handle coords regardless of component beneath
*/
final CoordinateListener coordsListener = new CoordinateListener();
/**
* Create Component listener to handle redrawing page on component resize
*/
final ResizeRedrawListener resizeRedrawListener = new ResizeRedrawListener();
/**
* Create the user interface for the Viewer example.
*
* @param decode_pdf the object that decodes the pdf
* @param commonValues all common values
* @param thumbnails the panel displaying the thumbnails
* @param properties all properties of the viewer
*/
public SwingGUI(final PdfDecoderInt decode_pdf, final Values commonValues, final SwingThumbnailPanel thumbnails, final PropertiesFile properties) {
this.decode_pdf = decode_pdf;
this.commonValues = commonValues;
this.thumbnails = thumbnails;
this.properties = properties;
statusBar = new StatusBar(new Color(235, 154, 0));
downloadBar = new StatusBar(new Color(185, 209, 0));
//Initialise the java components
top = new JPanel();
multibox = new JPanel();
navButtons = new JToolBar();
containerForThumbnails = new JPanel();
coords = new JLabel();
frame = new JFrame();
portfolioParent = ((RootPaneContainer) frame).getContentPane();
navOptionsPanel = new JTabbedPane();
pageCounter2 = new JTextField(4);
pageCounter3 = new JLabel();
memoryBar = new JProgressBar();
cursor = new JToolBar();
navToolBar = new JToolBar();
pagesToolBar = new JToolBar();
signaturesTree = new SwingSignaturesPanel();
layersPanel = new SwingLayersPanel();
scrollPane = new JScrollPane();
tree = new SwingOutline();
lpane = new JLayeredPane() {
@Override
public void doLayout() {
synchronized (getTreeLock()) {
final int w = getWidth();
final int h = getHeight();
for (final Component c : getComponents()) {
c.setBounds(0, 0, w, h);
}
}
}
};
setupDisplay();
}
public static int getPDFDisplayInset() {
return inset;
}
private static void setHHighlightComposite(final String propValue) {
float value = Float.parseFloat(propValue);
if (value > 1) {
value = 1;
}
if (value < 0) {
value = 0;
}
DecoderOptions.highlightComposite = value;
}
private static void getFlattenedTreeNodes(final TreeNode theNode, final List<TreeNode> items) {
// add the item
items.add(theNode);
// recursion
final int count = theNode.getChildCount();
for (int i = 0; i < count; i++) {
getFlattenedTreeNodes(theNode.getChildAt(i), items);
}
}
/**
* setup display
*/
private void setupDisplay() {
if (SwingUtilities.isEventDispatchThread()) {
setDisplayView(Display.SINGLE_PAGE, Display.DISPLAY_CENTERED);
} else {
final Runnable doPaintComponent = () -> setDisplayView(Display.SINGLE_PAGE, Display.DISPLAY_CENTERED);
try {
SwingUtilities.invokeAndWait(doPaintComponent);
} catch (InterruptedException | InvocationTargetException e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
}
//pass in SwingGUI so we can call via callback
decode_pdf.addExternalHandler(this, Options.GUIContainer);
//pass properties into Swing Menu items
menuItems = new SwingMenuItems(properties);
}
/**
* Returns the split pane used in the display area of the viewer.
*
* @return JSplitPane used to hold the page display and the side tab bar
*/
public JSplitPane getDisplayPane() {
return displayPane;
}
/**
* Returns the scroll bar used in single page mode that shows page thumbnails as you scroll
*
* @return JScrollBar used for the thumbnail scroll in single page mode
*/
public JScrollBar getThumbnailScrollBar() {
return thumbscroll;
}
/**
* Control thumbnail scroll bars visibility
*
* @param isVisible boolean value to control visibility
*/
public void setThumbnailScrollBarVisibility(final boolean isVisible) {
thumbscroll.setVisible(isVisible);
}
/**
* Set the position of the scroll bar based on desired page number
*
* @param pageNum int value for the page
*/
public void setThumbnailScrollBarValue(final int pageNum) {
thumbscroll.setValue(pageNum);
}
/**
* Gets the file path for the properties file used in the viewer.
*
* @return String containing the properties file name
*/
public String getPropertiesFileLocation() {
return properties.getConfigFile();
}
/**
* Gets the page number of the given bookmark
*
* @param bookmark String value of the bookmark
* @return String value of the page number
*/
public String getBookmark(final String bookmark) {
return tree.getPage(bookmark);
}
/**
* Set the side tab bar state and set any values required for that state.
*
* @param showVisible true to expand the side tab bar, false to collapse
*/
public void reinitialiseTabs(final boolean showVisible) {
if ("true".equalsIgnoreCase(properties.getValue("ShowSidetabbar"))) {
if (!showVisible && !"true".equalsIgnoreCase(properties.getValue("consistentTabBar"))) {
if (sideTabBarOpenByDefault) {
displayPane.setDividerLocation(expandedSize);
tabsExpanded = true;
} else {
displayPane.setDividerLocation(collapsedSize);
tabsExpanded = false;
navOptionsPanel.setSelectedIndex(-1);
}
}
lastTabSelected = -1;
if (commonValues.isPDF()) {
setupPdfDependantTabs();
}
if (tabsNotInitialised) {
navOptionsPanel.setSelectedIndex(-1);
for (int i = 0; i != navOptionsPanel.getTabCount(); i++) {
final String name;
if (DecoderOptions.isRunningOnMac) {
name = navOptionsPanel.getTitleAt(i);
} else {
name = navOptionsPanel.getIconAt(i).toString();
}
if (name != null && name.equals(startSelectedTab)) {
navOptionsPanel.setSelectedIndex(i);
break;
}
}
//allow user to now open tabs
tabsNotInitialised = false;
}
}
}
private void setupPdfDependantTabs() {
//add/remove optional tabs
if (!decode_pdf.hasOutline()) {
final int outlineTab = getTabIndex(bookmarksTitle);
if (outlineTab != -1) {
navOptionsPanel.remove(outlineTab);
}
} else if ("true".equalsIgnoreCase(properties.getValue("Bookmarkstab"))) {
final int outlineTab = getTabIndex(bookmarksTitle);
if (outlineTab == -1) {
if (DecoderOptions.isRunningOnMac && UIManager.getLookAndFeel().isNativeLookAndFeel()) {
navOptionsPanel.addTab(bookmarksTitle, tree);
} else {
final VTextIcon textIcon2 = new VTextIcon(navOptionsPanel, bookmarksTitle);
navOptionsPanel.addTab(null, textIcon2, tree);
}
}
}
//handle signatures pane
final AcroRenderer currentFormRenderer = decode_pdf.getFormRenderer();
Iterator<FormObject> signatureObjects = null;
if (currentFormRenderer != null) {
signatureObjects = currentFormRenderer.getSignatureObjects();
}
if (signatureObjects != null) {
signaturesTree.reinitialise(decode_pdf, signatureObjects);
checkTabShown(signaturesTitle);
} else {
removeTab(signaturesTitle);
}
//add a control Panel to enable/disable layers
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, scrollPane, commonValues.getCurrentPage());
} else {
removeTab(layersTitle);
}
final PdfObjectReader reader = decode_pdf.getIO();
Object[] embeddedFiles = null;
if (reader != null) {
if (reader.getNamesLookup() != null) {
embeddedFiles = reader.getNamesLookup().getEmbeddedFiles();
}
if (attachmentsPanel == null) {
attachmentsPanel = new SwingAttachmentsPanel(this);
}
//Always load values even when null as this method also loads values stored in annotations
if (attachmentsPanel.loadEmbeddedFileData(decode_pdf, embeddedFiles)) {
checkTabShown(attachmentsTitle);
} else {
removeTab(attachmentsTitle);
}
}
if (decode_pdf.getDisplayView() == Display.SINGLE_PAGE && !decode_pdf.isEncrypted()) {
checkTabShown(annotationTitle);
} else {
removeTab(annotationTitle);
}
setBookmarks(false);
}
private int getTabIndex(final String title) {
final int tabCount = navOptionsPanel.getTabCount();
for (int i = 0; i < tabCount; i++) {
final Icon icon = navOptionsPanel.getIconAt(i);
if (icon != null && icon.toString().equals(title)) {
return i;
}
final String t = navOptionsPanel.getTitleAt(i);
if (DecoderOptions.isRunningOnMac && t != null && t.equals(title)) {
return i;
}
}
return -1;
}
private void checkTabShown(final String title) {
final int outlineTab = getTabIndex(title);
if (outlineTab == -1) {
if (DecoderOptions.isRunningOnMac && UIManager.getLookAndFeel().isNativeLookAndFeel()) {
if (title.equals(signaturesTitle) && "true".equalsIgnoreCase(properties.getValue("Signaturestab"))) {
navOptionsPanel.addTab(signaturesTitle, signaturesTree);
navOptionsPanel.setTitleAt(navOptionsPanel.getTabCount() - 1, signaturesTitle);
} else if (title.equals(layersTitle) && "true".equalsIgnoreCase(properties.getValue("Layerstab"))) {
final JScrollPane layerScrollPane = new JScrollPane();
layerScrollPane.getViewport().add(layersPanel);
layerScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
layerScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
navOptionsPanel.addTab(layersTitle, layerScrollPane);
navOptionsPanel.setTitleAt(navOptionsPanel.getTabCount() - 1, layersTitle);
} else if (title.equals(annotationTitle) && "true".equalsIgnoreCase(properties.getValue("AnnotationTab")) && SwingAnnotationPanel.addPanel()) {
navOptionsPanel.addTab(annotationTitle, annotationsPanel.getDisplayPanel());
navOptionsPanel.setTitleAt(navOptionsPanel.getTabCount() - 1, annotationTitle);
} else if (title.equals(attachmentsTitle) && "true".equalsIgnoreCase(properties.getValue("AttachmentsTab"))) {
navOptionsPanel.addTab(attachmentsTitle, attachmentsPanel);
navOptionsPanel.setTitleAt(navOptionsPanel.getTabCount() - 1, attachmentsTitle);
}
} else {
if (title.equals(signaturesTitle) && "true".equalsIgnoreCase(properties.getValue("Signaturestab"))) { //stop spurious display of Sig tab
final VTextIcon textIcon2 = new VTextIcon(navOptionsPanel, signaturesTitle);
navOptionsPanel.addTab(null, textIcon2, signaturesTree);
} else if (title.equals(layersTitle) && "true".equalsIgnoreCase(properties.getValue("Layerstab"))) {
final VTextIcon textIcon = new VTextIcon(navOptionsPanel, layersTitle);
final JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport().add(layersPanel);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
navOptionsPanel.addTab(null, textIcon, scrollPane);
} else if (title.equals(annotationTitle) && "true".equalsIgnoreCase(properties.getValue("AnnotationTab")) && SwingAnnotationPanel.addPanel()) {
final VTextIcon textIcon = new VTextIcon(navOptionsPanel, annotationTitle);
navOptionsPanel.addTab(null, textIcon, annotationsPanel.getDisplayPanel());
} else if (title.equals(attachmentsTitle) && "true".equalsIgnoreCase(properties.getValue("AttachmentsTab"))) {
final VTextIcon textIcon = new VTextIcon(navOptionsPanel, attachmentsTitle);
navOptionsPanel.addTab(null, textIcon, attachmentsPanel);
}
}
}
}
private void removeTab(final String title) {
final int outlineTab = getTabIndex(title);
if (outlineTab != -1) {
navOptionsPanel.remove(outlineTab);
}
}
/**
* Stop thumbnails from being generated for the side tab bar
*/
public void stopThumbnails() {
if (thumbnails.isShownOnscreen()) {
//if running terminate first
thumbnails.terminateDrawing();
thumbnails.removeAllListeners();
}
}
/**
* Flag the thumbnails tab as requires reinitialisation
*/
public void reinitThumbnails() {
isSetup = false;
}
/**
* Reset the nav bar and collapse side tab bar
*/
public void resetNavBar() {
if (!"true".equalsIgnoreCase(properties.getValue("consistentTabBar"))) {
displayPane.setDividerLocation(collapsedSize);
navOptionsPanel.setSelectedIndex(-1);
tabsNotInitialised = true;
}
//also reset layers
layersPanel.resetLayers();
//disable page view buttons until we know we have multiple pages
swButtons.setPageLayoutButtonsEnabled(false);
}
/*
* Called when new file opened so we set flags here
*/
public void setNoPagesDecoded() {
bookmarksGenerated = false;
resetNavBar();
//Ensure preview from last file doesn't appear
if (scrollListener != null) {
scrollListener.lastImage = null;
}
}
/**
* Gets the thumbnail panel.
*
* @return SwingThumbnailPanel instance
*/
public SwingThumbnailPanel getThumbnailPanel() {
return thumbnails;
}
/**
* Gets the SwingOutline used for the outline panel
*
* @return SwingOutline
*/
public SwingOutline getOutlinePanel() {
return tree;
}
/**
* Gets the current vertical scroll bar. This will be the thumbnail scroll bar
* if it is currently present, otherwise it will be the scrollpanes scroll bar.
*
* @return a JScrollBar representing the scroll bar
*/
public JScrollBar getVerticalScrollBar() {
if (scrollPane.getVerticalScrollBar().isVisible()) {
return scrollPane.getVerticalScrollBar();
} else {
return thumbscroll;
}
}
/**
* Sets the scroll bar policy for the display areas scroll pane
*
* @param pol ScrollPolicy to be applied to the scroll pane
*/
public void setScrollBarPolicy(final ScrollPolicy pol) {
switch (pol) {
case VERTICAL_NEVER:
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
break;
case VERTICAL_AS_NEEDED:
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
break;
case HORIZONTAL_NEVER:
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
break;
case HORIZONTAL_AS_NEEDED:
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
break;
}
}
/**
* Specify a Container for the viewers interface to be added to.
*
* @param rawValue an Object that extends container to add the user interface to.
*/
public void setRootContainer(final Object rawValue) {
if (rawValue == null) {
throw new RuntimeException("Null containers not allowed.");
}
final Container rootContainer = (Container) rawValue;
Container c = rootContainer;
if ((rootContainer instanceof JTabbedPane)) {
final JPanel temp = new JPanel(new BorderLayout());
rootContainer.add(temp);
c = temp;
} else if (rootContainer instanceof final JScrollPane scroll) {
final JPanel temp = new JPanel(new BorderLayout());
scroll.getViewport().add(temp);
c = temp;
} else if (rootContainer instanceof JSplitPane) {
throw new RuntimeException("To add the viewer to a split pane please pass through either JSplitPane.getLeftComponent() or JSplitPane.getRightComponent()");
}
if (rootContainer instanceof JFrame) {
portfolioParent = ((RootPaneContainer) rootContainer).getContentPane();
} else {
c.setLayout(new BorderLayout());
portfolioParent = c;
}
//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 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
if (width < 0) {
width = d.width / 2;
if (width < 700) {
width = 700;
}
properties.setValue("startViewerWidth", String.valueOf(width));
}
if (height < 0) {
height = d.height / 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");
}
try {
width = Integer.parseInt(values.nextToken().trim());
height = Integer.parseInt(values.nextToken().trim());
} catch (final NumberFormatException ee) {
throw new RuntimeException("Unable to use value for org.jpedal.startWindowSize=" + customWindowSize + "\nValue should be in format org.jpedal.startWindowSize=200x300 " + ee);
}
}
c.setPreferredSize(new Dimension(width, height));
frame = c;
}
public void 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()) {
decode_pdf.repaint();
}
}
public PropertiesFile getProperties() {
return properties;
}
/**
* Set Search Bar to be in the Left hand Tabbed pane
*
* @param searchFrame the search window that will be add to the side tab bar
*/
public void searchInTab(final SwingSearchWindow searchFrame) {
this.searchFrame = searchFrame;
this.searchFrame.init(decode_pdf, commonValues);
if (DecoderOptions.isRunningOnMac && UIManager.getLookAndFeel().isNativeLookAndFeel()) {
if (thumbnails.isShownOnscreen()) {
navOptionsPanel.addTab("Search", searchFrame.getContentPane());
}
} else {
final VTextIcon textIcon2 = new VTextIcon(navOptionsPanel, "Search");
navOptionsPanel.addTab(null, textIcon2, searchFrame.getContentPane());
}
//Create menu bar text field to trigger side tab bar
final JTextField searchTextLocal = new JTextField();
searchTextLocal.setColumns(searchText.getColumns());
searchTextLocal.setText(searchText.getText());
//Get last listener as all listeners added to the end of the array
searchTextLocal.addKeyListener(searchText.getKeyListeners()[searchText.getKeyListeners().length - 1]);
searchTextLocal.addFocusListener(searchText.getFocusListeners()[searchText.getFocusListeners().length - 1]);
searchTextLocal.addActionListener(e -> {
navOptionsPanel.setSelectedComponent(searchFrame.getContentPane());
if (!tabsExpanded) {
displayPane.setDividerLocation(expandedSize);
tabsExpanded = true;
thumbnails.setIsDisplayedOnscreen(false);
}
searchText.setText(searchTextLocal.getText());
});
searchText.addActionListener(e -> searchTextLocal.setText(searchText.getText()));
swButtons.getTopButtons().add(searchTextLocal);
}
private JToggleButton createMenuBarSearchOptions() {
if (options == null) {
options = new JToggleButton(new ImageIcon(guiCursor.getURLForImage("menuSearchOptions.png")));
menu = new JPopupMenu();
options.setBorder(null);
options.setName("SEARCHOPTIONS");
options.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
menu.show(((Component) e.getSource()), 0, ((Component) e.getSource()).getHeight());
}
});
options.setFocusable(false);
options.setToolTipText(Messages.getMessage("PdfViewerSearch.Options"));
final JCheckBoxMenuItem wholeWords = new JCheckBoxMenuItem(Messages.getMessage("PdfViewerSearch.WholeWords"));
wholeWords.setToolTipText(Messages.getMessage("PdfViewerSearch.WholeWordsTooltip"));
wholeWords.addActionListener(e -> {
searchFrame.setWholeWords(((AbstractButton) e.getSource()).isSelected());
enableSearchItems(true);
});
final JCheckBoxMenuItem caseSense = new JCheckBoxMenuItem(Messages.getMessage("PdfViewerSearch.CaseSense"));
caseSense.setToolTipText(Messages.getMessage("PdfViewerSearch.CaseSenseTooltip"));
caseSense.addActionListener(e -> {
searchFrame.setCaseSensitive(((AbstractButton) e.getSource()).isSelected());
enableSearchItems(true);
});
final JCheckBoxMenuItem multiLine = new JCheckBoxMenuItem(Messages.getMessage("PdfViewerSearch.MultiLine"));
multiLine.setToolTipText(Messages.getMessage("PdfViewerSearch.MultiLineTooltip"));
multiLine.addActionListener(e -> {
searchFrame.setMultiLine(((AbstractButton) e.getSource()).isSelected());
enableSearchItems(true);
});
final JCheckBoxMenuItem ignoreSpaces = new JCheckBoxMenuItem(Messages.getMessage("PdfViewerSearch.IgnoreSpaces"));
ignoreSpaces.setToolTipText(Messages.getMessage("PdfViewerSearch.IgnoreSpacesTooltip"));
ignoreSpaces.addActionListener(e -> {
searchFrame.setIgnoreSpaces(((AbstractButton) e.getSource()).isSelected());
enableSearchItems(true);
});
menu.add(wholeWords);
menu.add(caseSense);
menu.add(multiLine);
menu.add(ignoreSpaces);
menu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
//We do not need to perform an action before making popup visible
}
@Override
public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
options.setSelected(false);
}
@Override
public void popupMenuCanceled(final PopupMenuEvent e) {
options.setSelected(false);
}
});
}
return options;
}
/*
* Set Search Bar to be in the Top Button Bar
*/
private void searchInMenu(final SwingSearchWindow searchFrame) {
this.searchFrame = searchFrame;
searchInMenu = true;
searchFrame.find(decode_pdf, commonValues);
swButtons.getTopButtons().add(searchText);
swButtons.getTopButtons().add(createMenuBarSearchOptions());
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerSearch.Previous"), "search_previous.gif", Commands.PREVIOUSRESULT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerSearch.Next"), "search_next.gif", Commands.NEXTRESULT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.getButton(Commands.NEXTRESULT).setEnabled(false);
swButtons.getButton(Commands.PREVIOUSRESULT).setEnabled(false);
swButtons.getButton(Commands.NEXTRESULT).setVisible(true);
swButtons.getButton(Commands.PREVIOUSRESULT).setVisible(true);
}
/**
* Sets the properties file to be used
*
* @param file String file name for the properties file to be used
*/
public void setPropertiesFileLocation(final String file) {
properties.loadProperties(file);
}
/**
* Gets the Commands object used by the user interface
*
* @return Commands object for executing commands
*/
public Commands getCommand() {
return currentCommands;
}
/**
* Gets the annotation tabs panel if one is present
*
* @return an implementation of SwingAnnotationPanel
*/
public SwingAnnotationPanel getAnnotationPanel() {
return annotationsPanel;
}
@SuppressWarnings({"OverlyLongMethod", "java:S138"})
public void init(final Commands currentCommands) {
//single listener to execute all commands
currentCommandListener = new SwingCommandListener(currentCommands);
//setup custom message and switch off error messages if used
customMessageHandler = (CustomMessageHandler) (decode_pdf.getExternalHandler(Options.CustomMessageOutput));
if (customMessageHandler != null) {
DecoderOptions.showErrorMessages = false;
showMessages = false;
}
this.currentCommands = currentCommands;
initProperties();
annotationsPanel = new SwingAnnotationPanel(this);
/*
* arrange insets
*/
decode_pdf.setInset(inset, inset);
setViewerTitle();
setViewerIcon();
setDefaultPdfDecoderParameters();
//Add Background color to the panel to help break up view.
setupCenterPanelBackground();
//setup combo boxes.
setupComboBoxes();
if (LogWriter.isRunningFromIDE) {
nameElements();
}
//add the pdf display to show page.
setupPDFDisplayPane();
//add the pdf display left and right panes.
setupBorderPanes();
//Initialise the Swing Buttons
swButtons.init(true);
//create a menu bar and add to display
createTopMenuBar();
//set colours on display boxes and add listener to page number
setupBottomToolBarItems();
//create other tool bars and add to display
createOtherToolBars();
initButtonsAndMenu();
//navigation toolbar for moving between pages
createNavbar();
//add cursor location
initCoordBox();
if (searchFrame != null && searchFrame.getViewStyle() == SwingSearchWindow.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
frame.invalidate();
frame.validate();
frame.repaint();
//Load properties
GUIModifier.load(properties, this);
initViewerWindow();
redrawDocumentOnResize();
setupPDFBorder();
//Set side tab bar state at start up
if (sideTabBarOpenByDefault) {
displayPane.setDividerLocation(expandedSize);
tabsExpanded = true;
} else {
displayPane.setDividerLocation(collapsedSize);
tabsExpanded = false;
navOptionsPanel.setSelectedIndex(-1);
}
mouseHandler = new SwingMouseListener((PdfDecoder) decode_pdf, this, commonValues, currentCommands);
mouseHandler.setupMouse();
final long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK;
//Add universal listener to catch coords updating regardless of form component
Toolkit.getDefaultToolkit().addAWTEventListener(coordsListener, eventMask);
if (LogWriter.isRunningFromIDE && !debugMode) {
final LimitDecodeTracker decodeLimiter = new LimitDecodeTracker(menuItems, (PdfDecoder) decode_pdf);
decode_pdf.addExternalHandler(decodeLimiter, Options.ErrorTracker);
}
}
private void setDefaultPdfDecoderParameters() {
final boolean separateCoverOn = "true".equalsIgnoreCase(properties.getValue("separateCoverOn"));
decode_pdf.getPages().setBoolean(Display.BoolValue.SEPARATE_COVER, separateCoverOn);
final boolean showFormFieldHighlights = "true".equalsIgnoreCase(properties.getValue("highlightFormFields"));
final int formFieldHighlightColor = Integer.parseInt(properties.getValue("formFieldsHighlightColor"));
final boolean useOffScreenRendering = "true".equalsIgnoreCase(properties.getValue("useOffScreenRendering"));
final boolean fullOffScreenRendering = "true".equalsIgnoreCase(properties.getValue("fullOffScreenRendering"));
final Map<Integer, Object> map = new HashMap<>();
map.put(JPedalSettings.HIGHLIGHT_FORM_FIELDS, showFormFieldHighlights);
map.put(JPedalSettings.HIGHLIGHT_FORM_FIELDS_COLOR, formFieldHighlightColor);
map.put(JPedalSettings.OFF_SCREEN_RENDERING, useOffScreenRendering);
map.put(JPedalSettings.FULL_OFF_SCREEN_RENDERING, fullOffScreenRendering);
try {
decode_pdf.modifyNonstaticJPedalParameters(map);
} catch (final PdfException e) {
LogWriter.writeLog("Error setting values from properties " + e);
}
}
private void initViewerWindow() {
//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 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
if (width < 0) {
width = d.width / 2;
if (width < minimumScreenWidth) {
width = minimumScreenWidth;
}
properties.setValue("startViewerWidth", String.valueOf(width));
}
if (height < 0) {
height = d.height / 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");
}
try {
width = Integer.parseInt(values.nextToken().trim());
height = Integer.parseInt(values.nextToken().trim());
} catch (final NumberFormatException ee) {
throw new RuntimeException("Unable to use value for org.jpedal.startWindowSize=" + customWindowSize + "\nValue should be in format org.jpedal.startWindowSize=200x300 " + ee);
}
}
createMainViewerWindow(width, height);
}
private void initButtonsAndMenu() {
//Menu bar for using the majority of functions
menuItems.createMainMenu(true, currentCommandListener, currentCommands, swButtons);
//sets up all the toolbar items
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.openFile"), "open.gif", Commands.OPENFILE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.saveFile"), "save.gif", Commands.SAVE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.print"), "print.gif", Commands.PRINT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
if (searchFrame != null && (searchFrame.getViewStyle() == SwingSearchWindow.SEARCH_EXTERNAL_WINDOW)) {
searchFrame.setViewStyle(SwingSearchWindow.SEARCH_EXTERNAL_WINDOW);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.search"), "find.gif", Commands.FIND, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
}
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.properties"), "properties.gif", Commands.DOCINFO, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
//snapshot screen function
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.snapshot"), "snapshot.gif", Commands.SNAPSHOT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.minimise"), "minimise.gif", Commands.ZOOMOUT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.zoom"), "zoom.gif", Commands.ZOOMIN, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
final JSeparator sep = new JSeparator(SwingConstants.VERTICAL);
sep.setPreferredSize(new Dimension(5, 32));
swButtons.getTopButtons().add(sep);
//combo boxes on toolbar
addCombo(Messages.getMessage("PdfViewerToolbarTooltip.zoomin"), Commands.SCALING);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.rotateLeft"), "rotateLeft.gif", Commands.ROTATELEFT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.rotateRight"), "rotateRight.gif", Commands.ROTATERIGHT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
addCombo(Messages.getMessage("PdfViewerToolbarTooltip.rotation"), Commands.ROTATION);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.mouseMode"), "mouse_select.png", Commands.MOUSEMODE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
final JSeparator sep2 = new JSeparator(SwingConstants.VERTICAL);
sep2.setPreferredSize(new Dimension(5, 32));
swButtons.getTopButtons().add(sep2);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.help"), "help.png", Commands.HELP, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(BUTTONBAR, Messages.getMessage("PdfViewerToolbarTooltip.openSystemDefault"), "openSystem.png", Commands.OPENINSYSTEMDEFAULT, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
}
@SuppressWarnings({"OverlyLongMethod", "java:S138"})
private void initProperties() {
String propValue = properties.getValue("pageInsets");
if (!propValue.isEmpty()) {
inset = Integer.parseInt(propValue);
}
propValue = properties.getValue("changeTextAndLineart");
if (!propValue.isEmpty()
&& "true".equalsIgnoreCase(propValue)) {
currentCommands.executeCommand(ViewerCommands.CHANGELINEART, new Object[]{Boolean.parseBoolean(propValue)});
}
propValue = properties.getValue("windowTitle");
if (!propValue.isEmpty()) {
windowTitle = propValue;
} else {
windowTitle = Messages.getMessage("PdfViewer.titlebar") + ' ' + version;
}
propValue = properties.getValue("vbgColor");
if (!propValue.isEmpty()) {
currentCommands.executeCommand(ViewerCommands.SETPAGECOLOR, new Object[]{Integer.parseInt(propValue)});
}
propValue = properties.getValue("replaceDocumentTextColors");
if (!propValue.isEmpty()
&& "true".equalsIgnoreCase(propValue)) {
propValue = properties.getValue("vfgColor");
if (!propValue.isEmpty()) {
currentCommands.executeCommand(ViewerCommands.SETTEXTCOLOR, new Object[]{Integer.parseInt(propValue)});
}
}
propValue = properties.getValue("TextColorThreshold");
if (!propValue.isEmpty()) {
currentCommands.executeCommand(ViewerCommands.SETREPLACEMENTCOLORTHRESHOLD, new Object[]{Integer.parseInt(propValue)});
}
propValue = properties.getValue("enhanceFractionalLines");
if (!propValue.isEmpty()) {
currentCommands.executeCommand(ViewerCommands.SETENHANCEFRACTIONALLINES, new Object[]{Boolean.parseBoolean(propValue)});
}
propValue = properties.getValue("hideLineWeights");
if (!propValue.isEmpty()) {
currentCommands.executeCommand(ViewerCommands.SETHIDELINEWEIGHTS, new Object[]{Boolean.parseBoolean(propValue)});
}
//Set autoScroll default and add to properties file
propValue = properties.getValue("autoScroll");
if (!propValue.isEmpty()) {
allowScrolling = Boolean.parseBoolean(propValue);
}
//set confirmClose
propValue = properties.getValue("confirmClose");
if (!propValue.isEmpty()) {
confirmClose = "true".equals(propValue);
}
//Dpi is taken into effect when zoom is called
propValue = properties.getValue("resolution");
if (!propValue.isEmpty()) {
setDpi(Integer.parseInt(propValue));
}
//Ensure valid value if not recognised
propValue = properties.getValue("startView");
if (!propValue.isEmpty()) {
int pageMode = Integer.parseInt(propValue);
if (pageMode < Display.SINGLE_PAGE || pageMode > Display.PAGEFLOW) {
pageMode = Display.SINGLE_PAGE;
}
//Default Page Layout
decode_pdf.setPageMode(pageMode);
}
final String val = properties.getValue("highlightBoxColor"); //empty string to old users
if (!val.isEmpty()) {
DecoderOptions.highlightColor = new Color(Integer.parseInt(val));
}
propValue = properties.getValue("useSmoothImage");
if (!propValue.isEmpty()) {
final HashMap<Integer, Object> map = new HashMap<>();
map.put(JPedalSettings.SMOOTH_IMAGES, Boolean.valueOf(propValue));
try {
decode_pdf.modifyNonstaticJPedalParameters(map);
} catch (final PdfException e) {
LogWriter.error(e, "Exception thrown while setting the useSmoothImage property");
}
}
propValue = properties.getValue("highlightTextColor");
if (!propValue.isEmpty()) {
DecoderOptions.backgroundColor = new Color(Integer.parseInt(propValue));
}
propValue = properties.getValue("showMouseSelectionBox");
if (!propValue.isEmpty()) {
DecoderOptions.showMouseBox = Boolean.parseBoolean(propValue);
}
propValue = properties.getValue("enhancedViewerMode");
if (!propValue.isEmpty()) {
decode_pdf.useNewGraphicsMode(Boolean.parseBoolean(propValue));
}
propValue = properties.getValue("highlightComposite");
if (!propValue.isEmpty()) {
setHHighlightComposite(propValue);
}
//Set border config value and repaint
propValue = properties.getValue("borderType");
if (!propValue.isEmpty()) {
decode_pdf.getPages().setBorderPresent(Integer.parseInt(propValue) == 1);
}
//Allow cursor to change
propValue = properties.getValue("allowCursorToChange");
if (!propValue.isEmpty()) {
GUIDisplay.allowChangeCursor = "true".equalsIgnoreCase(propValue);
}
propValue = properties.getValue("invertHighlights");
if (!propValue.isEmpty()) {
BaseDisplay.invertHighlight = Boolean.parseBoolean(propValue);
}
propValue = properties.getValue("enhancedFacingMode");
if (!propValue.isEmpty()) {
GUIDisplay.default_turnoverOn = Boolean.parseBoolean(propValue);
}
//Set whether to use hinting
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)) {
JOptionPane.showMessageDialog(null, Messages.getMessage("PdfCustomGui.hintingFlagFileConflict"));
}
TTGlyph.useHinting = "true".equalsIgnoreCase(propValue2);
//check properties file
} else {
TTGlyph.useHinting = !propValue.isEmpty() && "true".equalsIgnoreCase(propValue);
}
//Set icon location
propValue = properties.getValue("iconLocation");
if (!propValue.isEmpty()) {
guiCursor.setIconLocation(propValue);
}
}
private void nameElements() {
menuItems.getCurrentMenu().setName("menuBar");
frame.setName("frame");
pageCounter2.setName("pageCounter");
rotationBox.setName("rotationBox");
scalingBox.setName("scalingBox");
}
/**
* method being called from within init to create other tool bars to add to
* display
*/
private void createOtherToolBars() {
//setup top main menu toolbar and create options
top.add(menuItems.getCurrentMenu(), BorderLayout.NORTH);
//This is where we add the Buttons for the top ToolBar
top.add(swButtons.getTopButtons(), BorderLayout.CENTER);
//This is where we add the Buttons for the bottom ToolBar
if (frame instanceof JFrame) {
((RootPaneContainer) frame).getContentPane().add(navButtons, BorderLayout.SOUTH);
} else {
frame.add(navButtons, BorderLayout.SOUTH);
}
//This is where we add the display pane to the center of the viewer
if (frame instanceof JFrame) {
((RootPaneContainer) frame).getContentPane().add(displayPane, BorderLayout.CENTER);
} else {
frame.add(displayPane, BorderLayout.CENTER);
}
}
private void handleTabbedPanes() {
if (tabsNotInitialised) {
return;
}
//expand size if not already at size
final int tabSelected = navOptionsPanel.getSelectedIndex();
if (tabSelected == -1) {
return;
}
if (!tabsExpanded) {
//workout selected tab
final String tabName;
if (DecoderOptions.isRunningOnMac) {
tabName = navOptionsPanel.getTitleAt(tabSelected);
} else {
tabName = navOptionsPanel.getIconAt(tabSelected).toString();
}
thumbnails.setIsDisplayedOnscreen(tabName.equals(pageTitle));
if (tabName.equals(annotationTitle)) {
if (decode_pdf.isEncrypted()) {
showMessageDialog((Messages.getMessage("PdfViewerMessage.NewAnnotInEncryptedFile")), Messages.getMessage("PdfViewerGeneral.Warning"), JOptionPane.WARNING_MESSAGE);
}
annotationsPanel.populateList(decode_pdf);
}
displayPane.setDividerLocation(expandedSize);
tabsExpanded = true;
} else if (lastTabSelected == tabSelected) {
displayPane.setDividerLocation(collapsedSize);
tabsExpanded = false;
thumbnails.setIsDisplayedOnscreen(false);
navOptionsPanel.setSelectedIndex(-1);
}
lastTabSelected = tabSelected;
}
/**
* Not part of API - used internally
* <p>
* Set the state and value for the multi box at the bottom left of the user interface.
*
* @param flags an int[] used to control multi box display
*/
public void setMultibox(final int[] flags) {
//debug code for formclicktests
if (alwaysShowMouse) {
multibox.removeAll();
multibox.add(coords, BorderLayout.CENTER);
multibox.repaint();
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 (cursor.isEnabled() && cursor.isVisible() && cursorOverPage && decode_pdf.isOpen()) {
multibox.removeAll();
multibox.add(coords, BorderLayout.CENTER);
multibox.repaint();
return;
}
//DOWNLOAD_PROGRESS:
if (downloadBar.isEnabled() && downloadBar.isVisible() && !downloadBar.isDone() && (decode_pdf.isLoadingLinearizedPDF() || !decode_pdf.isOpen())) {
multibox.removeAll();
downloadBar.getStatusObject().setSize(multibox.getSize());
multibox.add(downloadBar.getStatusObject(), BorderLayout.CENTER);
multibox.repaint();
return;
}
//MEMORY:
if (memoryBar.isEnabled() && memoryBar.isVisible()) {
multibox.removeAll();
memoryBar.setSize(multibox.getSize());
memoryBar.setForeground(new Color(125, 145, 255));
multibox.add(memoryBar, BorderLayout.CENTER);
multibox.repaint();
}
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void setTitle(final String title) {
if (frame instanceof JFrame) {
((Frame) frame).setTitle(title);
}
}
public void resetComboBoxes(final boolean value) {
scalingBox.setEnabled(value);
rotationBox.setEnabled(value);
}
public SwingCombo getCombo(final int ID) {
return switch (ID) {
case Commands.SCALING -> scalingBox;
case Commands.ROTATION -> rotationBox;
default -> null;
};
}
/**
* All scaling and rotation should go through this.
*/
public void scaleAndRotate() {
if (decode_pdf != null) {
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;
}
}
// [AWI]: Get the values of all of the border insets around the scrollpane
Insets borderInsets = new Insets(0, 0, 0, 0);
if (scrollPane.getBorder() != null && scrollPane.getBorder().getBorderInsets(scrollPane) != null) {
borderInsets = scrollPane.getBorder().getBorderInsets(scrollPane);
}
float width = scrollPane.getWidth() - inset - inset - borderInsets.left - borderInsets.right;
final float height = scrollPane.getHeight() - inset - inset - borderInsets.top - borderInsets.bottom;
if (decode_pdf != null) {
int index = getSelectedComboIndex(Commands.SCALING);
if (decode_pdf.getDisplayView() == Display.PAGEFLOW) {
//Ensure we only display in window mode
setSelectedComboIndex(Commands.SCALING, 0);
index = 0;
//Disable scaling option
scalingBox.setEnabled(false);
} else if (decode_pdf.getDisplayView() != Display.PAGEFLOW) {
//No long pageFlow. enable scaling option
scalingBox.setEnabled(true);
}
if (index == -1) {
final float zoom = getScalingFromString((String) getSelectedComboItem(Commands.SCALING));
//if nothing off either attempt, use window value
if (zoom == -1) {
//its not set so use To window value
index = defaultSelection;
setSelectedComboIndex(Commands.SCALING, index);
} else {
scaling = decode_pdf.getDPIFactory().adjustScaling(zoom / 100);
setSelectedComboItem(Commands.SCALING, String.valueOf(zoom));
}
}
int page = commonValues.getCurrentPage();
//Multipage tiff should be treated as a single page
if (commonValues.isMultiTiff()) {
page = 1;
}
//always check in facing mode with turnover on
if (index != -1 || decode_pdf.getDisplayView() == Display.SINGLE_PAGE || (decode_pdf.getDisplayView() == Display.FACING && decode_pdf.getPages().getBoolean(Display.BoolValue.TURNOVER_ON))) {
final Dimension contentSize = getContentDimensions(page);
if (displayPane != null) {
width -= displayPane.getDividerSize();
}
setupScalingValue(index, width, height, contentSize.width, contentSize.height);
}
//allow for clicking on it before page opened
decode_pdf.setPageParameters(scaling, page, rotation);
//Ensure the page is displayed in the correct rotation
setRotation();
}
if (LogWriter.isRunningFromIDE) {
System.out.println("Memory usage after zoom=" + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1_000);
}
//Update annotation selection position if required
annotationsPanel.getAnnotationListener().updateSelectionPosition();
}
private Dimension getContentDimensions(final int page) {
final Dimension dimensions = new Dimension();
final PdfPageData pageData = decode_pdf.getPdfPageData();
final int raw_rotation;
if (decode_pdf.getDisplayView() == Display.FACING) {
raw_rotation = pageData.getRotation(page);
} else {
raw_rotation = 0;
}
final boolean isRotated = (rotation + raw_rotation) % 180 == 90;
final PageOffsets offsets = (PageOffsets) decode_pdf.getExternalHandler(Options.CurrentOffset);
switch (decode_pdf.getDisplayView()) {
case Display.CONTINUOUS_FACING:
handleContinuousModes(dimensions, isRotated, offsets, offsets.getMaxH() * 2, offsets.getMaxW() * 2);
break;
case Display.CONTINUOUS:
handleContinuousModes(dimensions, isRotated, offsets, offsets.getMaxH(), offsets.getMaxW());
break;
case Display.FACING:
handleFacing(page, dimensions, pageData, isRotated, decode_pdf, commonValues);
break;
default:
if (isRotated) {
dimensions.width = pageData.getCropBoxHeight(page);
dimensions.height = pageData.getCropBoxWidth(page);
} else {
dimensions.width = pageData.getCropBoxWidth(page);
dimensions.height = pageData.getCropBoxHeight(page);
}
}
//Check if image with no size data stored and get values direct from image
if (dimensions.width == 0 && dimensions.height == 0 && commonValues.getBufferedImg() != null) {
if (isRotated) {
dimensions.width = commonValues.getBufferedImg().getHeight();
dimensions.height = commonValues.getBufferedImg().getWidth();
} else {
dimensions.width = commonValues.getBufferedImg().getWidth();
dimensions.height = commonValues.getBufferedImg().getHeight();
}
}
return dimensions;
}
private static void handleFacing(final int page, final Dimension dimensions, final PdfPageData pageData,
final boolean isRotated, final PdfDecoderInt decode_pdf, final Values commonValues) {
int leftPage;
if (decode_pdf.getPages().getBoolean(Display.BoolValue.SEPARATE_COVER)) {
leftPage = (page / 2) * 2;
if (commonValues.getPageCount() == 2) {
leftPage = 1;
}
} else {
leftPage = (page);
if ((leftPage & 1) == 0) {
leftPage--;
}
}
if (leftPage == 0) {
leftPage++;
}
if (isRotated) {
dimensions.width = pageData.getCropBoxHeight(leftPage);
//if first or last page double the width, otherwise add other page width
if (leftPage + 1 > commonValues.getPageCount() || leftPage == 1) {
dimensions.width *= 2;
} else {
dimensions.width += pageData.getCropBoxHeight(leftPage + 1);
}
dimensions.height = pageData.getCropBoxWidth(leftPage);
if (leftPage + 1 <= commonValues.getPageCount() && dimensions.height < pageData.getCropBoxWidth(leftPage + 1)) {
dimensions.height = pageData.getCropBoxWidth(leftPage + 1);
}
} else {
dimensions.width = pageData.getCropBoxWidth(leftPage);
//if first or last page double the width, otherwise add other page width
if (leftPage + 1 > commonValues.getPageCount()) {
dimensions.width *= 2;
} else {
dimensions.width += pageData.getCropBoxWidth(leftPage + 1);
}
dimensions.height = pageData.getCropBoxHeight(leftPage);
if (leftPage + 1 <= commonValues.getPageCount() && dimensions.height < pageData.getCropBoxHeight(leftPage + 1)) {
dimensions.height = pageData.getCropBoxHeight(leftPage + 1);
}
}
}
private static void handleContinuousModes(final Dimension dimensions, final boolean isRotated,
final PageOffsets offsets, final int i, final int i2) {
if (isRotated) {
dimensions.width = i;
dimensions.height = offsets.getMaxW();
} else {
dimensions.width = i2;
dimensions.height = offsets.getMaxH();
}
}
private static float getScalingFromString(String numberValue) {
float zoom = -1;
if ((numberValue != null) && (!numberValue.isEmpty())) {
//Remove the % that may be present when setting value manually
if (numberValue.endsWith("%")) {
numberValue = numberValue.substring(0, numberValue.length() - 1);
}
try {
zoom = Float.parseFloat(numberValue);
} catch (final NumberFormatException e) {
LogWriter.writeLog("Exception in getting zoom " + e);
//its got characters in it so get first valid number string
final int length = numberValue.length();
int ii = 0;
while (ii < length) {
final char c = numberValue.charAt(ii);
if ((c >= '0' && c <= '9') || c == '.') {
ii++;
} else {
break;
}
}
if (ii > 0) {
numberValue = numberValue.substring(0, ii);
}
//try again if we reset above
try {
zoom = Float.parseFloat(numberValue);
} catch (final NumberFormatException e1) {
LogWriter.writeLog("Exception in getting zoom " + e1);
zoom = -1;
}
}
if (zoom > 1_000) {
zoom = 1_000;
}
}
return zoom;
}
private void setupScalingValue(final int index, float viewWidth, float viewHeight, final float contentWidth, final float contentHeight) {
// [AWI]: Get the default width of scrollbars from the UI Manager
int scrollbarWidth = 0;
final Object scrollbarWidthObj = UIManager.get("ScrollBar.width");
if (scrollbarWidthObj instanceof final Integer width) {
scrollbarWidth = width;
if (scrollbarWidth < 0) {
scrollbarWidth = 0;
}
}
float x_factor, y_factor, window_factor = 1.0f;
// [AWI]: Iterate over the scaling algorithm to try and take the scrollbar size into account.
// This logic was added to address an issue with 'Fit Width' and 'Fit Height' calculations where the
// scrollbar appeared as a result of the scaling, but the scrollbar was not taken into account in the
// scaling algorithm. When this occurred, both scrollbars could appear, even though only one should.
//
// The loopCount is configured such that the scaling routine should be run at least once, but not more
// than enough to make room for the scrollbar, to prevent an infinite loop.
float scaledWidth, scaledHeight;
int loopCount = 0;
while (loopCount++ <= (scrollbarWidth)) {
x_factor = viewWidth / contentWidth;
y_factor = viewHeight / contentHeight;
window_factor = Math.min(x_factor, y_factor);
if (index != -1) {
scaling = switch (index) {
case 0 -> window_factor;
case 1 -> y_factor;
case 2 -> x_factor;
default -> decode_pdf.getDPIFactory().adjustScaling(scalingFloatValues[index]);
};
if (index < 3) { //handle scroll to width/height/window
// [AWI]: Determine if the horizontal scrollbar will appear when set to 'Fit Height'
// or if the vertical scrollbar will appear when set to 'Fit Width' and if so, add the
// scrollbar size into the scaling algorithm and recalculate once.
scaledWidth = scaling * contentWidth;
scaledHeight = scaling * contentHeight;
if (index == 1 && scaledWidth > viewWidth) { // height
viewHeight--;
} else if (index == 2 && scaledHeight > viewHeight) { // width
viewWidth--;
} else {
// Break out to prevent multiple passes since scrollbars should not be displayed
break;
}
} else {
// Break out to prevent multiple passes since scrollbars should not be displayed
break;
}
}
}
if (decode_pdf.getDisplayView() == Display.FACING) { // Enable turnover if both pages properly displayed
pageTurnScalingAppropriate = scaling <= window_factor;
}
if (thumbscroll != null) {
if (decode_pdf.getDisplayView() == Display.SINGLE_PAGE && scaling <= window_factor) {
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
thumbscroll.setVisible(commonValues.getFileType() == Values.FileType.PDF);
} else {
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
thumbscroll.setVisible(false);
}
}
}
/**
* Set scaling to nearest of the viewer default values.
*
* @param newScaling The scaling value to snap to viewer default values
*/
public void snapScalingToDefaults(float newScaling) {
newScaling = decode_pdf.getDPIFactory().adjustScaling(newScaling / 100);
float width = scrollPane.getViewport().getWidth() - inset - inset;
final float height = scrollPane.getViewport().getHeight() - inset - inset;
final Dimension contentSize = getContentDimensions(commonValues.getCurrentPage());
if (displayPane != null) {
width -= displayPane.getDividerSize();
}
float x_factor;
float y_factor;
final float window_factor;
x_factor = width / contentSize.width;
y_factor = height / contentSize.height;
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;
}
}
public void rotate() {
rotation = Integer.parseInt((String) getSelectedComboItem(Commands.ROTATION));
scaleAndRotate();
((JComponent) decode_pdf).updateUI();
}
/**
* Scrolls the view to the specified page.
*
* @param page Page to scroll to.
*/
private void scrollToPage(final int page) {
commonValues.setCurrentPage(page);
if (commonValues.getCurrentPage() > 0) {
int yCord = 0;
if (decode_pdf.getDisplayView() != Display.SINGLE_PAGE) {
yCord = decode_pdf.getPages().getYCordForPage(page);
}
if ((decode_pdf.getDisplayView() == Display.CONTINUOUS_FACING || decode_pdf.getDisplayView() == Display.FACING)
&& page == 1) {
scrollPane.getHorizontalScrollBar().setValue((int) (decode_pdf.getPdfPageData().getCropBoxWidth(commonValues.getCurrentPage()) * scaling));
} else {
scrollPane.getHorizontalScrollBar().setValue(0);
}
scrollPane.getVerticalScrollBar().setValue(yCord);
}
if (decode_pdf.getPageCount() > 1) {
swButtons.setPageLayoutButtonsEnabled(true);
}
}
/**
* Set the thumbnail panel ready for display.
*/
private void setupThumbnailPanel() {
if (isSetup) {
return;
}
isSetup = true;
if (thumbnails.isShownOnscreen()) {
final int pages = decode_pdf.getPageCount();
//setup and add to display
thumbnails.setupThumbnails(pages, textFont, decode_pdf.getPdfPageData());
//add listener so clicking on button changes to page - has to be in Viewer so it can update it
final Object[] buttons = thumbnails.getButtons();
for (int i = 0; i < pages; i++) {
((AbstractButton) buttons[i]).addActionListener(new SwingPageChanger(this, commonValues, i));
}
//add global listener
thumbnails.addComponentListener();
}
}
/**
* Set the bookmarks for the outline panel
*
* @param alwaysGenerate true we will generate the bookmarks, false we only generate bookmarks if not already generated
*/
public void setBookmarks(final boolean alwaysGenerate) {
//Ignore if done already
if ((bookmarksGenerated && !alwaysGenerate) || decode_pdf.isLoadingLinearizedPDF()) {
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);
/*
* Use a mouse listener instead of a TreeSelectionListener for 2 reasons
* 1. Tree selection only works on new selections, Mouse clicks work on
* every click of the mouse.
* 2. Tree nodes are highlighted before the mouse event, the mouse event
* can check the node that was highlighted by the tree and trigger it
* allowing highlighted bookmarks to be triggered again.
* We can also check if the mouse is within the node so it doesn't trigger
* if clicked in an empty area.
*/
tree.getTree().addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent e) {
final DefaultMutableTreeNode node = tree.getLastSelectedPathComponent();
final int[] selected = tree.getTree().getSelectionRows();
if (selected != null && selected.length != 0) {
final Rectangle r = tree.getTree().getRowBounds(selected[0]);
if (node == null || !r.contains(e.getX(), e.getY())) {
return;
}
}
triggerBookmark(node);
}
});
/*
* Navigate bookmarks using arrow keys,
* left and right go to parent / child nodes,
* enter key will trigger the currently selected node
*/
tree.getTree().addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(final KeyEvent e) {
if (e.getKeyCode() == '\n') {
final DefaultMutableTreeNode node = tree.getLastSelectedPathComponent();
if (node != null) {
triggerBookmark(node);
}
}
}
});
} else {
tree.reset(null);
}
}
private void triggerBookmark(final DefaultMutableTreeNode node) {
final JTree jtree = tree.getTree();
final DefaultTreeModel treeModel = (DefaultTreeModel) jtree.getModel();
final java.util.List<TreeNode> flattenedTree = new ArrayList<>();
//flatten out the tree so we can find the index of the selected node
getFlattenedTreeNodes((TreeNode) treeModel.getRoot(), flattenedTree);
flattenedTree.remove(0); // remove the root node as we don't account for this
final int index = flattenedTree.indexOf(node);
final String ref = tree.convertNodeIDToRef(index);
final PdfObject Aobj = decode_pdf.getOutlineData().getAobj(ref);
//handle in our handler code
if (Aobj != null) {
if (SwingUtilities.isEventDispatchThread()) {
decode_pdf.getFormRenderer().getActionHandler().gotoDest(Aobj, ActionHandler.MOUSECLICKED, PdfDictionary.Dest);
} else {
try {
SwingUtilities.invokeAndWait(() -> decode_pdf.getFormRenderer().getActionHandler().gotoDest(Aobj, ActionHandler.MOUSECLICKED, PdfDictionary.Dest));
} catch (InterruptedException | InvocationTargetException e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
}
}
}
private void initStatus() {
decode_pdf.setStatusBarObject(statusBar);
//and initialise the display
setMultibox(new int[]{});
}
public void setCoordText(final String string) {
coords.setText(string);
}
private void initCoordBox() {
coords.setOpaque(true);
coords.setBorder(BorderFactory.createEtchedBorder());
coords.setPreferredSize(multibox.getPreferredSize());
//Need to ensure the coords appear correctly in facing mode
//incase coords have not been displayed before entering facing mode
coords.setSize(multibox.getPreferredSize());
coords.setText(" X: " + " Y: " + ' ' + ' ');
}
public void setPageNumber() {
if (SwingUtilities.isEventDispatchThread()) {
setPageNumberWorker();
if (isSetup) {
//Page changed so save this page as last viewed
setThumbnails();
}
} else {
final Runnable r = () -> {
setPageNumberWorker();
if (isSetup) {
//Page changed so save this page as last viewed
setThumbnails();
}
};
try {
SwingUtilities.invokeAndWait(r);
} catch (InterruptedException | InvocationTargetException e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
}
}
private void setPageNumberWorker() {
if (pageCounter2 == null) {
return;
}
if (!decode_pdf.isOpen() && !commonValues.isMultiTiff()) {
pageCounter2.setText("0");
pageCounter3.setText(Messages.getMessage("PdfViewerOfLabel.text") + " 0");
} else {
if (thumbscroll != null) {
thumbscroll.setMaximum(decode_pdf.getPageCount());
thumbscroll.setValue(commonValues.getCurrentPage() - 1);
}
final int currentPage = commonValues.getCurrentPage();
if (decode_pdf.getDisplayView() == Display.FACING || decode_pdf.getDisplayView() == Display.CONTINUOUS_FACING) {
if (decode_pdf.getPageCount() == 2) {
pageCounter2.setText(getPageLabel(1) + '/' + getPageLabel(2));
} else if (decode_pdf.getPages().getBoolean(Display.BoolValue.SEPARATE_COVER)) {
final int base = currentPage & -2;
if (base != decode_pdf.getPageCount() && base != 0) {
pageCounter2.setText(getPageLabel(base) + '/' + getPageLabel(base + 1));
} else {
pageCounter2.setText(getPageLabel(currentPage));
}
} else {
final int base = currentPage - (1 - (currentPage & 1));
if (base != decode_pdf.getPageCount()) {
pageCounter2.setText(getPageLabel(base) + '/' + getPageLabel(base + 1));
} else {
pageCounter2.setText(getPageLabel(currentPage));
}
}
} else {
pageCounter2.setText(getPageLabel(currentPage));
}
if (pageLabelDiffers(currentPage)) {
pageCounter3.setText("(" + currentPage + ' ' + Messages.getMessage("PdfViewerOfLabel.text") + ' ' + commonValues.getPageCount() + ')'); //$NON-NLS-1$
} else {
pageCounter3.setText(Messages.getMessage("PdfViewerOfLabel.text") + ' ' + commonValues.getPageCount()); //$NON-NLS-1$
}
swButtons.hideRedundentNavButtons(this);
}
}
/**
* note - to plugin put all on single line so addButton values over-ridden
*/
private void createNavbar() {
if (memoryMonitor == null) { //ensure only 1 instance
memoryMonitor = new Timer(500, event -> {
final int free = (int) (Runtime.getRuntime().freeMemory() / (1_024 * 1_024));
final int total = (int) (Runtime.getRuntime().totalMemory() / (1_024 * 1_024));
//this broke the image saving when it was run every time
if (finishedDecoding) {
finishedDecoding = false;
}
memoryBar.setMaximum(total);
memoryBar.setValue(total - free);
memoryBar.setStringPainted(true);
memoryBar.setString((total - free) + "M of " + total + 'M');
});
memoryMonitor.start();
}
multibox.setLayout(new BorderLayout());
navButtons.add(multibox, BorderLayout.WEST);
navButtons.add(Box.createHorizontalGlue());
navToolBar.add(Box.createHorizontalGlue());
swButtons.addButton(NAVBAR, Messages.getMessage("PdfViewerNavBar.RewindToStart"), "start.gif", Commands.FIRSTPAGE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(NAVBAR, Messages.getMessage("PdfViewerNavBar.Rewind10"), "fback.gif", Commands.FBACKPAGE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(NAVBAR, Messages.getMessage("PdfViewerNavBar.Rewind1"), "back.gif", Commands.BACKPAGE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
pageCounter1 = new JLabel(Messages.getMessage("PdfViewerPageLabel.text"));
pageCounter1.setOpaque(false);
navToolBar.add(pageCounter1); // Page
navToolBar.add(pageCounter2); // X
navToolBar.add(pageCounter3); // of total
swButtons.addButton(NAVBAR, Messages.getMessage("PdfViewerNavBar.Forward1"), "forward.gif", Commands.FORWARDPAGE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(NAVBAR, Messages.getMessage("PdfViewerNavBar.Forward10"), "fforward.gif", Commands.FFORWARDPAGE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(NAVBAR, Messages.getMessage("PdfViewerNavBar.ForwardLast"), "end.gif", Commands.LASTPAGE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
navToolBar.add(Box.createHorizontalGlue());
if (debugMode) {
addLimitDecodeSlider();
}
swButtons.addButton(PAGES, Messages.getMessage("PageLayoutButton.SinglePage"), "single.gif", Commands.SINGLE, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(PAGES, Messages.getMessage("PageLayoutButton.Continuous"), "continuous.gif", Commands.CONTINUOUS, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(PAGES, Messages.getMessage("PageLayoutButton.ContinousFacing"), "continuous_facing.gif", Commands.CONTINUOUS_FACING, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(PAGES, Messages.getMessage("PageLayoutButton.Facing"), "facing.gif", Commands.FACING, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
swButtons.addButton(PAGES, Messages.getMessage("PageLayoutButton.PageFlow"), "pageflow.gif", Commands.PAGEFLOW, menuItems, this, currentCommandListener, pagesToolBar, navToolBar);
final Dimension size;
navButtons.add(pagesToolBar, BorderLayout.EAST);
size = pagesToolBar.getPreferredSize();
multibox.setPreferredSize(size);
navButtons.add(navToolBar, BorderLayout.CENTER);
}
@SuppressWarnings({"OverlyLongMethod", "java:S138"})
private void addLimitDecodeSlider() {
final JToolBar decoderPanel = new JToolBar();
decoderPanel.setFloatable(false);
decoderPanel.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = 0;
final JSlider slider = new JSlider();
slider.setMinimum(0);
slider.setMinorTickSpacing(1);
slider.setMinorTickSpacing(10);
slider.setValue(0);
c.gridx = 0;
c.weightx = 0;
final NumberFormat format = NumberFormat.getInstance();
final NumberFormatter formatter = new NumberFormatter(format);
formatter.setValueClass(Integer.class);
formatter.setMinimum(0);
formatter.setAllowsInvalid(true);
formatter.setCommitsOnValidEdit(false);
final JFormattedTextField field = new JFormattedTextField(formatter);
final CommandWindow commandWindow = new CommandWindow();
CommandParser.commandWindow = commandWindow;
Tj.commandWindow = commandWindow;
final DefaultListModel<String> listModel = new DefaultListModel<>();
commandWindow.setListModel(listModel);
final HashSet<String> commands = new HashSet<>();
commandWindow.setHashSet(commands);
final JList<String> list = new JList<>(listModel);
final JScrollPane scrollPane = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
final JScrollBar v = scrollPane.getVerticalScrollBar();
final TreeSet<String> breaks =
new TreeSet<>(Comparator.comparingInt(cmd -> Integer.parseInt(cmd.substring(0, cmd.indexOf(':')))));
list.setCellRenderer(new ColorListCellRenderer(commands, breaks, 300));
final LimitDecodeTrackerNoSwing errorTracker =
new LimitDecodeTrackerNoSwing((PdfDecoder) decode_pdf, formatter, field, slider, breaks, list);
final KeyAdapter keyAdapter = new KeyAdapter() {
@Override
public void keyReleased(final KeyEvent e) {
final int value = (Integer) field.getValue();
if (e != null && (e.getKeyChar() == '\n' || e.getKeyChar() == '\r')) {
field.setValue(value);
}
slider.setValue(value);
}
};
field.addKeyListener(keyAdapter);
slider.addChangeListener(e -> {
if (slider.getValueIsAdjusting()) {
return; // Redecoding each time the slider moves is very slow
}
final int value = slider.getValue();
field.setValue(value);
errorTracker.repaint(value);
v.setValue(v.getMaximum());
});
decode_pdf.addExternalHandler(errorTracker, Options.ErrorTracker);
c.gridx = 1;
swButtons.addButtonToToolBarWithConstraints("Start", "start.gif", -1, menuItems, this, e -> {
field.setValue(0);
keyAdapter.keyReleased(null);
}, decoderPanel, c);
c.gridx = 2;
swButtons.addButtonToToolBarWithConstraints("Retreat by 1", "back.gif", -1, menuItems, this, e -> {
if ((Integer) field.getValue() <= 0) {
field.setValue(0);
} else {
field.setValue(((Integer) field.getValue()) - 1);
}
keyAdapter.keyReleased(null);
}, decoderPanel, c);
c.gridx = 3;
c.weightx = 1;
decoderPanel.add(field, c);
c.weightx = 0;
c.gridx = 4;
swButtons.addButtonToToolBarWithConstraints("Advance by 1", "forward.gif", -1, menuItems, this, e -> {
if ((int) field.getValue() >= (Integer) formatter.getMaximum()) {
field.setValue(formatter.getMaximum());
} else {
field.setValue(((Integer) field.getValue()) + 1);
}
keyAdapter.keyReleased(null);
}, decoderPanel, c);
c.gridx = 5;
swButtons.addButtonToToolBarWithConstraints("End", "end.gif", -1, menuItems, this, e -> {
field.setValue(formatter.getMaximum());
keyAdapter.keyReleased(null);
}, decoderPanel, c);
c.gridx = 6;
c.fill = GridBagConstraints.VERTICAL;
final JSeparator separator = new JSeparator(SwingConstants.VERTICAL);
decoderPanel.add(separator, c);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 7;
swButtons.addButtonToToolBarWithConstraints("Next breakpoint", "resume.gif", -1, menuItems, this, e -> {
// If we are at the end, the resume button restarts the decoding
if ((int) field.getValue() == (Integer) formatter.getMaximum()) {
try {
final String firstBreak = breaks.first();
field.setValue(Integer.parseInt(firstBreak.substring(0, firstBreak.indexOf(':'))));
keyAdapter.keyReleased(null);
return;
} catch (final NoSuchElementException ignored) {
//NOSONAR
}
}
// If we are not at the end, attempt to find the next breakpoint
String nextBreakpoint = null;
int nextBreakpointCommand = -1;
if (!breaks.isEmpty()) {
for (final String breakPoint : breaks) {
nextBreakpointCommand = Integer.parseInt(breakPoint.substring(0, breakPoint.indexOf(':')));
if (nextBreakpointCommand > (Integer) field.getValue()) {
nextBreakpoint = breakPoint;
break;
}
}
}
if (nextBreakpoint == null) {
field.setValue(formatter.getMaximum());
} else {
field.setValue(nextBreakpointCommand);
}
keyAdapter.keyReleased(null);
}, decoderPanel, c);
c.gridx = 8;
swButtons.addButtonToToolBarWithConstraints("Clear breakpoints", "clearb.gif", -1, menuItems, this, e -> {
breaks.clear();
list.repaint();
}, decoderPanel, c);
final ListSelectionListener listSelectionListener = e -> {
if (list.getValueIsAdjusting()) {
return;
}
final String command = list.getSelectedValue();
if (commands.contains(command)) { // Only allow commands to be breakpoints
if (breaks.contains(command)) {
breaks.remove(command);
} else {
breaks.add(command);
}
}
list.clearSelection();
};
final MouseAdapter listMouseListener = new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent e) {
showMenu(e);
}
@Override
public void mouseReleased(final MouseEvent e) {
showMenu(e);
}
private void showMenu(final MouseEvent e) {
if (!e.isPopupTrigger()) {
return;
}
final int clicked = list.locationToIndex(e.getPoint());
if (clicked != -1 && list.getCellBounds(clicked, clicked).contains(e.getPoint())) {
final JPopupMenu menu = new JPopupMenu();
final JMenuItem breakItem = new JMenuItem("Set breakpoint");
breakItem.addActionListener(e1 -> {
final String command = listModel.get(clicked);
if (commands.contains(command)) { // Only allow commands to be breakpoints
if (breaks.contains(command)) {
breaks.remove(command);
} else {
breaks.add(command);
}
list.repaint();
}
});
menu.add(breakItem);
final String command = listModel.get(clicked);
final Pattern doPattern = Pattern.compile("^\\d+?:\\s+?DO$", Pattern.CASE_INSENSITIVE);
final Matcher doMatcher = doPattern.matcher(command);
if (doMatcher.matches()) {
final JMenuItem imageItem = new JMenuItem("View Image");
imageItem.addActionListener(e1 -> {
final String imageCacheName = imageNameCollector.getCacheName(listModel.get(clicked - 1).substring(1));
if (imageCacheName == null) {
LogWriter.writeLog("[Warning] Cannot find image name in collector");
return;
}
final JDialog imageDialog = new JDialog((Frame) null, doMatcher.group(), false);
imageDialog.add(new JLabel(new ImageIcon(decode_pdf.getObjectStore().loadStoredImage(imageCacheName))));
imageDialog.pack();
imageDialog.setLocationRelativeTo(frame);
imageDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
imageDialog.setVisible(true);
});
menu.add(imageItem);
} else {
final Pattern idPattern = Pattern.compile("^(\\d+?):\\s+?ID$", Pattern.CASE_INSENSITIVE);
final Matcher idMatcher = idPattern.matcher(command);
if (idMatcher.matches()) {
final JMenuItem imageItem = new JMenuItem("View Inline Image");
imageItem.addActionListener(e1 -> {
final String imageCacheName = imageNameCollector.getCacheNameFromIDToken(Integer.parseInt(idMatcher.group(1)));
if (imageCacheName == null) {
LogWriter.writeLog("[Warning] Cannot find image name in collector");
return;
}
final JDialog imageDialog = new JDialog((Frame) null, idMatcher.group(), false);
imageDialog.add(new JLabel(new ImageIcon(decode_pdf.getObjectStore().loadStoredImage(imageCacheName))));
imageDialog.pack();
imageDialog.setLocationRelativeTo(frame);
imageDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
imageDialog.setVisible(true);
});
menu.add(imageItem);
}
}
menu.add(new JSeparator(SwingConstants.HORIZONTAL));
final JMenuItem copyItem = new JMenuItem("Copy Text");
copyItem.addActionListener(e1 -> {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
new StringSelection(listModel.get(clicked)), null);
});
menu.add(copyItem);
menu.show(list, e.getX(), e.getY());
}
}
};
list.addListSelectionListener(listSelectionListener);
list.addMouseListener(listMouseListener);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
final GridBagConstraints gridBagConstraints = new GridBagConstraints();
final JPanel panel = new JPanel(new GridBagLayout());
panel.setPreferredSize(new Dimension(400, 400));
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.weightx = 0;
gridBagConstraints.weighty = 0;
gridBagConstraints.anchor = GridBagConstraints.NORTH;
panel.add(slider, gridBagConstraints);
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.weightx = 1;
gridBagConstraints.weighty = 1;
gridBagConstraints.anchor = GridBagConstraints.CENTER;
panel.add(scrollPane, gridBagConstraints);
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.weightx = 0;
gridBagConstraints.weighty = 0;
gridBagConstraints.anchor = GridBagConstraints.SOUTH;
panel.add(decoderPanel, gridBagConstraints);
((RootPaneContainer) frame).getContentPane().add(panel, BorderLayout.EAST);
}
/**
* Set the interfaces page counter to the page number
* and alter any other interface options to match.
*
* @param page page number as an int
*/
public void setPage(int page) {
if (decode_pdf.getDisplayView() == Display.CONTINUOUS_FACING
&& (page & 1) == 1 && page != 1) {
page--;
}
if (decode_pdf.getDisplayView() == Display.FACING) {
final boolean isSeparacteCover = decode_pdf.getPages().getBoolean(Display.BoolValue.SEPARATE_COVER);
if (isSeparacteCover ? (page & 1) == 1 && page != 1 : (page & 1) == 0) {
page--;
}
}
commonValues.setCurrentPage(page);
setPageNumber();
}
/**
* Method to set the viewers rotation from external sources.
*
* @param rot The rotation to use in the Viewer
*/
public void setRotationFromExternal(final int rot) {
rotation = rot;
rotationBox.setSelectedIndex(rotation / 90);
if (!Values.isProcessing()) {
decode_pdf.repaint();
}
}
/**
* Method to set the viewers scaling from external sources.
*
* @param scale The scaling to use in the Viewer as an enum
*/
public void setScalingFromExternal(final Object[] scale) {
String selectedScale = "";
if (scale.length == 1) {
final String scaleValue = scale[0].toString();
if (scaleValue != null) {
selectedScale = StringUtils.isNumber(scaleValue) ? scaleValue + '%' : scaleValue;
} else {
throw new IllegalArgumentException("Missing second parameter: No custom value set");
}
} else if (scale.length == 2) {
selectedScale = scale[1].toString();
}
scalingBox.setSelectedItem(selectedScale);
}
public Container getFrame() {
return frame;
}
public void showMessageDialog(final String message1) {
final boolean showMessage = customMessageHandler == null || customMessageHandler.showMessage(message1);
//check user has not setup message and if we still show message
if (showMessage) {
javax.swing.SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame, message1));
}
}
public void showMessageDialog(final Object message, final String title, final int type) {
final boolean showMessage = customMessageHandler == null || customMessageHandler.showMessage(message);
//check user has not setup message and if we still show message
if (showMessage) {
javax.swing.SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame, message, title, type));
}
}
public String showInputDialog(final Object message, final String title, final int type) {
String returnMessage = null;
//check user has not setup message and if we still show message
if (customMessageHandler != null) {
returnMessage = customMessageHandler.requestInput(new Object[]{message, title, title});
}
if (returnMessage == null) {
return JOptionPane.showInputDialog(frame, message, title, type);
} else {
return returnMessage;
}
}
public String showInputDialog(final String message) {
String returnMessage = null;
//check user has not setup message and if we still show message
if (customMessageHandler != null) {
returnMessage = customMessageHandler.requestInput(new String[]{message});
}
if (returnMessage == null) {
return JOptionPane.showInputDialog(frame, message);
} else {
return returnMessage;
}
}
public int showConfirmDialog(final String message, final String message2, final int option) {
int returnMessage = -1;
//check user has not setup message and if we still show message
if (customMessageHandler != null) {
returnMessage = customMessageHandler.requestConfirm(new Object[]{message, message2, String.valueOf(option)});
}
if (returnMessage == -1) {
return JOptionPane.showConfirmDialog(frame, message, message2, option);
} else {
return returnMessage;
}
}
public int showOverwriteDialog(final String file, final boolean yesToAllPresent) {
final int n;
int returnMessage = -1;
//check user has not setup message and if we still show message
if (customMessageHandler != null) {
returnMessage = customMessageHandler.requestConfirm(new Object[]{file, String.valueOf(yesToAllPresent)});
}
if (returnMessage != -1) {
return returnMessage;
}
if (yesToAllPresent) {
final Object[] buttonRowObjects = {
Messages.getMessage("PdfViewerConfirmButton.Yes"),
Messages.getMessage("PdfViewerConfirmButton.YesToAll"),
Messages.getMessage("PdfViewerConfirmButton.No"),
Messages.getMessage("PdfViewerConfirmButton.Cancel")
};
n = JOptionPane.showOptionDialog(frame,
file + '\n' + Messages.getMessage("PdfViewerMessage.FileAlreadyExists")
+ '\n' + Messages.getMessage("PdfViewerMessage.ConfirmResave"),
Messages.getMessage("PdfViewerMessage.Overwrite"),
JOptionPane.DEFAULT_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
buttonRowObjects,
buttonRowObjects[0]);
} else {
n = JOptionPane.showOptionDialog(frame,
file + '\n' + Messages.getMessage("PdfViewerMessage.FileAlreadyExists")
+ '\n' + Messages.getMessage("PdfViewerMessage.ConfirmResave"),
Messages.getMessage("PdfViewerMessage.Overwrite"),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null, null, null);
}
return n;
}
public void showMessageDialog(final Object info) {
final boolean showMessage = customMessageHandler == null || customMessageHandler.showMessage(info);
//check user has not setup message and if we still show message
if (showMessage) {
javax.swing.SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame, info));
}
}
public void showFirstTimePopup() {
//allow user to disable
final boolean showMessage = customMessageHandler == null || customMessageHandler.showMessage("first time popup");
if (!showMessage) {
return;
}
try {
final JPanel a = new JPanel();
a.setLayout(new BoxLayout(a, BoxLayout.Y_AXIS));
final MouseAdapter supportListener = new MouseAdapter() {
@Override
public void mouseEntered(final MouseEvent e) {
if (GUIDisplay.allowChangeCursor) {
a.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
}
@Override
public void mouseExited(final MouseEvent e) {
if (GUIDisplay.allowChangeCursor) {
a.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
@Override
public void mouseClicked(final MouseEvent e) {
try {
java.awt.Desktop.getDesktop().browse(new URI((Messages.getMessage("PdfViewer.SupportLink.Link"))));
} catch (final Exception e1) {
showMessageDialog(e1.getMessage() + ' ' + Messages.getMessage("PdfViewer.ErrorWebsite"));
}
}
};
final JLabel img = new JLabel(new ImageIcon(getClass().getResource("/org/jpedal/examples/viewer/res/supportScreenshot.png")));
img.setBorder(BorderFactory.createRaisedBevelBorder());
img.setAlignmentX(JLabel.CENTER_ALIGNMENT);
img.addMouseListener(supportListener);
a.add(img);
final JLabel supportLink = new JLabel("<html><center><u>" + Messages.getMessage("PdfViewer.SupportLink.Text1") + ' ' + Messages.getMessage("PdfViewer.SupportLink.Text2") + "</u></html>");
supportLink.setMaximumSize(new Dimension(245, 60));
supportLink.setForeground(Color.BLUE);
supportLink.addMouseListener(supportListener);
supportLink.setAlignmentX(JLabel.CENTER_ALIGNMENT);
a.add(supportLink);
a.add(Box.createRigidArea(new Dimension(10, 10)));
JOptionPane.showMessageDialog(
frame,
a,
Messages.getMessage("PdfViewerTitle.RunningFirstTime"),
JOptionPane.PLAIN_MESSAGE);
} catch (final Exception | Error e) {
System.err.println(Messages.getMessage("PdfViewerFirstRunDialog.Error") + ' ' + e);
}
}
public int showConfirmDialog(final Object message, final String title, final int optionType, final int messageType) {
int returnMessage = -1;
//check user has not setup message and if we still show message
if (customMessageHandler != null) {
returnMessage = customMessageHandler.requestConfirm(new Object[]{message, title, String.valueOf(optionType), String.valueOf(messageType)});
}
if (returnMessage == -1) {
return JOptionPane.showConfirmDialog(frame, message, title, optionType, messageType);
} else {
return returnMessage;
}
}
public void setDownloadProgress(final String message, final int percentage) {
downloadBar.setProgress(message, percentage);
setMultibox(new int[]{});
}
public void updateStatusMessage(final String message) {
statusBar.updateStatus(message, 0);
}
public void resetStatusMessage(final String message) {
statusBar.resetStatus(message);
}
public void setStatusProgress(final int size) {
statusBar.setProgress(size);
setMultibox(new int[]{});
}
/**
* Gets the current divider location for the split pane
*
* @return int values representing the current divider location
*/
public int getSplitDividerLocation() {
return displayPane.getDividerLocation();
}
/**
* Get the print dialog.
*
* @param printersList String array of all available printers
* @param defaultPrinter String representing the default printer
* @return a print dialog.
*/
public PrintPanel printDialog(final String[] printersList, final String defaultPrinter) {
final JDialog printDialog = new JDialog((Frame) null, Messages.getMessage("PdfViewerLabel.Printer"), true);
printDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
//get default resolution
String propValue = properties.getValue("defaultDPI");
int defaultDPI = -1;
if (propValue != null && !propValue.isEmpty()) {
try {
propValue = propValue.replaceAll("[^0-9]", "");
defaultDPI = Integer.parseInt(propValue);
} catch (final NumberFormatException e) {
LogWriter.writeLog("Caught an Exception " + e);
}
}
if (printPanel == null) {
printPanel = new PrintPanel(printersList, defaultPrinter, getPaperSizes(), defaultDPI, commonValues.getCurrentPage(), decode_pdf);
} else {
printPanel.resetDefaults(printersList, defaultPrinter, commonValues.getPageCount(), commonValues.getCurrentPage());
}
printDialog.getContentPane().add(printPanel);
printDialog.setSize(670, 415);
printDialog.setResizable(false);
printDialog.setLocationRelativeTo(frame);
printDialog.setName("printDialog");
printDialog.setVisible(true);
printDialog.remove(printPanel);
return printPanel;
}
/**
* Gets the paper sizes for use by the print dialog
*
* @return a PaperSizes object holding all paper sizes
*/
public PaperSizes getPaperSizes() {
if (paperSizes == null) {
paperSizes = new PaperSizes(properties.getValue("defaultPagesize"));
}
return paperSizes;
}
private void setThumbnails() {
final org.jpedal.utils.SwingWorker worker = new org.jpedal.utils.SwingWorker() {
@Override
public Object construct() {
if (thumbnails.isShownOnscreen()) {
setupThumbnailPanel();
thumbnails.generateOtherVisibleThumbnails(commonValues.getCurrentPage());
}
return null;
}
};
worker.start();
}
/**
* Set the search text field to be used for the menu bar search input
*
* @param searchText A JTextField to be used for search input
*/
public void setSearchText(final Object searchText) {
this.searchText = (JTextField) searchText;
}
/**
* Set the search list to be used to hold search results in menu bar search mode
*
* @param results an implementation of SearchList
*/
public void setResults(final SearchList results) {
if (searchInMenu && results.getResultCount() == 0) {
showMessageDialog(Messages.getMessage("PdfViewerFileMenuFind.noResultText") + " \"" + results.getSearchTerm() + '"', Messages.getMessage("PdfViewerFileMenuFind.noResultTitle"), JOptionPane.INFORMATION_MESSAGE);
}
}
/**
* Get the JTabbedPane, as an Object, used for the side tab bar
*
* @return JTabbedPane used for the side tab bar
*/
public JTabbedPane getSideTabBar() {
return navOptionsPanel;
}
private void enableSearchItems(final boolean enabled) {
if (searchInMenu) { //Menu Bar search
searchText.setEnabled(enabled);
options.setEnabled(enabled);
swButtons.getButton(Commands.NEXTRESULT).setEnabled(false);
swButtons.getButton(Commands.PREVIOUSRESULT).setEnabled(false);
} else if (swButtons.getButton(Commands.FIND) != null) { //External Window search
swButtons.getButton(Commands.FIND).setEnabled(enabled);
}
}
/**
* Removes the search window from view with option to clear search results
*
*/
private void removeSearchWindow() {
searchFrame.removeSearchWindow(false);
}
/**
* Alter property using properties files keys and values to alter the user interface
*
* @param value String representation of the property key
* @param set true to turn on option, false to turn off
*/
public void alterProperty(final String value, final boolean set) {
GUIModifier.alterProperty(value, set, this);
}
/**
* Refresh the user interface for the current properties
*/
public void updateInterface() {
GUIModifier.load(properties, this);
}
/**
* Method to dispose of objects used by the interface before closing.
*/
@SuppressWarnings({"OverlyLongMethod", "java:S138"})
public void dispose() {
//Only remove our AWTListeners
Toolkit.getDefaultToolkit().removeAWTEventListener(coordsListener);
if (frame != null) {
//Only remove our ComponentListeners
frame.removeComponentListener(resizeRedrawListener);
}
if (options != null) {
for (final ItemListener listener : options.getItemListeners()) {
options.removeItemListener(listener);
}
}
if (menu != null) {
for (final PopupMenuListener listener : menu.getPopupMenuListeners()) {
menu.removePopupMenuListener(listener);
}
menu.removeAll();
}
if (thumbnails != null) {
thumbnails.removeAllListeners();
thumbnails.dispose();
}
if (scrollListener != null) {
scrollListener.dispose();
}
if (mouseHandler != null) {
mouseHandler.dispose();
}
if (navButtons != null) {
navButtons.removeAll();
}
if (menuItems != null) {
menuItems.dispose();
}
if (coords != null) {
coords.removeAll();
}
if (navOptionsPanel != null) {
disposeNavOptions(navOptionsPanel);
}
if (thumbscroll != null) {
disposeThumbnailScroll(thumbscroll);
}
if (scrollPane != null) {
disposeScrollPane(scrollPane);
}
if (signaturesTree != null) {
signaturesTree.setCellRenderer(null);
signaturesTree.removeAll();
}
if (layersPanel != null) {
layersPanel.removeAll();
}
if (annotationsPanel != null) {
annotationsPanel.dispose();
}
if (searchFrame != null) {
searchFrame.dispose();
}
if (navToolBar != null) {
disposeNavToolBar(navToolBar);
}
if (pagesToolBar != null) {
disposePageToolBar(pagesToolBar);
}
//release memory at end
if (memoryMonitor != null) {
disposeMemoryMonitor();
}
//Ensure Timer is closed as does not use daemon thread
if (viewListener != null) {
viewListener.dispose();
}
//Below line causes a NullPointerException
if (searchFrame != null) {
removeSearchWindow();
}
if (swButtons != null) {
swButtons.dispose();
}
//display pane cleanup
if (displayPane != null) {
disposeDisplayPane();
}
if (attachmentsPanel != null) {
attachmentsPanel.dispose();
attachmentsPanel = null;
}
if (tree != null) {
final JTree disposeTree = tree.getTree();
if (disposeTree != null) {
disposeTree(disposeTree);
}
}
if (decode_pdf != null) {
removePageListener();
removeGUIListeners((Component) decode_pdf);
}
if (glassPane != null) {
removeGUIListeners(glassPane);
glassPane.removeAll();
}
if (lpane != null) {
for (final PropertyChangeListener listener : lpane.getPropertyChangeListeners()) {
lpane.removePropertyChangeListener(listener);
}
for (final ComponentListener listener : lpane.getComponentListeners()) {
lpane.removeComponentListener(listener);
}
lpane.removeAll();
}
if (containerForThumbnails != null) {
for (final PropertyChangeListener listener : containerForThumbnails.getPropertyChangeListeners()) {
containerForThumbnails.removePropertyChangeListener(listener);
}
containerForThumbnails.removeAll();
}
if (frame != null) {
frame.removeAll();
}
if (decode_pdf != null) {
decode_pdf.closePdfFile();
decode_pdf.dispose();
((Container) decode_pdf).removeAll();
}
setGlobalVariablesToNull();
tree = null;
scalingFloatValues = null;
rotationBox = null;
scalingBox = null;
}
private void setGlobalVariablesToNull() {
options = null;
menu = null;
scrollListener = null;
mouseHandler = null;
pageTitle = null;
bookmarksTitle = null;
signaturesTitle = null;
layersTitle = null;
annotationTitle = null;
attachmentsTitle = null;
currentCommandListener = null;
navButtons = null;
coords = null;
navOptionsPanel = null;
thumbscroll = null;
scrollPane = null;
textFont = null;
statusBar = null;
downloadBar = null;
pageCounter2 = null;
pageCounter3 = null;
signaturesTree = null;
layersPanel = null;
annotationsPanel = null;
searchFrame = null;
navToolBar = null;
pagesToolBar = null;
layersObject = null;
viewListener = null;
glassPane = null;
frame = null;
portfolioParent = null;
decode_pdf = null;
}
private static void disposePageToolBar(final JToolBar toolBar) {
for (final MouseListener listener : toolBar.getMouseListeners()) {
toolBar.removeMouseListener(listener);
}
for (final MouseMotionListener listener : toolBar.getMouseMotionListeners()) {
toolBar.removeMouseMotionListener(listener);
}
toolBar.removeAll();
}
private static void disposeNavToolBar(final JToolBar navBar) {
final Component[] comps = navBar.getComponents();
for (final Component comp : comps) {
for (final PropertyChangeListener listener : comp.getPropertyChangeListeners()) {
comp.removePropertyChangeListener(listener);
}
for (final ComponentListener listener : comp.getComponentListeners()) {
comp.removeComponentListener(listener);
}
}
navBar.removeAll();
}
private static void disposeScrollPane(final JScrollPane scrollPane) {
for (final PropertyChangeListener listener : scrollPane.getPropertyChangeListeners()) {
scrollPane.removePropertyChangeListener(listener);
}
for (final MouseWheelListener listener : scrollPane.getMouseWheelListeners()) {
scrollPane.removeMouseWheelListener(listener);
}
for (final MouseMotionListener listener : scrollPane.getMouseMotionListeners()) {
scrollPane.removeMouseMotionListener(listener);
}
for (final FocusListener listener : scrollPane.getFocusListeners()) {
scrollPane.removeFocusListener(listener);
}
scrollPane.removeAll();
}
private static void disposeNavOptions(final JTabbedPane navOptions) {
for (final MouseListener listener : navOptions.getMouseListeners()) {
navOptions.removeMouseListener(listener);
}
for (final ChangeListener listener : navOptions.getChangeListeners()) {
navOptions.removeChangeListener(listener);
}
navOptions.removeAll();
}
private static void disposeThumbnailScroll(final JScrollBar thumbnailScroll) {
for (final MouseListener listener : thumbnailScroll.getMouseListeners()) {
thumbnailScroll.removeMouseListener(listener);
}
for (final PropertyChangeListener listener : thumbnailScroll.getPropertyChangeListeners()) {
thumbnailScroll.removePropertyChangeListener(listener);
}
thumbnailScroll.removeAll();
}
private void disposeMemoryMonitor() {
memoryMonitor.stop();
final ActionListener[] al = memoryMonitor.getActionListeners();
for (int i = 0; i != al.length; i++) {
memoryMonitor.removeActionListener(al[i]);
}
}
private static void removeGUIListeners(final Component comp) {
for (final KeyListener listener : comp.getKeyListeners()) {
comp.removeKeyListener(listener);
}
for (final MouseListener listener : comp.getMouseListeners()) {
comp.removeMouseListener(listener);
}
for (final MouseWheelListener listener : comp.getMouseWheelListeners()) {
comp.removeMouseWheelListener(listener);
}
for (final MouseMotionListener listener : comp.getMouseMotionListeners()) {
comp.removeMouseMotionListener(listener);
}
}
private static void disposeTree(final JTree disposeTree) {
for (final MouseListener listener : disposeTree.getMouseListeners()) {
disposeTree.removeMouseListener(listener);
}
for (final KeyListener listener : disposeTree.getKeyListeners()) {
disposeTree.removeKeyListener(listener);
}
disposeTree.removeAll();
}
private void disposeDisplayPane() {
for (final PropertyChangeListener listener : displayPane.getPropertyChangeListeners()) {
displayPane.removePropertyChangeListener(listener);
}
for (final MouseListener listener : displayPane.getMouseListeners()) {
displayPane.removeMouseListener(listener);
}
for (final ComponentListener listener : displayPane.getComponentListeners()) {
displayPane.removeComponentListener(listener);
}
displayPane.removeAll();
}
/**
* Not part of API - used internally
*
* @return true if it is page turn scaling is appropriate
*/
public boolean getPageTurnScalingAppropriate() {
return pageTurnScalingAppropriate;
}
/**
* Not part of API - used internally
*
* @return the gui cursor object
*/
public SwingCursor getGUICursor() {
return guiCursor;
}
/**
* Checks if title matches the title of any of the side tab bar tabs
*
* @param title String title to check for.
* @return String title of tab found.
*/
public String getTitles(final String title) {
if (title.equals(pageTitle)) {
return pageTitle;
} else if (title.equals(bookmarksTitle)) {
return bookmarksTitle;
} else if (title.equals(signaturesTitle)) {
return signaturesTitle;
} else if (title.equals(layersTitle)) {
return layersTitle;
} else if (title.equals(annotationTitle)) {
return annotationTitle;
} else if (title.equals(attachmentsTitle)) {
return attachmentsTitle;
}
return null;
}
/**
* Get the GUIButtons object that holds all buttons used by the user interface
*
* @return GUIButtons implementation used by the user interface
*/
public Buttons getButtons() {
return swButtons;
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void setViewerIcon() {
if (frame instanceof JFrame) {
//Check if file location provided
final URL path = guiCursor.getURLForImage("icon.png");
if (path != null) {
try {
final BufferedImage fontIcon = JDeli.read(path.openStream());
((Window) frame).setIconImage(fontIcon);
} catch (final Exception e) {
LogWriter.writeLog("Exception attempting to set icon " + e);
}
}
}
}
/**
* Get the SwingMenuItems object that holds all the menu items used by the user interface.
*
* @return SwingMenuItems implementation used by the user interface
*/
public SwingMenuItems getMenuItems() {
return menuItems;
}
/**
* Key method that calls decodePage from GUI.java
*/
public void decodePage() {
if (SwingAnnotationPanel.addPanel()) {
annotationsPanel.getAnnotationListener().setFormMode(SwingAnnotationPanel.FORMMODE.CREATION);
}
//Ensure thumbnail scroll bar is updated when page changed
if (thumbscroll != null) {
setThumbnailScrollBarValue(commonValues.getCurrentPage() - 1);
}
decodeGUIPage();
}
/**
* Get the PdfDecoder object used to display the PDF in the user interface
*
* @return PdfDecoder object used for display
*/
public PdfDecoder getPdfDecoder() {
return ((PdfDecoder) decode_pdf);
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void addComboListenerAndLabel(final SwingCombo combo) {
//add listener
combo.addActionListener(currentCommandListener);
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void addGUIComboBoxes(final SwingCombo combo) {
swButtons.getTopButtons().add(combo);
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void setupCenterPanelBackground() {
String propValue = properties.getValue("replacePdfDisplayBackground");
Color background = new Color(190, 190, 190);
if (!propValue.isEmpty()
&& "true".equalsIgnoreCase(propValue)) {
propValue = properties.getValue("pdfDisplayBackground");
if (!propValue.isEmpty()) {
currentCommands.executeCommand(ViewerCommands.SETDISPLAYBACKGROUND, new Object[]{Integer.parseInt(propValue)});
}
background = new Color(Integer.parseInt(propValue));
} else {
if (decode_pdf.getDecoderOptions().getDisplayBackgroundColor() != null) {
background = decode_pdf.getDecoderOptions().getDisplayBackgroundColor();
} else if (decode_pdf.useNewGraphicsMode()) {
background = new Color(55, 55, 65);
}
}
((Component) decode_pdf).setBackground(background);
frame.setBackground(background);
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void setupComboBoxes() {
final String[] rotationValues = {"0", "90", "180", "270"};
final String[] scalingValues = {Messages.getMessage("PdfViewerScaleWindow.text"), Messages.getMessage("PdfViewerScaleHeight.text"),
Messages.getMessage("PdfViewerScaleWidth.text"),
"25%", "50%", "75%", "100%", "125%", "150%", "200%", "250%", "500%", "750%", "1000%"};
//set new default if appropriate
String choosenScaling = System.getProperty("org.jpedal.defaultViewerScaling");
//Only use value from properties is VM arguement not set
if (choosenScaling == null) {
choosenScaling = properties.getValue("startScaling");
switch (choosenScaling) {
case "Fit Height":
LogWriter.warning("Use of \"Fit Height\" for the property startScaling is deprecated. The Viewer will update this to PdfViewerScaleHeight for you if the file is writable. Otherwise, please update your properties file for the new value.");
choosenScaling = "PdfViewerScaleHeight";
break;
case "Fit Page":
LogWriter.warning("Use of \"Fit Page\" for the property startScaling is deprecated. The Viewer will update this to PdfViewerScaleWindow for you if the file is writable. Otherwise, please update your properties file for the new value.");
choosenScaling = "PdfViewerScaleWindow";
break;
case "Fit Width":
LogWriter.warning("Use of \"Fit Width\" for the property startScaling is deprecated. The Viewer will update this to PdfViewerScaleWidth for you if the file is writable. Otherwise, please update your properties file for the new value.");
choosenScaling = "PdfViewerScaleWidth";
break;
}
if (choosenScaling.startsWith("P")) { //Only time first character isn't a digit is when the value is a scaled based on page dimensions
choosenScaling = Messages.getMessage(choosenScaling + ".text");
}
}
if (choosenScaling != null) {
final int total = scalingValues.length;
for (int aa = 0; aa < total; aa++) {
if (scalingValues[aa].equals(choosenScaling)) {
defaultSelection = aa;
aa = total;
}
}
}
scalingBox = new SwingCombo(scalingValues);
scalingBox.setEditable(true);
scalingBox.setSelectedIndex(defaultSelection); //set default before we add a listener
//if you enable, remember to change rotation and quality Comboboxes
rotationBox = new SwingCombo(rotationValues);
rotationBox.setSelectedIndex(0); //set default before we add a listener
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void setupKeyboardControl() {
((Component) decode_pdf).addKeyListener(new KeyAdapter() {
int count;
int pageChange;
java.util.Timer t2;
@Override
public void keyPressed(final KeyEvent e) {
final JScrollBar scroll = scrollPane.getVerticalScrollBar();
final int keyPressed = e.getKeyCode();
if (keyPressed == KeyEvent.VK_LEFT || keyPressed == KeyEvent.VK_RIGHT) {
//Only use left and right arrows to change pages if page is smaller than display area
if (scrollPane.getWidth() > ((Component) decode_pdf).getWidth()) {
if (keyPressed == KeyEvent.VK_LEFT) { //change page
pageChange--;
} else { //change page
pageChange++;
}
}
} else if ((keyPressed == KeyEvent.VK_UP || keyPressed == KeyEvent.VK_DOWN) && (count == 0)) {
if (keyPressed == KeyEvent.VK_UP
&& scroll.getValue() == scroll.getMinimum()
&& commonValues.getCurrentPage() > 1) {
//change page
pageChange--;
} else if (keyPressed == KeyEvent.VK_DOWN
&& (scroll.getValue() == scroll.getMaximum() - scroll.getHeight() || scroll.getHeight() == 0)
&& commonValues.getCurrentPage() < decode_pdf.getPageCount()) {
//change page
pageChange++;
}
}
count++;
if (pageChange != 0) {
if (t2 != null) {
t2.cancel();
}
final TimerTask t = new TimerTask() {
@Override
public void run() {
int p = (commonValues.getCurrentPage() + pageChange);
if (p < 1) {
p = 1;
} else if (p > decode_pdf.getPageCount()) {
p = decode_pdf.getPageCount();
}
if (p != commonValues.getCurrentPage()) {
final String page = String.valueOf(p);
SwingUtilities.invokeLater(() -> {
currentCommands.executeCommand(ViewerCommands.GOTO, new Object[]{page});
if (scroll.getValue() == scroll.getMinimum()
&& commonValues.getCurrentPage() > 1) {
scroll.setValue(scroll.getMaximum());
} else if ((scroll.getValue() == scroll.getMaximum() - scroll.getHeight() || scroll.getHeight() == 0)
&& commonValues.getCurrentPage() < decode_pdf.getPageCount()) {
scroll.setValue(scroll.getMinimum());
}
});
}
pageChange = 0;
//Add cancel for the timer at the end of this task.
//This should ensure the timer is not kept active
if (t2 != null) {
t2.cancel();
}
}
};
//restart - if its not stopped it will trigger page update
t2 = new java.util.Timer();
t2.schedule(t, 500);
}
}
@Override
public void keyReleased(final KeyEvent e) {
count = 0;
}
});
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void setupPDFDisplayPane() {
if (SwingAnnotationPanel.addPanel()) {
glassPane = new JPanel();
glassPane.setLayout(null);
glassPane.setOpaque(false);
lpane.add(scrollPane, 0, 0);
lpane.add(glassPane, 1, 1);
}
thumbscroll = new JScrollBar(JScrollBar.VERTICAL, 0, 1, 0, 1);
thumbscroll.setName("ThumbnailScroll");
if (scrollListener == null) {
scrollListener = new SwingScrollListener(this, "true".equalsIgnoreCase(properties.getValue("previewOnSingleScroll")));
}
thumbscroll.addAdjustmentListener(scrollListener);
thumbscroll.addMouseListener(scrollListener);
containerForThumbnails.setLayout(new BorderLayout());
containerForThumbnails.add(thumbscroll, BorderLayout.EAST);
scrollPane.getViewport().add(((Component) decode_pdf));
if (SwingAnnotationPanel.addPanel()) {
containerForThumbnails.add(lpane, BorderLayout.CENTER);
} else {
containerForThumbnails.add(scrollPane, BorderLayout.CENTER);
}
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.getVerticalScrollBar().setUnitIncrement(80);
scrollPane.getHorizontalScrollBar().setUnitIncrement(80);
//Keyboard control of next/previous page
setupKeyboardControl();
}
/**
* Overrides method from GUI.java, see GUI.java for DOCS.
*/
private void setupBorderPanes() {
//Create a left-right split pane with tabs and add to main display
navOptionsPanel.setTabPlacement(JTabbedPane.LEFT);
navOptionsPanel.setOpaque(true);
//Use start size as min width to keep divider from covering tabs
navOptionsPanel.setMinimumSize(new Dimension(collapsedSize, 100));
navOptionsPanel.setName("NavPanel");
navOptionsPanel.setFocusable(false);
setupSidebarTitles();
displayPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, navOptionsPanel, containerForThumbnails);
displayPane.setOneTouchExpandable(false);
//update scaling when divider moved
displayPane.addPropertyChangeListener("dividerLocation", e -> {
//hack to get it to use current values instead of old values
scrollPane.getViewport().setSize((scrollPane.getViewport().getWidth() + (Integer) e.getOldValue() - (Integer) e.getNewValue()),
scrollPane.getViewport().getHeight());
//Keep track of the current divider position
final int dividerPos = (Integer) e.getNewValue();
if (tabsExpanded && (dividerPos > collapsedSize)) {
expandedSize = dividerPos;
}
scaleAndRotate();
});
if (DecoderOptions.isRunningOnMac && UIManager.getLookAndFeel().isNativeLookAndFeel()) {
navOptionsPanel.addTab(pageTitle, thumbnails);
navOptionsPanel.setTitleAt(navOptionsPanel.getTabCount() - 1, pageTitle);
if (thumbnails.isShownOnscreen()) {
navOptionsPanel.addTab(bookmarksTitle, tree);
navOptionsPanel.setTitleAt(navOptionsPanel.getTabCount() - 1, bookmarksTitle);
}
} else {
//Remove borders from thumbnail and bookmark tab in windows as looks better
tree.setBorder(null);
thumbnails.setBorder(null);
if (thumbnails.isShownOnscreen()) {
final VTextIcon textIcon1 = new VTextIcon(navOptionsPanel, pageTitle);
navOptionsPanel.addTab(null, textIcon1, thumbnails);
}
final VTextIcon textIcon2 = new VTextIcon(navOptionsPanel, bookmarksTitle);
navOptionsPanel.addTab(null, textIcon2, tree);
}
String propValue = properties.getValue("startSideTabOpen");
if (!propValue.isEmpty()) {
sideTabBarOpenByDefault = "true".equalsIgnoreCase(propValue);
}
propValue = properties.getValue("startSelectedSideTab");
if (!propValue.isEmpty()) {
startSelectedTab = Messages.getMessage("PdfViewerJPanel." + propValue);
}
if (!hasListener) {
hasListener = true;
navOptionsPanel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(final MouseEvent mouseEvent) {
handleTabbedPanes();
}
});
}
}
/**
* Sets up the position and visual style of the items on the bottom toolbar
* (page navigation buttons etc).
*/
private void setupBottomToolBarItems() {
pageCounter2.setEditable(true);
pageCounter2.setToolTipText(Messages.getMessage("PdfViewerTooltip.goto"));
pageCounter2.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(1, 1, 1, 1),
pageCounter2.getBorder()));
pageCounter2.setMargin(new Insets(0, 0, 0, 0));
pageCounter2.setColumns(2);
pageCounter2.setMaximumSize(pageCounter2.getPreferredSize());
pageCounter2.addActionListener(arg0 -> {
String value = pageCounter2.getText().trim();
//Check if mapped and if so set the actual page number
//This is updated later to the correct page label
final PdfObjectReader reader = decode_pdf.getIO();
if (reader != null) {
final PageLabels pageLabels = reader.getPageLabels();
if (pageLabels != null && pageLabels.containsValue(value)) {
final Set<Map.Entry<Integer, String>> entries = pageLabels.entrySet();
for (final Map.Entry<Integer, String> e : entries) {
if (e.getValue().equals(value)) {
value = e.getKey().toString();
break;
}
}
}
}
currentCommands.executeCommand(ViewerCommands.GOTO, new Object[]{value});
});
pageCounter2.setHorizontalAlignment(JTextField.CENTER);
setPageNumber();
pageCounter3 = new JLabel(Messages.getMessage("PdfViewerOfLabel.text") + ' ');
pageCounter3.setOpaque(false);
navToolBar.setLayout(new BoxLayout(navToolBar, BoxLayout.LINE_AXIS));
navToolBar.setFloatable(false);
pagesToolBar.setFloatable(false);
navButtons.setBorder(BorderFactory.createEmptyBorder());
navButtons.setLayout(new BorderLayout());
navButtons.setFloatable(false);
navButtons.setPreferredSize(new Dimension(5, 24));
}
/**
* Creates a glowing border around the PDFDisplayPane.
*/
private void setupPDFBorder() {
//add a border
if (decode_pdf.useNewGraphicsMode()) {
decode_pdf.setPDFBorder(new AbstractBorder() {
@Override
public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) {
final Graphics2D g2 = (Graphics2D) g;
final int cornerDepth = (glowThickness / 2) + 1;
//left
g2.setPaint(new GradientPaint(x, 0, glowOuterColor, x + glowThickness, 0, glowInnerColor));
g2.fillRect(x, y + glowThickness, glowThickness, height - (glowThickness * 2));
//bottom left corner
g2.setPaint(new GradientPaint(x - cornerDepth + glowThickness, y + height + cornerDepth - glowThickness, glowOuterColor, x + glowThickness, y + height - glowThickness, glowInnerColor));
g2.fillRect(x, y + height - glowThickness, glowThickness, glowThickness);
//below
g2.setPaint(new GradientPaint(0, y + height, glowOuterColor, 0, y + height - glowThickness, glowInnerColor));
g2.fillRect(x + glowThickness, y + height - glowThickness, width - (glowThickness * 2), glowThickness);
//bottom right corner
g2.setPaint(new GradientPaint(x + width + cornerDepth - glowThickness, y + height + cornerDepth - glowThickness, glowOuterColor, x + width - glowThickness, y + height - glowThickness, glowInnerColor));
g2.fillRect(x + width - glowThickness, y + height - glowThickness, glowThickness, glowThickness);
//right
g2.setPaint(new GradientPaint(x + width, 0, glowOuterColor, x + width - glowThickness, 0, glowInnerColor));
g2.fillRect(x + width - glowThickness, y + glowThickness, glowThickness, height - (glowThickness * 2));
//top right corner
g2.setPaint(new GradientPaint(x + width - glowThickness + cornerDepth, y + glowThickness - cornerDepth, glowOuterColor, x + width - glowThickness, y + glowThickness, glowInnerColor));
g2.fillRect(x + width - glowThickness, y, glowThickness, glowThickness);
//above
g2.setPaint(new GradientPaint(0, y, glowOuterColor, 0, y + glowThickness, glowInnerColor));
g2.fillRect(x + glowThickness, y, width - (glowThickness * 2), glowThickness);
//top left corner
g2.setPaint(new GradientPaint(x - cornerDepth + glowThickness, y - cornerDepth + glowThickness, glowOuterColor, x + glowThickness, y + glowThickness, glowInnerColor));
g2.fillRect(x, y, glowThickness, glowThickness);
//draw black over top
g2.setPaint(Color.black);
g2.drawRect(x + glowThickness, y + glowThickness, width - (glowThickness * 2), height - (glowThickness * 2));
}
@Override
public Insets getBorderInsets(final Component c, final Insets insets) {
insets.set(glowThickness, glowThickness, glowThickness, glowThickness);
return insets;
}
});
} else {
decode_pdf.setPDFBorder(BorderFactory.createLineBorder(Color.black, 1));
}
}
/**
* Creates the top two menu bars, the file loading and viewer properties one
* and the PDF toolbar, the one which controls printing, searching etc.
*/
private void createTopMenuBar() {
top.setLayout(new BorderLayout());
if (frame instanceof JFrame) {
((RootPaneContainer) frame).getContentPane().add(top, BorderLayout.NORTH);
} else {
frame.add(top, BorderLayout.NORTH);
}
}
/**
* Ensure Document is redrawn when frame is resized and scaling set to
* width, height or window.
*/
private void redrawDocumentOnResize() {
frame.addComponentListener(resizeRedrawListener);
}
/**
* Creates the Main Display Window for all of the Swing Content.
*
* @param width is of type int
* @param height is of type int
*/
private void createMainViewerWindow(final int width, final int height) {
if (frame instanceof final JFrame castFrame) {
castFrame.setSize(width, height);
castFrame.setLocationRelativeTo(null); //centre on screen
castFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
castFrame.addWindowListener(new FrameCloser(currentCommands, this, decode_pdf, thumbnails, commonValues, properties));
castFrame.setVisible(true);
}
}
/**
* Returns the RecentDocuments used to hand the recent document list
*
* @return RecentDocuments used by the user interface
*/
public RecentDocuments getRecentDocument() {
if (recent == null) {
recent = new RecentDocuments(PropertiesFile.getNoRecentDocumentsToDisplay());
}
return recent;
}
/**
* Create the RecentDocuments for the user interface
*/
public void setRecentDocument() {
recent = new RecentDocuments(PropertiesFile.getNoRecentDocumentsToDisplay());
}
/**
* Open a file in the Viewer
*
* @param fileToOpen String filename for the file to be opened
*/
public void openFile(final String fileToOpen) {
if (fileToOpen != null) {
OpenFile.open(fileToOpen, commonValues, searchFrame, this, decode_pdf, properties, thumbnails);
}
}
/**
* Open a file in the Viewer
*
* @param fileName String filename for the file to be opened
*/
public void open(final String fileName) {
OpenFile.open(fileName, commonValues, searchFrame, this, decode_pdf, properties, thumbnails);
}
/**
* Enable and make visible different parts of the page counter of all of it
*
* @param value PageCounter value for the different page counter elements
* @param enabled true to enable the counter section, false to disable.
* @param visibility true to make visible the counter section, false to disable.
*/
public void enablePageCounter(final PageCounter value, final boolean enabled, final boolean visibility) {
switch (value) {
case PAGECOUNTER1:
pageCounter1.setVisible(visibility);
pageCounter1.setEnabled(enabled);
break;
case PAGECOUNTER2:
pageCounter2.setVisible(visibility);
pageCounter2.setEnabled(enabled);
break;
case PAGECOUNTER3:
pageCounter3.setVisible(visibility);
pageCounter3.setEnabled(enabled);
break;
case ALL:
pageCounter1.setVisible(visibility);
pageCounter1.setEnabled(enabled);
pageCounter2.setVisible(visibility);
pageCounter2.setEnabled(enabled);
pageCounter3.setVisible(visibility);
pageCounter3.setEnabled(enabled);
break;
default:
System.out.println("No Value detected, please choose from Enum PageCounter in GUI.java");
break;
}
}
/**
* Set the text content for a section of the page counter.
*
* @param value PageCounter value for the different page counter elements
* @param text String value to be place in the page counter
*/
public void setPageCounterText(final PageCounter value, final String text) {
switch (value) {
case PAGECOUNTER1:
pageCounter1.setText(text);
break;
case PAGECOUNTER2:
pageCounter2.setText(text);
break;
case PAGECOUNTER3:
pageCounter3.setText(text);
break;
default:
System.out.println("No Value detected, please choose from Enum PageCounter in GUI.java");
break;
}
}
/**
* Get the page counter segments
*
* @param value PageCounter value for the different page counter elements
* @return an Object containing the section of the page counter
*/
public Object getPageCounter(final PageCounter value) {
switch (value) {
case PAGECOUNTER1:
return pageCounter1;
case PAGECOUNTER2:
return pageCounter2;
case PAGECOUNTER3:
return pageCounter3;
default:
System.out.println("No Value detected, please choose from Enum PageCounter in GUI.java");
return 0;
}
}
/**
* Update the page counter input size to accommodate the contents
*/
public void updateTextBoxSize() {
//Set textbox size
int col = (String.valueOf(commonValues.getPageCount())).length();
if (decode_pdf.getIO().getPageLabels() != null) {
final Collection<String> values = decode_pdf.getIO().getPageLabels().values();
for (final String value : values) {
final int length = value.length();
if (col < length) {
col = length;
}
}
}
if (decode_pdf.getDisplayView() == Display.FACING || decode_pdf.getDisplayView() == Display.CONTINUOUS_FACING) {
col *= 2;
}
if (col < 2) {
col = 2;
}
pageCounter2.setColumns(col);
pageCounter2.setMaximumSize(pageCounter2.getPreferredSize());
navToolBar.invalidate();
navToolBar.doLayout();
}
/**
* Enable and or set visibility for the cursor display for multi box
*
* @param enabled true to enable, false to disable
* @param visible true to make visible, false to make invisible
*/
public void enableCursor(final boolean enabled, final boolean visible) {
cursor.setEnabled(enabled);
cursor.setVisible(visible);
}
/**
* Enable and or set visibility for the memory bar display for multi box
*
* @param enabled true to enable, false to disable
* @param visible true to make visible, false to make invisible
*/
public void enableMemoryBar(final boolean enabled, final boolean visible) {
multibox.setEnabled(enabled);
multibox.setVisible(visible);
}
/**
* Enable and or set visibility for the navigation bar
*
* @param enabled true to enable, false to disable
* @param visible true to make visible, false to make invisible
*/
public void enableNavigationBar(final boolean enabled, final boolean visible) {
navButtons.setEnabled(enabled);
navButtons.setVisible(visible);
}
/**
* Enable and or set visibility for the download display for multi box
*
* @param enabled true to enable, false to disable
* @param visible true to make visible, false to make invisible
*/
public void enableDownloadBar(final boolean enabled, final boolean visible) {
downloadBar.setEnabled(enabled);
downloadBar.setVisible(visible);
}
/**
* Get the current count of tabs on the side tab bar
*
* @return int value representing the number of tabs currently visible
*/
public int getSidebarTabCount() {
return navOptionsPanel.getTabCount();
}
/**
* Get the title of the tab at the given position
*
* @param pos The position of the tab on the tabbed pane
* @return String title of the given tab or an empty String is tab not present.
*/
public String getSidebarTabTitleAt(final int pos) {
if (navOptionsPanel.getIconAt(pos) != null) {
return navOptionsPanel.getIconAt(pos).toString();
} else {
return "";
}
}
/**
* Remove the tab at the specified position from the side tab bar
*
* @param pos Tab index to removed.
*/
public void removeSidebarTabAt(final int pos) {
navOptionsPanel.remove(pos);
}
/**
* Get the divider location for the viewer
*
* @return double representation of the divider location
*/
private double getDividerLocation() {
return displayPane.getDividerLocation();
}
/**
* calculates the scalingToFit required to make the given area visible
*
* @param left float value representing the smallest value on the x axis
* @param right float value representing the largest value on the x axis
* @param top float value representing the largest value on the y axis
* @param bottom float value representing the smallest value on the y axis
* @return float value representing the scalingToFit value
*/
public float scaleToVisible(final float left, final float right, final float top, final float bottom) {
float width = scrollPane.getViewport().getWidth() - inset - inset;
final float height = scrollPane.getViewport().getHeight() - inset - inset;
if (displayPane != null) {
width -= displayPane.getDividerSize();
}
final float widthScaling = (right - left) / width;
final float heightScaling = (top - bottom) / height;
if (widthScaling > heightScaling) {
return decode_pdf.getDPIFactory().adjustScaling(widthScaling);
} else {
return decode_pdf.getDPIFactory().adjustScaling(heightScaling);
}
}
/**
* Get the thickness of the glow border
*
* @return thickness of glow as an int
*/
public static int getDropShadowDepth() {
return glowThickness;
}
/**
* Set up the split pane divider for use
*
* @param size size of the divider
* @param visibility true to set left side of divider visible and enabled, false to set disabled and invisible
*/
public void setupSplitPaneDivider(final int size, final boolean visibility) {
displayPane.setDividerSize(size);
displayPane.getLeftComponent().setEnabled(visibility);
displayPane.getLeftComponent().setVisible(visibility);
}
/**
* Get the start / collapsed position of the split pane divider
*
* @return divider position as a double
*/
private static double getStartSize() {
return collapsedSize;
}
/**
* Set the start / collapsed position of the split pane divider
*
* @param size position as an int
*/
public static void setStartSize(final int size) {
collapsedSize = size;
}
/**
* Get the page container for the PDF display area
*
* @return JScrollPane containing the PdfDecoder
*/
public JScrollPane getPageContainer() {
return scrollPane;
}
/**
* Enable and or set visibility for the status bar for multi box
*
* @param enabled true to enable, false to disable
* @param visible true to make visible, false to make invisible
*/
public void enableStatusBar(final boolean enabled, final boolean visible) {
statusBar.setEnabled(enabled);
statusBar.setVisible(visible);
}
/**
* Converts mouse / components coords to pdf page coords
*
* @param x :: The x coordinate of the cursors location in display area coordinates
* @param y :: The y coordinate of the cursors location in display area coordinates
* @param page :: The page we are currently on
* @return Point object of the cursor location in page coordinates
*/
public Point convertComponentCoordsToPageCoords(final int x, final int y, final int page) {
return SwingMouseUtils.getCoordsOnPage(x, y, page, decode_pdf, this, commonValues);
}
/**
* Set the display view options to be used to display the PDF.
*
* @param displayView int value for display view from Display
* @param orientation int value for display orientation from Display
*/
public void setDisplayView(final int displayView, final int orientation) {
final int lastDisplayView = decode_pdf.getDisplayView();
if (decode_pdf.isOpen() && lastDisplayView != displayView) {
final PdfObject collectionObj = decode_pdf.getIO().getPDFObject(PdfDictionary.Collection);
if (displayView == Display.PORTFOLIO_DETAIL || displayView == Display.PORTFOLIO_TILE) {
if (collectionObj == null) {
//Incorrect mode for file type so cancel display view change
showMessageDialog(Messages.getMessage("PdfPortfolio.nonPortfolioIncorrectDisplay"));
return;
} else {
switch (displayView) {
case Display.PORTFOLIO_DETAIL:
setupDetailPortfolioView();
break;
case Display.PORTFOLIO_TILE:
setupTilePortfolioView();
break;
}
}
} else {
//Only set up pdf view if we were in a portfolio mode
if ((lastDisplayView == Display.PORTFOLIO_DETAIL || lastDisplayView == Display.PORTFOLIO_TILE)) {
setupPdfView();
}
}
}
//Check for null as can be called before UI is set up
if (annotationsPanel != null && SwingAnnotationPanel.addPanel()) {
annotationsPanel.getAnnotationListener().setFormMode(SwingAnnotationPanel.FORMMODE.CREATION);
}
// remove listener if setup
if (viewListener != null) {
((Component) decode_pdf).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 && hasChanged) {
try {
decode_pdf.setPageParameters(scaling, pageNumber, decode_pdf.getDisplayRotation());
((Component) decode_pdf).invalidate();
((JComponent) decode_pdf).updateUI();
decode_pdf.decodePage(pageNumber);
//Hide annotations tab if not needed
if (decode_pdf.getDisplayView() == Display.SINGLE_PAGE) {
checkTabShown(annotationTitle);
} else {
removeTab(annotationTitle);
}
} catch (final Exception e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
}
//Only all search in certain modes
enableSearchItems(displayView == Display.SINGLE_PAGE
|| displayView == Display.CONTINUOUS
|| displayView == Display.CONTINUOUS_FACING);
// add listener if one not already there
if (viewListener == null) {
viewListener = new RefreshLayout(decode_pdf);
((Component) decode_pdf).addComponentListener(viewListener);
}
}
public boolean allowScrolling() {
return allowScrolling;
}
public boolean confirmClose() {
return confirmClose;
}
public void toogleAutoScrolling() {
allowScrolling = !allowScrolling;
}
public int getRotation() {
return rotation;
}
public float getScaling() {
return scaling;
}
/**
* Get the values used by the GUI
*
* @return Values object to hold various details for the viewer
*/
public Values getValues() {
return commonValues;
}
private void addCombo(final String tooltip, final int ID) {
final SwingCombo combo = switch (ID) {
case Commands.SCALING -> scalingBox;
case Commands.ROTATION -> rotationBox;
default -> null;
};
if (combo != null) {
combo.setID(ID);
if (!tooltip.isEmpty()) {
combo.setToolTipText(tooltip);
addGUIComboBoxes(combo);
addComboListenerAndLabel(combo);
}
}
}
/**
* get Map containing Form Objects setup for Unique Annotations
*
* @return Map
*/
public Map<FormObject, String> getHotspots() {
return Collections.unmodifiableMap(objs);
}
/**
* example code which sets up an individual icon for each annotation to display - only use
* if you require each annotation to have its own icon<p>
* To use this you ideally need to parse the annotations first -there is a method allowing you to
* extract just the annotations from the data.
*/
private void createUniqueAnnotationIcons() {
//and place to store so we can test later
//flush list if needed
if (objs == null) {
objs = new HashMap<>();
} else {
objs.clear();
}
//create Annots - you can replace with your own implementation using setExternalHandler()
((AnnotationHandler) decode_pdf.getExternalHandler(Options.UniqueAnnotationHandler)).handleAnnotations(decode_pdf, objs, commonValues.getCurrentPage());
}
/**
* Set the dpi value used to adjust scaling
*
* @param dpi int value to represent the dpi
*/
private void setDpi(final int dpi) {
decode_pdf.getDPIFactory().setDpi(dpi);
}
/**
* Get the thickness of the glow border
*
* @return int value of the glow border thickness
*/
@SuppressWarnings("MethodMayBeStatic")
public int getGlowThickness() {
return glowThickness;
}
/**
* Get the outer colour of the glow border
*
* @return Color used for the outer section of the glow border
*/
public Color getGlowOuterColor() {
return glowOuterColor;
}
/**
* Get the inner colour of the glow border
*
* @return Color used for the inner section of the glow border
*/
public Color getGlowInnerColor() {
return glowInnerColor;
}
/**
* Set the search frame to be used as part of the user interface
*
* @param searchFrame SwingSearchWindow object to be used as the search window
*/
public void setSearchFrame(final SwingSearchWindow searchFrame) {
this.searchFrame = searchFrame;
}
private void setRotation() {
if (rotation > 360) {
rotation -= 360;
}
if (getSelectedComboIndex(Commands.ROTATION) != (rotation / 90)) {
setSelectedComboIndex(Commands.ROTATION, (rotation / 90));
}
}
/**
* Get selected index for the specified combo-box
*
* @param ID int value specifying a combo-box
* @return int value of the selected index in the given combo-box or -1 if ID is not valid
*/
public int getSelectedComboIndex(final int ID) {
return switch (ID) {
case Commands.SCALING -> scalingBox.getSelectedIndex();
case Commands.ROTATION -> rotationBox.getSelectedIndex();
default -> -1;
};
}
/**
* Set selected index for the specified combo-box
*
* @param ID int value specifying a combo-box
* @param index int value of the index to select
*/
public void setSelectedComboIndex(final int ID, final int index) {
switch (ID) {
case Commands.SCALING:
scalingBox.setSelectedIndex(index);
break;
case Commands.ROTATION:
rotationBox.setSelectedIndex(index);
break;
}
}
/**
* Get selected item for the specified combo-box
*
* @param ID int value specifying a combo-box
* @return Object representing the selected item or null if ID is not recognised
*/
public Object getSelectedComboItem(final int ID) {
return switch (ID) {
case Commands.SCALING -> scalingBox.getSelectedItem();
case Commands.ROTATION -> rotationBox.getSelectedItem();
default -> null;
};
}
/**
* Set selected item for the specified combo-box
*
* @param ID int value specifying a combo-box
* @param index String value of the item to select
*/
public void setSelectedComboItem(final int ID, String index) {
switch (ID) {
case Commands.SCALING:
//When using any of the fit scalings, adding a % will break it
//Only add if scaling is a number
if (StringUtils.isNumber(index)) {
index += '%';
}
scalingBox.setSelectedItem(index);
break;
case Commands.ROTATION:
rotationBox.setSelectedItem(index);
break;
}
}
private void prepareForDecode() {
//Remove Image extraction outlines when page is changed
decode_pdf.getPages().setHighlightedImage(null);
resetRotationBox();
/* if running terminate first */
if (thumbnails.isShownOnscreen()) {
thumbnails.terminateDrawing();
}
if (thumbnails.isShownOnscreen()) {
setupThumbnailPanel();
final LinearThread linearizedBackgroundRenderer = (LinearThread) decode_pdf.getJPedalObject(PdfDictionary.LinearizedReader);
if (linearizedBackgroundRenderer != null && !linearizedBackgroundRenderer.isAlive()) {
thumbnails.drawThumbnails();
}
}
if (decode_pdf.getDisplayView() == Display.SINGLE_PAGE) {
setPageCounterText(PageCounter.PAGECOUNTER2, getPageLabel(commonValues.getCurrentPage()));
if (pageLabelDiffers(commonValues.getCurrentPage())) {
setPageCounterText(PageCounter.PAGECOUNTER3, "(" + commonValues.getCurrentPage() + ' ' + Messages.getMessage("PdfViewerOfLabel.text") + ' ' + commonValues.getPageCount() + ')');
} else {
setPageCounterText(PageCounter.PAGECOUNTER3, Messages.getMessage("PdfViewerOfLabel.text") + ' ' + commonValues.getPageCount());
}
}
updateTextBoxSize();
//ensure text and color extracted. If you do not need color, take out line for faster decode
decode_pdf.setExtractionMode(PdfDecoderInt.TEXT + PdfDecoderInt.TEXTCOLOR);
//remove any search highlight
decode_pdf.getTextLines().clearHighlights();
}
@SuppressWarnings({"OverlyLongMethod", "java:S138"})
private void performDecoding() {
Values.setProcessing(true);
setCursor(2);
if (LogWriter.isRunningFromIDE) {
start = System.currentTimeMillis();
}
try {
statusBar.updateStatus("Decoding Page", 0);
/*
* decode the page
*/
try {
decode_pdf.decodePage(commonValues.getCurrentPage());
final Object errorTracker = decode_pdf.getExternalHandler(Options.ErrorTracker);
if (debugMode && errorTracker instanceof final LimitDecodeTrackerNoSwing tracker) {
tracker.onPageChange();
}
//wait to ensure decoded
decode_pdf.waitForDecodingToFinish();
//value set from JVM flag org.jpedal.maxShapeCount=maxNumber
if (decode_pdf.getPageDecodeStatus(DecodeStatus.TooManyShapes)) {
final String status = "Too many shapes on page";
showMessageDialog(status);
}
if (!decode_pdf.getPageDecodeStatus(DecodeStatus.ImagesProcessed)) {
final String status = (Messages.getMessage("PdfViewer.ImageDisplayError") +
Messages.getMessage("PdfViewer.ImageDisplayError1") +
Messages.getMessage("PdfViewer.ImageDisplayError2") +
Messages.getMessage("PdfViewer.ImageDisplayError3") +
Messages.getMessage("PdfViewer.ImageDisplayError4") +
Messages.getMessage("PdfViewer.ImageDisplayError5") +
Messages.getMessage("PdfViewer.ImageDisplayError6") +
Messages.getMessage("PdfViewer.ImageDisplayError7"));
showMessageDialog(status);
}
/*
* Tell user if hinting is probably required
*/
if (decode_pdf.getPageDecodeStatus(DecodeStatus.TTHintingRequired)) {
final String status = Messages.getMessage("PdfCustomGui.ttHintingRequired");
showMessageDialog(status);
}
if (decode_pdf.getPageDecodeStatus(DecodeStatus.NonEmbeddedCIDFonts)) {
final String status = ("This page contains non-embedded CID fonts \n" +
decode_pdf.getPageDecodeStatusReport(DecodeStatus.NonEmbeddedCIDFonts) +
"\nwhich may need mapping to display correctly.\n" +
"See https://support.idrsolutions.com/jpedal/api-documents/pdf-fonts-how-they-work-and-how-they-can-be-displayed-in");
showMessageDialog(status);
}
//create custom annot icons
if (decode_pdf.getExternalHandler(Options.UniqueAnnotationHandler) != null) {
/*
* ANNOTATIONS code to create unique icons
*
* this code allows you to create a unique set on icons for any type of annotations, with
* an icons for every annotation, not just types.
*/
final FormFactory formfactory = decode_pdf.getFormRenderer().getFormFactory();
//swing needs it to be done with invokeLater
if (formfactory.getType() == FormFactory.SWING) {
final Runnable doPaintComponent2 = this::createUniqueAnnotationIcons;
SwingUtilities.invokeLater(doPaintComponent2);
} else {
createUniqueAnnotationIcons();
}
}
statusBar.updateStatus("Displaying Page", 0);
} catch (final Exception e) {
LogWriter.error(e, Messages.getMessage("PdfViewerError.Exception") + ' ' + e +
' ' + Messages.getMessage("PdfViewerError.DecodePage"));
Values.setProcessing(false);
}
//tell user if we had a memory error on decodePage
if (DecoderOptions.showErrorMessages) {
String status = decode_pdf.getPageDecodeReport();
if (status.contains("java.lang.OutOfMemoryError")) {
status = (Messages.getMessage("PdfViewer.OutOfMemoryDisplayError") +
Messages.getMessage("PdfViewer.OutOfMemoryDisplayError1") +
Messages.getMessage("PdfViewer.OutOfMemoryDisplayError2") +
Messages.getMessage("PdfViewer.OutOfMemoryDisplayError3") +
Messages.getMessage("PdfViewer.OutOfMemoryDisplayError4") +
Messages.getMessage("PdfViewer.OutOfMemoryDisplayError5"));
showMessageDialog(status);
}
}
Values.setProcessing(false);
setViewerTitle(); //restore title
if (LogWriter.isRunningFromIDE) {
/*
* show time and memory usage
*/
System.out
.println(((Runtime.getRuntime().totalMemory() - Runtime
.getRuntime().freeMemory()) / 1_000)
+ "K");
System.out.println((((float) Math.abs(((System
.currentTimeMillis() - start) / 100))) / 10)
+ "s");
}
if (decode_pdf.getPageCount() > 0 && thumbnails.isShownOnscreen() && decode_pdf.getDisplayView() == Display.SINGLE_PAGE) {
thumbnails.generateOtherVisibleThumbnails(commonValues.getCurrentPage());
}
} catch (final Exception e) {
LogWriter.error(e, "Exception thrown whilst decoding page in Viewer");
Values.setProcessing(false); // Remove processing flag so that the viewer can be exited.
setViewerTitle(); //restore title
}
}
/**
* called by nav functions to decode next page (in GUI code as needs to
* manipulate large part of GUI)
*/
private void decodeGUIPage() {
//Prepare GUI for decoding
prepareForDecode();
//kick-off thread to create pages
switch (decode_pdf.getDisplayView()) {
case Display.FACING:
scaleAndRotate();
scrollToPage(commonValues.getCurrentPage());
decode_pdf.getPages().decodeOtherPages(commonValues.getCurrentPage());
return;
case Display.CONTINUOUS, Display.CONTINUOUS_FACING:
//resize (ensure at least certain size)
//must be here as otherwise will not redraw if new page opened
//in multipage mode
scaleAndRotate();
scrollToPage(commonValues.getCurrentPage());
return;
case Display.PAGEFLOW:
return;
default:
break;
}
//stop user changing scaling while decode in progress
resetComboBoxes(false);
swButtons.setPageLayoutButtonsEnabled(false);
//Decoding happens here
performDecoding();
//Update multibox
statusBar.setProgress(100);
setMultibox(new int[]{});
//reanable user changing scaling
resetComboBoxes(true);
if (decode_pdf.getPageCount() > 1) {
swButtons.setPageLayoutButtonsEnabled(true);
}
if (frame != null) {
reinitialiseTabs(getDividerLocation() > getStartSize());
}
finishedDecoding = true;
//Ensure page is at the correct scaling and rotation for display
scaleAndRotate();
setCursor(1);
}
public void setViewerTitle() {
String title;
if (commonValues.getSelectedFile() == null) {
title = (windowTitle + ' ');
} else {
title = (windowTitle + ' ' + commonValues.getSelectedFile());
}
final PdfObject linearObj = (PdfObject) decode_pdf.getJPedalObject(PdfDictionary.Linearized);
if (linearObj != null) {
final LinearThread linearizedBackgroundReaderer = (LinearThread) decode_pdf.getJPedalObject(PdfDictionary.LinearizedReader);
if (linearizedBackgroundReaderer != null && linearizedBackgroundReaderer.isAlive()) {
title += " (still loading)";
} else {
title += " (Linearized)";
}
}
if (commonValues.isFormsChanged()) {
title = "* " + title;
}
if (FileAccessHelper.bb > 0) {
title = "(" + FileAccessHelper.bb + " days left) " + title;
}
setTitle(title);
}
private void setupSidebarTitles() {
pageTitle = Messages.getMessage("PdfViewerJPanel.thumbnails");
bookmarksTitle = Messages.getMessage("PdfViewerJPanel.bookmarks");
layersTitle = Messages.getMessage("PdfViewerJPanel.layers");
signaturesTitle = Messages.getMessage("PdfViewerJPanel.signatures");
annotationTitle = Messages.getMessage("PdfViewerJPanel.annotations");
attachmentsTitle = Messages.getMessage("PdfViewerJPanel.attachments");
}
/**
* Get the label of the given page. PDFs can specify a label for a page
* (such as using Roman numerals), the method will return the page label if
* one exists, otherwise it will return the default of the page number.
*
* @param pageNumber int value to present the page number
* @return String value representing the page label for the page.
*/
public String getPageLabel(final int pageNumber) {
if (commonValues.isPDF()) { //Only check labels if pdf
final String value = decode_pdf.getIO().convertPageNumberToLabel(pageNumber);
if (value != null) {
return value;
}
}
return String.valueOf(pageNumber);
}
/**
* Get if the page label for a given page differs from the default page numbers
*
* @param pageNumber int value representing the page number
* @return true if the page label differs, false otherwise
*/
private boolean pageLabelDiffers(final int pageNumber) {
final PdfObjectReader reader = decode_pdf.getIO();
if (reader != null) {
final String value = reader.convertPageNumberToLabel(pageNumber);
if (value != null) {
return !value.equals(String.valueOf(pageNumber));
}
}
return false;
}
public void hideVersionNumber() {
windowTitle = "JPedal PDF workflow tools";
setViewerTitle();
}
public void showVersionNumber() {
windowTitle = Messages.getMessage("PdfViewer.titlebar") + ' ' + version;
setViewerTitle();
}
/**
* Generic ENUMS for setting Swing values.
*/
public enum ScrollPolicy {
VERTICAL_AS_NEEDED, HORIZONTAL_AS_NEEDED, VERTICAL_NEVER, HORIZONTAL_NEVER
}
public enum PageCounter {
PAGECOUNTER1, PAGECOUNTER2, PAGECOUNTER3, ALL
}
/**
* class to repaint multiple views
*/
private class RefreshLayout extends ComponentAdapter {
final PdfDecoderInt decode_pdf;
final PageMoveTracker tracker = new PageMoveTracker();
int delay = 500;
RefreshLayout(final PdfDecoderInt pdf) {
decode_pdf = pdf;
final String value = properties.getValue("pageMoveDelay");
if (value != null && !value.isEmpty()) {
delay = Integer.parseInt(value);
}
}
@Override
public void componentMoved(final ComponentEvent e) {
tracker.startTimer(decode_pdf.getPages(), decode_pdf.getPageNumber(), (FileAccess) decode_pdf.getExternalHandler(Options.FileAccess), delay);
}
@Override
public void componentResized(final ComponentEvent e) {
final int displayView = decode_pdf.getDisplayView();
final PdfPageData pageData = decode_pdf.getPdfPageData();
final int page = decode_pdf.getPageNumber();
if (displayView != Display.SINGLE_PAGE && displayView != Display.PAGEFLOW) {
final Rectangle r = decode_pdf.getVisibleRect();
if (!(pageData.getScaledCropBoxWidth(page) > r.width
|| pageData.getScaledCropBoxHeight(page) > r.height)) {
scrollToPage(page);
}
tracker.startTimer(decode_pdf.getPages(), decode_pdf.getPageNumber(), (FileAccess) decode_pdf.getExternalHandler(Options.FileAccess), delay);
}
}
//fix submitted by Niklas Matthies
void dispose() {
tracker.dispose();
}
}
/**
* Remove the ComponentListener from the PdfDecoder and flush cached pages
*/
public void removePageListener() {
// remove listener if not removed by close
if (viewListener != null) {
//flush any cached pages
decode_pdf.getPages().flushPageCaches();
((Component) decode_pdf).removeComponentListener(viewListener);
viewListener.dispose();
viewListener = null;
}
}
/**
* Trigger page turn in magazine mode. Not to be used in other display modes.
*
* @param decode_pdf PdfDecoder holder the PDF
* @param commonValues Values object holding PDF values
* @param updatedTotal New page total after page turn
* @param rightTurn true if turning page right, false if turning left
*/
public void triggerPageTurnAnimation(final PdfDecoderInt decode_pdf, final Values commonValues, final int updatedTotal, final boolean rightTurn) {
float pageW = decode_pdf.getPdfPageData().getCropBoxWidth(1);
float pageH = decode_pdf.getPdfPageData().getCropBoxHeight(1);
if (decode_pdf.getPdfPageData().getRotation(1) % 180 == 90) {
final float temp = pageW;
pageW = pageH;
pageH = temp;
}
final Point corner = new Point();
corner.x = (int) (decode_pdf.getVisibleRect().getWidth() / 2);
if (rightTurn) {
corner.x -= pageW;
} else {
corner.x += pageW;
}
corner.y = (int) (decode_pdf.getInsetH() + pageH);
final Point cursor = new Point();
cursor.x = (int) ((decode_pdf.getVisibleRect().getWidth() / 2) + pageW);
if (rightTurn) {
cursor.x += pageW;
} else {
cursor.x -= pageW;
}
cursor.y = (int) (decode_pdf.getInsetH() + pageH);
final SwingGUI currentGUI = this;
final Thread animation = new Thread(() -> {
// Fall animation
int velocity = 1;
//ensure cursor is not outside expected range
if (rightTurn ? cursor.x <= corner.x : cursor.x >= corner.x) {
cursor.x = corner.x - 1;
}
//Calculate distance required
final double distX = (corner.x - cursor.x);
//Loop through animation
while (rightTurn ? cursor.getX() >= corner.getX() : cursor.getX() <= corner.getX()) {
//amount to move this time
double xMove = velocity * distX * 0.001;
//make sure always moves at least 1 pixel in each direction
if ((rightTurn && xMove > -1)) {
xMove = -1;
} else {
if ((!rightTurn && xMove < 1)) {
xMove = 1;
}
}
cursor.setLocation(cursor.getX() + xMove, cursor.getY());
if (rightTurn) {
decode_pdf.setUserOffsets((int) cursor.getX(), (int) cursor.getY(), OffsetOptions.INTERNAL_DRAG_CURSOR_BOTTOM_RIGHT);
} else {
decode_pdf.setUserOffsets((int) cursor.getX(), (int) cursor.getY(), OffsetOptions.INTERNAL_DRAG_CURSOR_BOTTOM_LEFT);
}
//Double speed til moving 32/frame
if (velocity < 32) {
velocity *= 2;
}
//sleep til next frame
try {
Thread.sleep(50);
} catch (final InterruptedException e) {
LogWriter.error(e, "Exception thrown while waiting for page turn animation to perform next frame");
}
}
//change page
commonValues.setCurrentPage(updatedTotal);
setPageNumber();
decode_pdf.setPageParameters(scaling, commonValues.getCurrentPage());
decodePage();
//unlock corner drag
PageNavigator.setPageTurnAnimating(false, currentGUI);
//hide turnover
decode_pdf.setUserOffsets(0, 0, OffsetOptions.INTERNAL_DRAG_BLANK);
});
animation.setDaemon(true);
//lock corner drag
PageNavigator.setPageTurnAnimating(true, this);
animation.start();
}
private void setCursor(final int type) {
if (type == 1) {
((Component) decode_pdf).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} else if (type == 2) {
((Component) decode_pdf).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
}
/**
* Not part of API - used internally
* 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
*/
private boolean setDisplayView2(final int displayView, final int orientation) {
final DecoderOptions options = decode_pdf.getDecoderOptions();
Display pages = decode_pdf.getPages();
final FileAccess fileAccess = (FileAccess) decode_pdf.getExternalHandler(Options.FileAccess);
final int pageNumber = decode_pdf.getPageNumber();
final ExternalHandlers externalHandlers = decode_pdf.getExternalHandler();
final PdfDecoder comp = (PdfDecoder) decode_pdf;
final int lastDisplayView = decode_pdf.getDisplayView();
options.setPageAlignment(orientation);
if (pages != null) {
pages.stopGeneratingPage();
}
final boolean hasChanged = displayView != lastDisplayView;
options.setDisplayView(displayView);
if (lastDisplayView != displayView && lastDisplayView == Display.PAGEFLOW && pages != null) {
pages.dispose();
}
final Object customSwingHandle = externalHandlers.getExternalHandler(Options.MultiPageUpdate);
//Set the default value, may be replaced for pageflow as not needed
if (decode_pdf.getFormRenderer() != null) {
decode_pdf.getFormRenderer().getCompData().setRootDisplayComponent(decode_pdf);
}
switch (displayView) {
case Display.SINGLE_PAGE:
if (pages == null || hasChanged) {
final DynamicVectorRenderer currentDisplay = decode_pdf.getDynamicRenderer();
pages = new SingleDisplay(pageNumber, currentDisplay, comp, options); //abacus-del
}
break;
case Display.PAGEFLOW:
if (pages instanceof org.jpedal.display.swing.PageFlowDisplay) { //adode-del abacus-del
return hasChanged; //adode-del abacus-del
} //adode-del abacus-del
//Remove root display to prevent annotations being added behind page flow display
//Without this pageflow display will not resize with the viewer.
if (decode_pdf.getFormRenderer() != null) {
decode_pdf.getFormRenderer().getCompData().setRootDisplayComponent(null);
}
if (lastDisplayView != Display.SINGLE_PAGE) {
setDisplayView(Display.SINGLE_PAGE, 0);
setDisplayView(Display.PAGEFLOW, 0);
return hasChanged;
}
((JScrollPane) comp.getParent().getParent()).setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
((JScrollPane) comp.getParent().getParent()).setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
pages = new org.jpedal.display.swing.PageFlowDisplay((SwingGUI) customSwingHandle, decode_pdf); //adode-del abacus-del
break;
default:
pages = new MultiDisplay(pageNumber, null, displayView, comp, options, fileAccess); //adode-del abacus-del
//pass in value if needed
final RenderChangeListener customRenderChangeListener = (RenderChangeListener) externalHandlers.getExternalHandler(Options.RenderChangeListener);
if (customRenderChangeListener != null) {
pages.setObjectValue(Options.RenderChangeListener, customRenderChangeListener);
}
break;
}
//enable pageFlow mode and setup slightly different display configuration
if (lastDisplayView == Display.PAGEFLOW) {
comp.removeAll();
//forms needs null layout manager
comp.setLayout(null);
((JScrollPane) comp.getParent().getParent()).setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
((JScrollPane) comp.getParent().getParent()).setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
final javax.swing.Timer t = new javax.swing.Timer(1_000, e -> comp.repaint());
t.setRepeats(false);
t.start();
}
//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();
pages.init(scaling, decode_pdf.getDisplayRotation(), decode_pdf.getPageNumber(), currentDisplay, true);
pages.setPageRotation(decode_pdf.getDisplayRotation()); //force update //adode-del
pages.forceRedraw();
pages.refreshDisplay();
comp.pages = pages;
return hasChanged;
}
/**
* Get the glass panes used to allow annotations draw in the viewer for
* adding to the PDF.
*
* @return a JPanel to act as a glass pane.
*/
public JPanel getGlassPane() {
return glassPane;
}
private void setupTilePortfolioView() {
if (tileView == null) {
tileView = new SwingPortfolioTile(this);
}
tileView.loadCollectionValues(decode_pdf);
if (detailView != null) {
portfolioParent.remove(detailView);
}
if (tileView != null) {
portfolioParent.remove(tileView);
}
portfolioParent.remove(displayPane);
portfolioParent.add(tileView, BorderLayout.CENTER);
menuItems.getMenuItem(Commands.PORTFOLIO_TILES).setSelected(true);
menuItems.getMenuItem(Commands.PORTFOLIO_DETAIL).setSelected(false);
portfolioParent.repaint();
frame.validate();
}
private void setupDetailPortfolioView() {
if (detailView == null) {
detailView = new SwingPortfolioDetail(this);
}
detailView.loadCollectionValues(decode_pdf);
if (detailView != null) {
portfolioParent.remove(detailView);
}
if (tileView != null) {
portfolioParent.remove(tileView);
}
portfolioParent.remove(displayPane);
portfolioParent.add(detailView, BorderLayout.CENTER);
menuItems.getMenuItem(Commands.PORTFOLIO_TILES).setSelected(false);
menuItems.getMenuItem(Commands.PORTFOLIO_DETAIL).setSelected(true);
portfolioParent.repaint();
frame.validate();
}
private void setupPdfView() {
if (detailView != null) {
portfolioParent.remove(detailView);
}
if (tileView != null) {
portfolioParent.remove(tileView);
}
portfolioParent.remove(displayPane);
portfolioParent.add(displayPane, BorderLayout.CENTER);
portfolioParent.validate();
//Ensure display pane is displayed correctly
displayPane.repaint();
scaleAndRotate();
}
@SuppressWarnings({"OverlyLongMethod", "java:S138"})
public void setupGUIElementsForFiletype(final Values.FileType type) {
//Set Visibility of items (also check properties)
swButtons.getButton(Commands.ROTATELEFT).setVisible((type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("RotateLeftbutton")));
swButtons.getButton(Commands.ROTATERIGHT).setVisible((type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("RotateRightbutton")));
swButtons.getButton(Commands.ZOOMIN).setVisible((type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("ZoomInbutton")));
swButtons.getButton(Commands.ZOOMOUT).setVisible((type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("ZoomOutbutton")));
swButtons.getButton(Commands.MOUSEMODE).setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("MouseModeButton")));
swButtons.getButton(Commands.FIRSTPAGE).setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Firstbottom")));
swButtons.getButton(Commands.FBACKPAGE).setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Back10bottom")));
swButtons.getButton(Commands.BACKPAGE).setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Backbottom")));
swButtons.getButton(Commands.FORWARDPAGE).setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Forwardbottom")));
swButtons.getButton(Commands.FFORWARDPAGE).setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Forward10bottom")));
swButtons.getButton(Commands.LASTPAGE).setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Lastbottom")));
swButtons.getButton(Commands.SINGLE).setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Singlebottom")));
swButtons.getButton(Commands.CONTINUOUS).setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Continuousbottom")));
swButtons.getButton(Commands.FACING).setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Facingbottom")));
swButtons.getButton(Commands.CONTINUOUS_FACING).setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Continuousfacingbottom")));
swButtons.getButton(Commands.PAGEFLOW).setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("PageFlowbottom")));
swButtons.getButton(Commands.PRINT).setVisible((type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("Printbutton")));
swButtons.getButton(Commands.SNAPSHOT).setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Snapshotbutton")));
multibox.setVisible(type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Memorybottom")));
navButtons.setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("ShowNavigationbar")));
//Handle Combo boxes
getCombo(Commands.SCALING).setVisibility((type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("Scalingdisplay")));
getCombo(Commands.ROTATION).setVisibility((type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("Rotationdisplay")));
final boolean showPageCounter = "true".equalsIgnoreCase(properties.getValue("Gotobottom"));
pageCounter1.setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff())) && showPageCounter);
pageCounter2.setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff())) && showPageCounter);
pageCounter3.setVisible((type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff())) && showPageCounter);
final boolean showMenubarSearch = "2".equalsIgnoreCase(properties.getValue("searchWindowType"));
swButtons.getButton(Commands.NEXTRESULT).setVisible(type == Values.FileType.PDF && showMenubarSearch);
swButtons.getButton(Commands.PREVIOUSRESULT).setVisible(type == Values.FileType.PDF && showMenubarSearch);
searchText.setVisible(type == Values.FileType.PDF);
if (options != null) {
options.setVisible(type == Values.FileType.PDF && showMenubarSearch);
}
navOptionsPanel.setVisible((type == Values.FileType.PDF)
&& "true".equalsIgnoreCase(properties.getValue("ShowSidetabbar")));
//No property for this value as always needed
menuItems.setMenuItemVisibility(Commands.PORTFOLIOMENU, type == Values.FileType.PORTFOLIO);
//Set enabled for items
menuItems.setMenuItemVisibility(Commands.SAVE, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Save")));
menuItems.setMenuItemVisibility(Commands.SAVEFORM, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Resaveasforms")));
menuItems.setMenuItemVisibility(Commands.DOCINFO, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Documentproperties")));
menuItems.setMenuItemVisibility(Commands.FIND, (type == Values.FileType.PDF)
&& "true".equalsIgnoreCase(properties.getValue("Find")));
menuItems.setMenuItemVisibility(Commands.PRINT, (type == Values.FileType.PDF)
&& "true".equalsIgnoreCase(properties.getValue("Print")));
menuItems.setMenuItemVisibility(Commands.SEPARATECOVER, (type == Values.FileType.PDF)
&& "true".equalsIgnoreCase(properties.getValue("separateCover")));
menuItems.setMenuItemVisibility(Commands.PANMODE, (type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("panMode")));
menuItems.setMenuItemVisibility(Commands.TEXTSELECT, (type == Values.FileType.PDF)
&& "true".equalsIgnoreCase(properties.getValue("textSelect")));
menuItems.setMenuItemVisibility(Commands.FULLSCREEN, (type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("Fullscreen")));
menuItems.setMenuItemVisibility(Commands.FIRSTPAGE, (type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Firstpage")));
menuItems.setMenuItemVisibility(Commands.BACKPAGE, (type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Backpage")));
menuItems.setMenuItemVisibility(Commands.GOTO, (type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Goto")));
menuItems.setMenuItemVisibility(Commands.FORWARDPAGE, (type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Forwardpage")));
menuItems.setMenuItemVisibility(Commands.LASTPAGE, (type == Values.FileType.PDF || (type == Values.FileType.IMAGES && commonValues.isMultiTiff()))
&& "true".equalsIgnoreCase(properties.getValue("Lastpage")));
menuItems.setMenuItemVisibility(Commands.EDITMENU, (type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("EditMenu")));
menuItems.setMenuItemVisibility(Commands.PAGELAYOUTMENU, (type == Values.FileType.PDF || type == Values.FileType.IMAGES)
&& "true".equalsIgnoreCase(properties.getValue("PagelayoutMenu")));
menuItems.setMenuItemVisibility(Commands.COPY, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Copy")));
menuItems.setMenuItemVisibility(Commands.SELECTALL, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Selectall")));
menuItems.setMenuItemVisibility(Commands.DESELECTALL, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Deselectall")));
menuItems.setMenuItemVisibility(Commands.IMAGES, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Images")));
menuItems.setMenuItemVisibility(Commands.TEXT, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Text")));
menuItems.setMenuItemVisibility(Commands.CONTENTMENU, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("ContentMenu")));
menuItems.setMenuItemVisibility(Commands.BITMAP, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Bitmap")));
menuItems.setMenuItemVisibility(Commands.SIGN, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("Sign")));
menuItems.setMenuItemVisibility(Commands.EXPORTMENU, type == Values.FileType.PDF
&& "true".equalsIgnoreCase(properties.getValue("ExportMenu")));
menuItems.setMenuItemVisibility(Commands.XREF, type == Values.FileType.PDF
&& debugMode);
menuItems.setMenuItemVisibility(Commands.XREFSTREAM, type == Values.FileType.PDF
&& debugMode);
menuItems.setMenuItemVisibility(Commands.OBJECTS, type == Values.FileType.PDF
&& debugMode);
menuItems.setMenuItemVisibility(Commands.PORTFOLIOMENU, type == Values.FileType.PORTFOLIO);
if (type == Values.FileType.IMAGES) {
currentCommands.executeCommand(ViewerCommands.PANMODE, null);
} else {
currentCommands.executeCommand(ViewerCommands.TEXTSELECT, null);
}
//Check for double separaters and remove as needed
swButtons.checkButtonSeparators();
menuItems.checkMenuItemSeparators();
frame.revalidate();
}
public void setDebugMode(final boolean enableDebugMode) {
debugMode = enableDebugMode;
menuItems.setDebugMode(enableDebugMode);
imageNameCollector = new ImageNameCollector() {
final HashMap<String, String> names = new HashMap<>();
@Override
public void put(final String imageName, final String cacheName) {
names.put(decode_pdf.getPageNumber() + "-" + imageName, cacheName);
}
@Override
public String getCacheName(final String imageName) {
return names.get(decode_pdf.getPageNumber() + "-" + imageName);
}
@Override
public String getCacheNameFromIDToken(final int tokenNumber) {
final String prefix = decode_pdf.getPageNumber() + "-";
final String suffix = "-IN-" + tokenNumber;
for (final String name : names.keySet()) {
if (name.startsWith(prefix) && name.endsWith(suffix)) {
return names.get(name);
}
}
return null;
}
@Override
public boolean cacheAllToDisk() {
return true;
}
};
decode_pdf.setImageNameCollector(imageNameCollector);
}
/**
* Listener to allow Coordinates to update across the whole page including over form components
*/
private final class CoordinateListener implements AWTEventListener {
//Required to prevent recursive calling of listener
boolean listenerCalled;
@Override
public void eventDispatched(final AWTEvent e) {
if (decode_pdf != null && !listenerCalled && e.getID() == MouseEvent.MOUSE_MOVED) {
listenerCalled = true;
Point mousePosition = ((Component) decode_pdf).getMousePosition();
if ((annotationsPanel != null && SwingAnnotationPanel.addPanel()) && glassPane != null) {
mousePosition = glassPane.getMousePosition();
}
if (mousePosition != null) {
mouseHandler.allowMouseCoordsUpdate();
((Component) decode_pdf).dispatchEvent(new MouseEvent(((Component) decode_pdf),
e.getID(),
System.currentTimeMillis(),
0,
mousePosition.x,
mousePosition.y,
0,
false));
}
listenerCalled = false;
}
}
}
/**
* Listener to redraw the Viewer display area when component is resized
*/
private final class ResizeRedrawListener extends ComponentAdapter {
@Override
public void componentResized(final ComponentEvent e) {
if (((Component) decode_pdf).getParent() != null
&& (getSelectedComboIndex(Commands.SCALING) < 3 || decode_pdf.getDisplayView() == Display.FACING)) { //always rezoom in facing mode for turnover
scaleAndRotate();
}
}
}
}
|