diff --git a/pom.xml b/pom.xml index cde38760..51917fe7 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,11 @@ kernel ${itext.version} + + com.itextpdf + search + ${itext.version} + org.dom4j dom4j @@ -309,18 +314,6 @@ sh.tak.appbundler appbundle-maven-plugin 1.2.0 - - - org.codehaus.plexus - plexus-archiver - 3.6.0 - - - org.apache.velocity - velocity-tools - 2.0 - - package @@ -329,6 +322,18 @@ + + + org.apache.velocity + velocity-tools + 2.0 + + + org.codehaus.plexus + plexus-archiver + 3.6.0 + + com.itextpdf.rups.RupsLauncher Info.plist @@ -340,4 +345,4 @@ - \ No newline at end of file + diff --git a/src/main/java/com/itextpdf/rups/RupsConfiguration.java b/src/main/java/com/itextpdf/rups/RupsConfiguration.java index 3cc23551..e7253c3d 100644 --- a/src/main/java/com/itextpdf/rups/RupsConfiguration.java +++ b/src/main/java/com/itextpdf/rups/RupsConfiguration.java @@ -74,21 +74,27 @@ public enum RupsConfiguration { INSTANCE; private static final String DEFAULT_CONFIG_PATH = "/config/default.properties"; + private static final String ICON_CONFIG_PATH = "/config/icon.properties"; private static final String DEFAULT_HOME_VALUE = "home"; private static final String CLOSE_OPERATION_KEY = "ui.closeoperation"; private static final String DUPLICATE_OPEN_FILES_KEY = "rups.duplicatefiles"; private static final String HOME_FOLDER_KEY = "user.home"; private static final String LOCALE_KEY = "user.locale"; private static final String LOOK_AND_FEEL_KEY = "ui.lookandfeel"; + private static final String ICON_KEY = "ui.lookandfeel"; private final Preferences systemPreferences; private final Properties defaultProperties; private final Properties temporaryProperties; + private final Properties iconProperties; + + RupsConfiguration() { this.defaultProperties = loadDefaultProperties(); this.temporaryProperties = new Properties(); this.systemPreferences = Preferences.userNodeForPackage(RupsConfiguration.class); + this.iconProperties = loadIconProperties(); initializeSystemDefaults(this.defaultProperties, this.systemPreferences); } @@ -307,6 +313,22 @@ private Properties loadDefaultProperties() { return properties; } + private Properties loadIconProperties() { + final InputStream resourceAsStream = RupsConfiguration.class.getResourceAsStream(ICON_CONFIG_PATH); + final Properties properties = new Properties(); + + if (resourceAsStream != null) { + try { + properties.load(resourceAsStream); + resourceAsStream.close(); + } catch (IOException e) { + LoggerHelper.error(Language.ERROR_LOADING_ICON_DEFINITIONS.getString(), e, RupsConfiguration.class); + } + } + + return properties; + } + private void initializeSystemDefaults(Properties defaultProperties, Preferences systemPreferences) { try { final String[] keys = systemPreferences.keys(); @@ -337,4 +359,8 @@ private String getValueFromSystemPreferences(String key, String defaultValue) { private String getValueFromSystemPreferences(String key) { return this.systemPreferences.get(key, this.defaultProperties.getProperty(key)); } -} + + public String getIconFor(String key){ + return this.iconProperties.getProperty(key, "default"); + } +} \ No newline at end of file diff --git a/src/main/java/com/itextpdf/rups/controller/IRupsController.java b/src/main/java/com/itextpdf/rups/controller/IRupsController.java index 1c3949a9..78217717 100644 --- a/src/main/java/com/itextpdf/rups/controller/IRupsController.java +++ b/src/main/java/com/itextpdf/rups/controller/IRupsController.java @@ -42,6 +42,8 @@ This file is part of the iText (R) project. */ package com.itextpdf.rups.controller; +import com.itextpdf.kernel.pdf.PdfObject; +import com.itextpdf.search.ISearchHandler; import com.itextpdf.rups.model.PdfFile; import java.awt.Component; diff --git a/src/main/java/com/itextpdf/rups/controller/RupsController.java b/src/main/java/com/itextpdf/rups/controller/RupsController.java index 5a6a5103..a8e05990 100644 --- a/src/main/java/com/itextpdf/rups/controller/RupsController.java +++ b/src/main/java/com/itextpdf/rups/controller/RupsController.java @@ -43,6 +43,7 @@ This file is part of the iText (R) project. package com.itextpdf.rups.controller; import com.itextpdf.rups.RupsConfiguration; +import com.itextpdf.search.ISearchHandler; import com.itextpdf.rups.event.AllFilesClosedEvent; import com.itextpdf.rups.event.OpenFileEvent; import com.itextpdf.rups.event.RupsEvent; @@ -77,6 +78,8 @@ public class RupsController extends Observable public RupsController(Dimension dimension, RupsTabbedPane rupsTabbedPane) { this.rupsTabbedPane = rupsTabbedPane; + //TODO: SearchHandler -> TabbedPane -> InstanceController, SearchHandler -> SearchBar.handler + this.dimension = dimension; } @@ -90,6 +93,10 @@ public Component getMasterComponent() { return rupsTabbedPane.getJTabbedPane(); } + public ISearchHandler getSearchHandler(){ + return rupsTabbedPane.getCurrentController().getSearchHandler(); + } + @Override public final void update(Observable o, Object arg) { //Events that have come from non observable classes: ObjectLoader and FileChooserAction @@ -127,6 +134,7 @@ public final void closeCurrentFile() { } } + @Override public final PdfFile getCurrentFile() { return this.rupsTabbedPane.getCurrentFile(); diff --git a/src/main/java/com/itextpdf/rups/controller/RupsInstanceController.java b/src/main/java/com/itextpdf/rups/controller/RupsInstanceController.java index b4a2b3d7..fa9cdeac 100644 --- a/src/main/java/com/itextpdf/rups/controller/RupsInstanceController.java +++ b/src/main/java/com/itextpdf/rups/controller/RupsInstanceController.java @@ -46,6 +46,8 @@ This file is part of the iText (R) project. import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.utils.CompareTool; +import com.itextpdf.search.ISearchHandler; +import com.itextpdf.rups.controller.search.PdfSearchHandler; import com.itextpdf.rups.event.CloseDocumentEvent; import com.itextpdf.rups.event.PostCompareEvent; import com.itextpdf.rups.event.RupsEvent; @@ -57,6 +59,8 @@ This file is part of the iText (R) project. import com.itextpdf.rups.view.Console; import com.itextpdf.rups.view.Language; import com.itextpdf.rups.view.PageSelectionListener; +import com.itextpdf.rups.view.RupsPanel; +import com.itextpdf.rups.view.RupsSearchBar; import com.itextpdf.rups.view.contextmenu.ConsoleContextMenu; import com.itextpdf.rups.view.contextmenu.ContextMenuMouseListener; import com.itextpdf.rups.view.itext.treenodes.PdfObjectTreeNode; @@ -105,6 +109,9 @@ public class RupsInstanceController extends Observable */ private final PdfReaderController readerController; + private final ISearchHandler searchHandler; + + private RupsSearchBar searchBar; /** * Contains all other components: the page panel, the outline tree, etc. */ @@ -134,6 +141,13 @@ public RupsInstanceController(Dimension dimension, JPanel owner) { readerController = new PdfReaderController(this, this); addObserver(readerController); + searchHandler = new PdfSearchHandler(); + searchBar = RupsSearchBar.getSearchBar(readerController.pdfTree); + + //TODO: Find a neater way of identifying document section in focus. + readerController.getPdfTree().addTreeSelectionListener((TreeSelectionListener) searchHandler); + searchBar.setSearchHandler(((RupsPanel) owner).getRupsSearchHandler()); + // creating the master component masterComponent = new JSplitPane(); masterComponent.setOrientation(JSplitPane.VERTICAL_SPLIT); @@ -158,6 +172,7 @@ public RupsInstanceController(Dimension dimension, JPanel owner) { info.add(readerController.getObjectPanel(), JSplitPane.LEFT); final JTabbedPane editorPane = readerController.getEditorTabs(); final JScrollPane cons = new JScrollPane(console.getTextArea()); + console.getTextArea().addMouseListener( new ContextMenuMouseListener(ConsoleContextMenu.getPopupMenu(console.getTextArea()), console.getTextArea())); @@ -362,6 +377,10 @@ public void valueChanged(TreeSelectionEvent evt) { } } + public ISearchHandler getSearchHandler() { + return searchHandler; + } + // page navigation @Override diff --git a/src/main/java/com/itextpdf/rups/controller/search/PdfSearchHandler.java b/src/main/java/com/itextpdf/rups/controller/search/PdfSearchHandler.java new file mode 100644 index 00000000..6143d73e --- /dev/null +++ b/src/main/java/com/itextpdf/rups/controller/search/PdfSearchHandler.java @@ -0,0 +1,75 @@ +package com.itextpdf.rups.controller.search; + +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfIndirectReference; +import com.itextpdf.kernel.pdf.PdfObject; +import com.itextpdf.search.model.ESearchScope; +import com.itextpdf.search.model.ISearchFilter; +import com.itextpdf.search.model.ISearchResult; +import com.itextpdf.search.model.SearchContext; + +import javax.swing.event.TreeSelectionEvent; +import java.util.HashMap; +import java.util.Map; + +public class PdfSearchHandler extends RupsSearchHandler { + + Map> contexts; + + PdfIndirectReference currentTarget; + + public PdfSearchHandler(){ + super(); + contexts = new HashMap<>(); + } + + + @Override + public ISearchResult find(SearchContext searchContext, ISearchFilter filter) { + if((null == filter || (null == searchContext.getSearchScope()))) + return null; //TODO: Replace this. + + switch(searchContext.getSearchScope()) { + case ALL_DOCUMENTS: + break; + case DOCUMENT: + PdfDocument testdoc = searchContext.getTarget().getIndirectReference().getDocument(); + break; + default: + case SELECTION: + PdfObject testobj = searchContext.getTarget().getIndirectReference().getRefersTo(true); + } + return null; + } + + public ISearchFilter getNewFilter() { + return new PdfStreamSearchFilter(); + } + + @Override + public SearchContext getNewContext(ESearchScope itemScope){ + SearchContext newContext = super.getNewContext(); + newContext.setTarget(getCurrentTarget()); + Map scopeContexts = contexts.getOrDefault(currentTarget, new HashMap()); + scopeContexts.put(itemScope, newContext); + contexts.put(getCurrentTarget(), scopeContexts); + return getContext(currentTarget, ESearchScope.SELECTION); + + } + + @Override + public SearchContext getContext(PdfIndirectReference currentTarget, ESearchScope selectedItem) { + Map scopeContexts = contexts.getOrDefault(currentTarget, new HashMap()); + return scopeContexts.getOrDefault(currentTarget,getNewContext(selectedItem)); + } + + @Override + public PdfIndirectReference getCurrentTarget() { + return currentTarget; + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + + } +} diff --git a/src/main/java/com/itextpdf/rups/controller/search/PdfStreamSearchFilter.java b/src/main/java/com/itextpdf/rups/controller/search/PdfStreamSearchFilter.java new file mode 100644 index 00000000..0d9d315e --- /dev/null +++ b/src/main/java/com/itextpdf/rups/controller/search/PdfStreamSearchFilter.java @@ -0,0 +1,35 @@ +package com.itextpdf.rups.controller.search; + +import com.itextpdf.search.model.ESearchOptions; +import com.itextpdf.search.model.ISearchFilter; + +import java.util.Arrays; +import java.util.Collection; + +public class PdfStreamSearchFilter implements ISearchFilter { + String query = ""; + ESearchOptions[] options = {}; + @Override + public boolean setQuery(String inputQuery) { + //TODO: Properly Validate input... + if(null != inputQuery) + this.query = inputQuery; + return (null !=query) && (query == inputQuery); + } + + @Override + public boolean setOptions(ESearchOptions[] searchOptions) { + options = searchOptions; + return (null != options) && (options == searchOptions); + } + + @Override + public String getQuery() { + return query; + } + + @Override + public Collection getOptions() { + return Arrays.asList(options); + } +} diff --git a/src/main/java/com/itextpdf/rups/controller/search/RupsSearchHandler.java b/src/main/java/com/itextpdf/rups/controller/search/RupsSearchHandler.java new file mode 100644 index 00000000..93e8ed51 --- /dev/null +++ b/src/main/java/com/itextpdf/rups/controller/search/RupsSearchHandler.java @@ -0,0 +1,57 @@ +package com.itextpdf.rups.controller.search; + +import com.itextpdf.kernel.pdf.PdfIndirectReference; +import com.itextpdf.kernel.pdf.PdfObject; +import com.itextpdf.rups.view.RupsPanel; +import com.itextpdf.search.model.ESearchOptions; +import com.itextpdf.search.model.ESearchScope; +import com.itextpdf.search.model.ISearchFilter; +import com.itextpdf.search.ISearchHandler; +import com.itextpdf.search.model.ISearchResult; +import com.itextpdf.search.model.SearchContext; +import com.itextpdf.search.SearchHandler; + +import javax.swing.event.TreeSelectionListener; +import java.util.concurrent.atomic.AtomicBoolean; + + +public abstract class RupsSearchHandler extends SearchHandler implements TreeSelectionListener { + private static ISearchHandler INSTANCE; + + public RupsSearchHandler() { + if (null == INSTANCE) { + INSTANCE = this; + } + + } + + @Override + public ISearchResult find(SearchContext context, ISearchFilter filter) { + AtomicBoolean case_sensitive = new AtomicBoolean(false); + AtomicBoolean regex = new AtomicBoolean(false); + AtomicBoolean word = new AtomicBoolean(false); + + filter.getOptions().forEach((ESearchOptions option) -> { + case_sensitive.set(case_sensitive.get() || option == ESearchOptions.CASE_SENSITIVE); + regex.set((!word.get()) && (regex.get() || option == ESearchOptions.REGEX)); + word.set((!regex.get()) && (word.get() || option == ESearchOptions.REGEX)); + }); + + if(regex.get()){ + // Regex Evaluation + } else if (word.get()) { + // Whole Word Matches only + } else { + // Default Search + } + // new ThreadedSearch + + return null; + } + + public static ISearchHandler getInstance(RupsPanel rupsPanel) { + return INSTANCE; + } + + public abstract SearchContext getNewContext(ESearchScope itemScope); +} diff --git a/src/main/java/com/itextpdf/rups/view/Language.java b/src/main/java/com/itextpdf/rups/view/Language.java index 0a54ac42..3e748339 100644 --- a/src/main/java/com/itextpdf/rups/view/Language.java +++ b/src/main/java/com/itextpdf/rups/view/Language.java @@ -111,6 +111,7 @@ public enum Language { ERROR_INITIALIZING_SETTINGS, ERROR_KEY_IS_NOT_NAME, ERROR_LOADING_DEFAULT_SETTINGS, + ERROR_LOADING_ICON_DEFINITIONS, ERROR_LOADING_IMAGE, ERROR_LOADING_XFA, ERROR_LOOK_AND_FEEL, diff --git a/src/main/java/com/itextpdf/rups/view/RupsMenuBar.java b/src/main/java/com/itextpdf/rups/view/RupsMenuBar.java index fe87b95f..94a190db 100644 --- a/src/main/java/com/itextpdf/rups/view/RupsMenuBar.java +++ b/src/main/java/com/itextpdf/rups/view/RupsMenuBar.java @@ -52,16 +52,13 @@ This file is part of the iText (R) project. import com.itextpdf.rups.io.OpenInViewerAction; import com.itextpdf.rups.io.filters.PdfFilter; +import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.util.HashMap; import java.util.Observable; import java.util.Observer; -import javax.swing.Box; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.KeyStroke; +import javax.swing.*; public class RupsMenuBar extends JMenuBar implements Observer { /** @@ -93,6 +90,8 @@ public class RupsMenuBar extends JMenuBar implements Observer { */ private final PreferencesWindow preferencesWindow; + private final RupsSearchBar searchBar; + /** * Creates a JMenuBar. */ @@ -109,6 +108,9 @@ public RupsMenuBar(RupsController controller) { new FileCompareAction(controller, PdfFilter.INSTANCE, controller.getMasterComponent()); final JMenu file = new JMenu(Language.MENU_BAR_FILE.getString()); + + searchBar = new RupsSearchBar(); + addItem(file, Language.MENU_BAR_OPEN.getString(), fileOpenAction, KeyStroke.getKeyStroke('O', InputEvent.CTRL_DOWN_MASK)); addItem(file, Language.MENU_BAR_CLOSE.getString(), new FileCloseAction(controller), @@ -129,6 +131,15 @@ public RupsMenuBar(RupsController controller) { preferencesWindow.show(controller.getMasterComponent()); } ); + addItem(edit, "Find", e -> { + Window masterComponent = SwingUtilities.getWindowAncestor((JComponent)controller.getMasterComponent()); + Component target = masterComponent.getFocusOwner(); + if(target instanceof JComponent) { + searchBar.setSearchHandler(controller.getSearchHandler()); + searchBar.setTarget((JComponent) target); + } + }, + KeyStroke.getKeyStroke('F', InputEvent.CTRL_DOWN_MASK)); add(edit); add(Box.createGlue()); diff --git a/src/main/java/com/itextpdf/rups/view/RupsPanel.java b/src/main/java/com/itextpdf/rups/view/RupsPanel.java index 91a3a3c0..a625b187 100644 --- a/src/main/java/com/itextpdf/rups/view/RupsPanel.java +++ b/src/main/java/com/itextpdf/rups/view/RupsPanel.java @@ -43,6 +43,7 @@ This file is part of the iText (R) project. package com.itextpdf.rups.view; import com.itextpdf.rups.controller.RupsInstanceController; +import com.itextpdf.rups.controller.search.RupsSearchHandler; import com.itextpdf.rups.model.PdfFile; import java.awt.BorderLayout; @@ -55,6 +56,7 @@ This file is part of the iText (R) project. public class RupsPanel extends JPanel { private RupsInstanceController rupsInstanceController; + private RupsSearchHandler rupsSearchHandler; public RupsPanel() { setLayout(new BorderLayout()); @@ -71,4 +73,12 @@ public PdfFile getPdfFile() { public RupsInstanceController getRupsInstanceController() { return this.rupsInstanceController; } + + public void setSearchContext(RupsSearchHandler instance) { + this.rupsSearchHandler = instance; + } + + public RupsSearchHandler getRupsSearchHandler() { + return rupsSearchHandler; + } } diff --git a/src/main/java/com/itextpdf/rups/view/RupsSearchBar.java b/src/main/java/com/itextpdf/rups/view/RupsSearchBar.java new file mode 100644 index 00000000..7bae7e86 --- /dev/null +++ b/src/main/java/com/itextpdf/rups/view/RupsSearchBar.java @@ -0,0 +1,278 @@ +package com.itextpdf.rups.view; + +import com.itextpdf.kernel.pdf.PdfIndirectReference; +import com.itextpdf.rups.RupsConfiguration; +import com.itextpdf.search.model.ESearchOptions; +import com.itextpdf.search.model.ESearchScope; +import com.itextpdf.search.model.ISearchFilter; +import com.itextpdf.search.ISearchHandler; +import com.itextpdf.search.model.SearchContext; +import com.itextpdf.rups.view.icons.IconDropdownRenderer; +import com.itextpdf.rups.view.icons.IconFetcher; + +import javax.swing.*; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.ArrayList; +import java.util.List; + +public class RupsSearchBar extends JDialog /*implements TreeSelectionListener */{ + + //TODO: + // Move tree listener to search bar. + // Track SearchInstance instances rather than search bar instances + // -> re-fill in the search bar details from instance when a node is re-selected + // Make search handler more atomic + + static private RupsSearchBar INSTANCE; + private JComboBox scopeDropdown = new JComboBox<>(); + private AncestorListener ancestorListener; + private FocusListener focusListener; + private KeyListener keyListener; + + JComponent targetComponent; + PdfIndirectReference targetObject; + JTextField searchBox; + JButton caseSensitivity; + JRadioButton wordSearch; + JRadioButton regexSearch; + ISearchHandler searchHandler; + + private int width; + private int height; + + public RupsSearchBar(){ +// super(SwingUtilities.getWindowAncestor(component)); + super(); + +// this.targetComponent = component; + //TODO: Move < Instance>> pairing to either an internal Map or to the controller + this.setUndecorated(true); + this.setupListeners(); + this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + this.setLayout(new BorderLayout()); + this.setAlwaysOnTop(true); + JPanel searchControls = new JPanel(); + searchControls.setLayout(new BorderLayout()); + + JPanel searchOptions = new JPanel(); + searchOptions.setLayout(new FlowLayout()); + + JScrollPane searchResults = new JScrollPane(); + JTextField search = new JTextField(""); + search.setName("SearchInput"); + this.searchBox = search; + + searchControls.add(searchBox, BorderLayout.WEST); + + JButton caseSwitch = new JButton(IconFetcher.getIcon(RupsConfiguration.INSTANCE.getIconFor("rups.search.options.case_sensitivity"))); + caseSwitch.setName("CaseSensitive"); + caseSwitch.setToolTipText(ESearchOptions.CASE_SENSITIVE.getTooltip()); + caseSensitivity = caseSwitch; + + searchOptions.add(caseSensitivity); + + JRadioButtonMenuItem wordOrRegex = new JRadioButtonMenuItem(); + + JRadioButton word = new JRadioButton(IconFetcher.getIcon(RupsConfiguration.INSTANCE.getIconFor("rups.search.options.word"))); + word.setName("Word"); + word.setToolTipText(ESearchOptions.WORD.getTooltip()); + wordSearch = word; + + JRadioButton regex = new JRadioButton(IconFetcher.getIcon(RupsConfiguration.INSTANCE.getIconFor("rups.search.options.regex"))); + regex.setName("Regex"); + regex.setToolTipText(ESearchOptions.REGEX.getTooltip()); + regexSearch = regex; + + searchOptions.add(wordSearch); + searchOptions.add(regexSearch); + +// scopeDropdown.setIcon(IconFetcher.getIcon("magnifier.png")); + + JLabel localScope = new JLabel(); + localScope.setIcon(IconFetcher.getIcon(RupsConfiguration.INSTANCE.getIconFor("rups.search.scope.selection"))); + localScope.setToolTipText(ESearchScope.SELECTION.getTooltip()); + localScope.setVisible(true); + scopeDropdown.addItem(ESearchScope.SELECTION); + + JLabel documentScope = new JLabel(); + documentScope.setIcon(IconFetcher.getIcon(RupsConfiguration.INSTANCE.getIconFor("rups.search.scope.document"))); + documentScope.setToolTipText(ESearchScope.DOCUMENT.getTooltip()); + documentScope.setVisible(true); + scopeDropdown.addItem(ESearchScope.DOCUMENT); + + JLabel allDocumentsScope = new JLabel(); + allDocumentsScope.setIcon(IconFetcher.getIcon(RupsConfiguration.INSTANCE.getIconFor("rups.search.scope.all_documents"))); + allDocumentsScope.setToolTipText(ESearchScope.ALL_DOCUMENTS.getTooltip()); + allDocumentsScope.setVisible(true); + scopeDropdown.addItem(ESearchScope.ALL_DOCUMENTS); + + scopeDropdown.setSelectedIndex(0); + + scopeDropdown.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + scopeDropdown.setToolTipText(((ESearchScope)scopeDropdown.getSelectedItem()).getTooltip()); + } + }); + + scopeDropdown.setToolTipText(((ESearchScope) scopeDropdown.getSelectedItem()).getTooltip()); + + scopeDropdown.setRenderer(new IconDropdownRenderer()); + + searchOptions.add(scopeDropdown); + + searchControls.add(searchOptions, BorderLayout.EAST); + this.add(searchControls,BorderLayout.NORTH); + this.add(searchResults,BorderLayout.CENTER); + + + this.searchBox.addKeyListener(keyListener); + + } + + private void setupListeners() { + // Key Listener + this.keyListener = new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + if (e.getKeyChar() != KeyEvent.VK_ENTER) + return; +// searchHandler.setSearchTarget(,(ESearchScope) INSTANCES.get(target).scopeDropdown.getSelectedItem()); + SearchContext currentContext = searchHandler.getContext(searchHandler.getCurrentTarget(), (ESearchScope) scopeDropdown.getSelectedItem()); + ISearchFilter query = currentContext.getNewFilter(); + query.setQuery(searchBox.getText()); + + List searchOptions = new ArrayList(); + if (caseSensitivity.isSelected()) + searchOptions.add(ESearchOptions.CASE_SENSITIVE); + if (wordSearch.getModel().isSelected()) + searchOptions.add(ESearchOptions.WORD); + if (regexSearch.getModel().isSelected()) + searchOptions.add(ESearchOptions.REGEX); + ESearchOptions[] resultingOptions = new ESearchOptions[searchOptions.size()]; + searchOptions.toArray(resultingOptions); + query.setOptions(resultingOptions); +// (ESearchScope) scopeDropdown.getSelectedItem()) + + //TODO - Fix this being Null. + targetObject = searchHandler.getCurrentTarget().getIndirectReference(); + + searchHandler.find(currentContext, query); + System.out.println("Rups SearchInstance Handler"); + System.out.println(query.toString()); + } + + @Override + public void keyPressed(KeyEvent e) {} + + @Override + public void keyReleased(KeyEvent e) {} + }; + + // Focus Listener + this.focusListener = new FocusListener() { + + @Override + public void focusGained(FocusEvent e) { + System.out.println("Target Focus Gained."); + } + + @Override + public void focusLost(FocusEvent e) { + System.out.println("Target Focus Lost."); +// INSTANCES.get(target).hideSearchBar(true); + } + }; + + // Ancestor Listener + this.ancestorListener = new AncestorListener() { + @Override + public void ancestorAdded(AncestorEvent event) {} + + @Override + public void ancestorRemoved(AncestorEvent event) { + setTarget(null); // clean up old listeners but don't assign new ones + } + + @Override + public void ancestorMoved(AncestorEvent event) { + setWindowPosition(); + } + }; + } + + public void setTarget(JComponent newTarget) { + if (newTarget != this.targetComponent) { + if (this.targetComponent != null) { + this.targetComponent.removeAncestorListener(this.ancestorListener); + this.targetComponent.removeFocusListener(this.focusListener); + } + //TODO: remove listeners from old target + // Update the listeners to the new target + // Add the listeners to the new target + this.targetComponent = newTarget; +// newTarget.getTopLevelAncestor(); + } + + if (this.targetComponent == null) + return; + + this.targetComponent.addAncestorListener(ancestorListener); + + this.targetComponent.addFocusListener(this.focusListener); + +// setWindowPosition(); + + this.width = /*500;//*/this.targetComponent.getSize().width; + this.height = 24;/*500;//*///searchBox.getHeight(); + int fontWidth = searchBox.getFont().getSize(); +// int fontWidth = 18; + int buttonWidth = caseSensitivity.getWidth(); + int colNum = (width - (3 * buttonWidth))/ fontWidth; + System.out.print("Column #: "); + System.out.println(colNum); + searchBox.setColumns(colNum); + searchBox.setLocation(1,0); + setWindowPosition(); + INSTANCE.hideSearchBar(false); + } + + private void setWindowPosition() { + Point targetLocation = targetComponent.getLocation(); + SwingUtilities.convertPointToScreen(targetLocation, targetComponent); + setLocation(targetLocation); + setBounds(new Rectangle(targetLocation.x,targetLocation.y, width, height)); + } + + private void hideSearchBar(boolean hide){ + setVisible(!hide); + setEnabled(!hide); + } + + public static RupsSearchBar getSearchBar(JComponent component){ + if(INSTANCE == null) + INSTANCE = new RupsSearchBar(); + INSTANCE.setTarget(component); + return INSTANCE; + //TODO: Bring to Foreground. + //TODO: Hide on losing focus. + } + +// @Override +// public void valueChanged(TreeSelectionEvent e) { +// this.targetObject = ((PdfObjectTreeNode) e.getNewLeadSelectionPath().getLastPathComponent()).getPdfObject().getIndirectReference(); +// } + //TODO let SearchHandler determine focused object. + + public void setSearchHandler(ISearchHandler searchHandler) { + this.searchHandler = searchHandler; + } +} diff --git a/src/main/java/com/itextpdf/rups/view/RupsTabbedPane.java b/src/main/java/com/itextpdf/rups/view/RupsTabbedPane.java index b0525ecd..1ab6acaa 100644 --- a/src/main/java/com/itextpdf/rups/view/RupsTabbedPane.java +++ b/src/main/java/com/itextpdf/rups/view/RupsTabbedPane.java @@ -42,7 +42,10 @@ This file is part of the iText (R) project. */ package com.itextpdf.rups.view; +import com.itextpdf.kernel.pdf.PdfObject; import com.itextpdf.rups.controller.RupsInstanceController; +import com.itextpdf.search.ISearchHandler; +import com.itextpdf.rups.controller.search.RupsSearchHandler; import com.itextpdf.rups.model.PdfFile; import java.awt.Component; @@ -76,9 +79,12 @@ public void openNewFile(File file, Dimension dimension, boolean readonly) { this.jTabbedPane.removeTabAt(this.jTabbedPane.getSelectedIndex()); } + // TODO: Handle the propagation of SearchControllers out from this class... + RupsPanel rupsPanel = new RupsPanel(); RupsInstanceController rupsInstanceController = new RupsInstanceController(dimension, rupsPanel); rupsPanel.setRupsInstanceController(rupsInstanceController); + rupsPanel.setSearchContext((RupsSearchHandler) RupsSearchHandler.getInstance(rupsPanel)); rupsInstanceController.loadFile(file, readonly); this.jTabbedPane.addTab(file.getName(), null, rupsPanel); this.jTabbedPane.setSelectedComponent(rupsPanel); @@ -88,6 +94,7 @@ public void openNewFile(File file, Dimension dimension, boolean readonly) { public boolean closeCurrentFile() { boolean isLastTab = this.jTabbedPane.getTabCount() == 1; + //TODO: Remove any associated SearchBar Instances this.jTabbedPane.removeTabAt(this.jTabbedPane.getSelectedIndex()); if (this.jTabbedPane.getTabCount() == 0) { @@ -107,6 +114,10 @@ public void saveCurrentFile(File file) { RupsPanel currentRupsPanel = (RupsPanel) this.jTabbedPane.getSelectedComponent(); currentRupsPanel.getRupsInstanceController().saveFile(file); } + public RupsInstanceController getCurrentController() { + RupsPanel currentRupsPanel = (RupsPanel) this.jTabbedPane.getSelectedComponent(); + return currentRupsPanel.getRupsInstanceController(); + } public Component getJTabbedPane() { return this.jTabbedPane; diff --git a/src/main/java/com/itextpdf/rups/view/icons/IconDropdownRenderer.java b/src/main/java/com/itextpdf/rups/view/icons/IconDropdownRenderer.java new file mode 100644 index 00000000..665ef1f7 --- /dev/null +++ b/src/main/java/com/itextpdf/rups/view/icons/IconDropdownRenderer.java @@ -0,0 +1,22 @@ +package com.itextpdf.rups.view.icons; + +import com.itextpdf.rups.RupsConfiguration; +import com.itextpdf.search.model.PropertyEnum; + +import javax.swing.*; +import java.awt.*; + +public class IconDropdownRenderer extends JLabel implements ListCellRenderer { + @Override + public Component getListCellRendererComponent(JList list, PropertyEnum object, int index, boolean isSelected, boolean cellHasFocus) { + String type = object.getType(); + String value = object.toString().toLowerCase(); + String iconString = RupsConfiguration.INSTANCE.getIconFor(String.format("rups.%s.%1s",type,value)); + Icon icon = IconFetcher.getIcon(iconString); + this.setIcon(icon); + this.setToolTipText(object.getTooltip());// TODO: Implement Locales. + this.setSize(icon.getIconWidth(),icon.getIconHeight()); + this.setVisible(true); + return this; + } +} diff --git a/src/main/java/com/itextpdf/rups/view/itext/PdfObjectPanel.java b/src/main/java/com/itextpdf/rups/view/itext/PdfObjectPanel.java index 21f4e467..880f6194 100644 --- a/src/main/java/com/itextpdf/rups/view/itext/PdfObjectPanel.java +++ b/src/main/java/com/itextpdf/rups/view/itext/PdfObjectPanel.java @@ -55,6 +55,7 @@ This file is part of the iText (R) project. import com.itextpdf.rups.event.RupsEvent; import com.itextpdf.rups.model.PdfSyntaxParser; import com.itextpdf.rups.view.Language; +import com.itextpdf.rups.view.RupsSearchBar; import com.itextpdf.rups.view.icons.IconFetcher; import com.itextpdf.rups.view.itext.treenodes.PdfObjectTreeNode; import com.itextpdf.rups.view.models.AbstractPdfObjectPanelTableModel; @@ -114,6 +115,7 @@ public class PdfObjectPanel extends Observable implements Observer { */ public PdfObjectPanel() { // layout + panel.setLayout(layout); // dictionary / array / stream diff --git a/src/main/java/com/itextpdf/rups/view/itext/contentstream/ContentStreamSearchUtils.java b/src/main/java/com/itextpdf/rups/view/itext/contentstream/ContentStreamSearchUtils.java new file mode 100644 index 00000000..ab4153eb --- /dev/null +++ b/src/main/java/com/itextpdf/rups/view/itext/contentstream/ContentStreamSearchUtils.java @@ -0,0 +1,55 @@ +/* + This file is part of the iText (R) project. + Copyright (c) 1998-2022 iText Group NV + Authors: iText Software. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License version 3 + as published by the Free Software Foundation with the addition of the + following permission added to Section 15 as permitted in Section 7(a): + FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY + ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT + OF THIRD PARTY RIGHTS + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Affero General Public License for more details. + You should have received a copy of the GNU Affero General Public License + along with this program; if not, see http://www.gnu.org/licenses or write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA, 02110-1301 USA, or download the license from the following URL: + http://itextpdf.com/terms-of-use/ + + The interactive user interfaces in modified source and object code versions + of this program must display Appropriate Legal Notices, as required under + Section 5 of the GNU Affero General Public License. + + In accordance with Section 7(b) of the GNU Affero General Public License, + a covered work must retain the producer line in every PDF that is created + or manipulated using iText. + + You can be released from the requirements of the license by purchasing + a commercial license. Buying such a license is mandatory as soon as you + develop commercial activities involving the iText software without + disclosing the source code of your own applications. + These activities include: offering paid services to customers as an ASP, + serving PDFs on the fly in a web application, shipping iText with a closed + source product. + + For more information, please contact iText Software Corp. at this + address: sales@itextpdf.com + */ +package com.itextpdf.rups.view.itext.contentstream; + +import com.itextpdf.search.model.ISearchFilter; +import com.itextpdf.search.model.ISearchResult; + +import java.io.ByteArrayInputStream; + +final class ContentStreamSearchUtils { + static ISearchResult find(ByteArrayInputStream stream, ISearchFilter filter){ + + return null; + } +} diff --git a/src/main/resources/bundles/rups-lang.properties b/src/main/resources/bundles/rups-lang.properties index 9fd4a5be..3e1d28a3 100644 --- a/src/main/resources/bundles/rups-lang.properties +++ b/src/main/resources/bundles/rups-lang.properties @@ -61,6 +61,7 @@ ERROR_INDEX_NOT_INTEGER=The typed index isn't integer. ERROR_INITIALIZING_SETTINGS=Error initializing settings. ERROR_KEY_IS_NOT_NAME=Key value isn't value Name object. ERROR_LOADING_DEFAULT_SETTINGS=Error loading default settings. +ERROR_LOADING_ICON_DEFINITIONS=Error loading icon definitions. ERROR_LOADING_IMAGE=Image can't be loaded. ERROR_LOADING_XFA=Can't load XFA. ERROR_LOOK_AND_FEEL=Error setting the look and feel. @@ -170,6 +171,10 @@ SAVE_TO_FILE=Save to File SAVE_TO_STREAM=Save to Stream SAVE_UNSAVED_CHANGES=You have unchanged changes! Are you sure you want to discard them? +SEARCH_SCOPE_SELECTION=Current Object +SEARCH_SCOPE_DOCUMENT=Whole Document +SEARCH_SCOPE_ALL_DOCUMENTS=All Opened Documents + SELECT_ALL=Select All STREAM=Stream diff --git a/src/main/resources/bundles/rups-lang_en_US.properties b/src/main/resources/bundles/rups-lang_en_US.properties index 3212b49f..e8c1a799 100644 --- a/src/main/resources/bundles/rups-lang_en_US.properties +++ b/src/main/resources/bundles/rups-lang_en_US.properties @@ -148,6 +148,10 @@ SAVE_SUCCESS=File Saved SAVE_TO_FILE=Save to File SAVE_TO_STREAM=Save to Stream +SEARCH_SCOPE_SELECTION=Current Object +SEARCH_SCOPE_DOCUMENT=Whole Document +SEARCH_SCOPE_ALL_DOCUMENTS=All Opened Documents + SELECT_ALL=Select All STREAM=Stream diff --git a/src/main/resources/bundles/rups-lang_nl_NL.properties b/src/main/resources/bundles/rups-lang_nl_NL.properties index b1b298e3..18438633 100644 --- a/src/main/resources/bundles/rups-lang_nl_NL.properties +++ b/src/main/resources/bundles/rups-lang_nl_NL.properties @@ -58,6 +58,7 @@ ERROR_INDEX_NOT_IN_RANGE=De index is niet in range. ERROR_INDEX_NOT_INTEGER=De index is niet een getal. ERROR_KEY_IS_NOT_NAME=Key value is niet een Name object. ERROR_LOADING_DEFAULT_SETTINGS=Fout tijdens het inladen van de standaard instellingen. +ERROR_LOADING_ICON_DEFINITIONS=Fout tijdens het inladen van de icon instellingen. ERROR_LOADING_IMAGE=De afbeelding kan niet geladen worden. ERROR_LOADING_XFA=Het XFA form kan niet geladen worden. ERROR_LOOK_AND_FEEL=Fout opgetreden tijdens het aanpassen van de look and feel. @@ -166,6 +167,10 @@ SAVE_TO_FILE=Opslaan Als SAVE_TO_STREAM=Opslaan in de Stream SAVE_UNSAVED_CHANGES=Er zijn veranderingen die niet opgeslaan zijn! Wilt u deze negeren? +SEARCH_SCOPE_SELECTION=Deze Object +SEARCH_SCOPE_DOCUMENT=Hele Document +SEARCH_SCOPE_ALL_DOCUMENTS=Alle Documenten + SELECT_ALL=Selecteer Alles STREAM=Stream diff --git a/src/main/resources/com/itextpdf/rups/view/icons/case_sensitive.png b/src/main/resources/com/itextpdf/rups/view/icons/case_sensitive.png new file mode 100644 index 00000000..8dcc2dbb Binary files /dev/null and b/src/main/resources/com/itextpdf/rups/view/icons/case_sensitive.png differ diff --git a/src/main/resources/com/itextpdf/rups/view/icons/no_icon.png b/src/main/resources/com/itextpdf/rups/view/icons/no_icon.png new file mode 100644 index 00000000..c260e1d9 Binary files /dev/null and b/src/main/resources/com/itextpdf/rups/view/icons/no_icon.png differ diff --git a/src/main/resources/com/itextpdf/rups/view/icons/regex.png b/src/main/resources/com/itextpdf/rups/view/icons/regex.png new file mode 100644 index 00000000..bab7cc9b Binary files /dev/null and b/src/main/resources/com/itextpdf/rups/view/icons/regex.png differ diff --git a/src/main/resources/com/itextpdf/rups/view/icons/search.png b/src/main/resources/com/itextpdf/rups/view/icons/search.png new file mode 100644 index 00000000..cf3d97f7 Binary files /dev/null and b/src/main/resources/com/itextpdf/rups/view/icons/search.png differ diff --git a/src/main/resources/com/itextpdf/rups/view/icons/whole_word.png b/src/main/resources/com/itextpdf/rups/view/icons/whole_word.png new file mode 100644 index 00000000..280fd442 Binary files /dev/null and b/src/main/resources/com/itextpdf/rups/view/icons/whole_word.png differ diff --git a/src/main/resources/config/icon.properties b/src/main/resources/config/icon.properties new file mode 100644 index 00000000..414c5210 --- /dev/null +++ b/src/main/resources/config/icon.properties @@ -0,0 +1,7 @@ +default=no_icon.png +rups.search.options.case_sensitivity=case_sensitive.png +rups.search.options.word=whole_word.png +rups.search.options.regex=regex.png +rups.search.scope.selection=xfa.png +rups.search.scope.document=page.png +rups.search.scope.all_documents=pages.png \ No newline at end of file diff --git a/src/test/java/com/itextpdf/rups/mock/MockedRupsController.java b/src/test/java/com/itextpdf/rups/mock/MockedRupsController.java index 57349edb..3cdd3c77 100644 --- a/src/test/java/com/itextpdf/rups/mock/MockedRupsController.java +++ b/src/test/java/com/itextpdf/rups/mock/MockedRupsController.java @@ -43,6 +43,7 @@ This file is part of the iText (R) project. package com.itextpdf.rups.mock; import com.itextpdf.rups.controller.IRupsController; +import com.itextpdf.search.ISearchHandler; import com.itextpdf.rups.model.PdfFile; import java.awt.Component; @@ -81,6 +82,7 @@ public void closeCurrentFile() { this.openCount--; } + public int getOpenedCount() { return this.openCount; }