/* * Autopsy Forensic Browser * * Copyright 2011-16 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sleuthkit.autopsy.keywordsearch; import java.awt.ComponentOrientation; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JMenuItem; import javax.swing.JTextPane; import javax.swing.SizeRequirements; import javax.swing.SwingWorker; import javax.swing.text.Element; import javax.swing.text.View; import javax.swing.text.ViewFactory; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.HTMLEditorKit.HTMLFactory; import javax.swing.text.html.InlineView; import javax.swing.text.html.ParagraphView; import javax.swing.text.html.StyleSheet; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.TextUtil; /** * Panel displays HTML content sent to ExtractedContentViewer, and provides a * combo-box to select between multiple sources. */ class ExtractedContentPanel extends javax.swing.JPanel { private static Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName()); ExtractedContentPanel() { initComponents(); initControls(); customizeComponents(); } private void customizeComponents() { HTMLEditorKit editorKit = new HTMLEditorKit() { @Override public ViewFactory getViewFactory() { return new HTMLFactory() { @Override public View create(Element e) { View v = super.create(e); if (v instanceof InlineView) { return new InlineView(e) { @Override public int getBreakWeight(int axis, float pos, float len) { return GoodBreakWeight; } @Override public View breakView(int axis, int p0, float pos, float len) { if (axis == View.X_AXIS) { checkPainter(); int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len); if (p0 == getStartOffset() && p1 == getEndOffset()) { return this; } return createFragment(p0, p1); } return this; } }; } else if (v instanceof ParagraphView) { return new ParagraphView(e) { @Override protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) { if (r == null) { r = new SizeRequirements(); } float pref = layoutPool.getPreferredSpan(axis); float min = layoutPool.getMinimumSpan(axis); // Don't include insets, Box.getXXXSpan will include them. r.minimum = (int) min; r.preferred = Math.max(r.minimum, (int) pref); r.maximum = Integer.MAX_VALUE; r.alignment = 0.5f; return r; } }; } return v; } }; } }; // set font size manually in an effort to get fonts in this panel to look // similar to what is in the 'String View' content viewer. StyleSheet ss = editorKit.getStyleSheet(); ss.addRule("body {font-size: 8.5px;}"); //NON-NLS extractedTextPane.setEditorKit(editorKit); sourceComboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { IndexedText source = (IndexedText) e.getItem(); setMarkup(source); } } }); setSources(new ArrayList<IndexedText>()); extractedTextPane.setComponentPopupMenu(rightClickMenu); ActionListener actList = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JMenuItem jmi = (JMenuItem) e.getSource(); if (jmi.equals(copyMenuItem)) { extractedTextPane.copy(); } else if (jmi.equals(selectAllMenuItem)) { extractedTextPane.selectAll(); } } }; copyMenuItem.addActionListener(actList); selectAllMenuItem.addActionListener(actList); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { rightClickMenu = new javax.swing.JPopupMenu(); copyMenuItem = new javax.swing.JMenuItem(); selectAllMenuItem = new javax.swing.JMenuItem(); jScrollPane1 = new javax.swing.JScrollPane(); extractedTextPane = new JTextPane(){ public boolean getScrollableTracksViewportWidth() { return (getSize().width < 400); }}; sourceComboBox = new javax.swing.JComboBox<>(); hitLabel = new javax.swing.JLabel(); hitCountLabel = new javax.swing.JLabel(); hitOfLabel = new javax.swing.JLabel(); hitTotalLabel = new javax.swing.JLabel(); hitButtonsLabel = new javax.swing.JLabel(); hitPreviousButton = new javax.swing.JButton(); hitNextButton = new javax.swing.JButton(); pageButtonsLabel = new javax.swing.JLabel(); pagePreviousButton = new javax.swing.JButton(); pageNextButton = new javax.swing.JButton(); pagesLabel = new javax.swing.JLabel(); pageCurLabel = new javax.swing.JLabel(); pageOfLabel = new javax.swing.JLabel(); pageTotalLabel = new javax.swing.JLabel(); copyMenuItem.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.copyMenuItem.text")); // NOI18N rightClickMenu.add(copyMenuItem); selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.selectAllMenuItem.text")); // NOI18N rightClickMenu.add(selectAllMenuItem); setPreferredSize(new java.awt.Dimension(700, 400)); jScrollPane1.setBackground(new java.awt.Color(255, 255, 255)); jScrollPane1.setPreferredSize(new java.awt.Dimension(700, 400)); extractedTextPane.setEditable(false); extractedTextPane.setAutoscrolls(false); extractedTextPane.setMaximumSize(new java.awt.Dimension(2000, 2000)); extractedTextPane.setPreferredSize(new java.awt.Dimension(700, 400)); jScrollPane1.setViewportView(extractedTextPane); sourceComboBox.setModel(new javax.swing.DefaultComboBoxModel<IndexedText>()); hitLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitLabel.text")); // NOI18N hitLabel.setToolTipText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitLabel.toolTipText")); // NOI18N hitCountLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); hitCountLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitCountLabel.text")); // NOI18N hitCountLabel.setMaximumSize(new java.awt.Dimension(18, 14)); hitCountLabel.setMinimumSize(new java.awt.Dimension(18, 14)); hitCountLabel.setPreferredSize(new java.awt.Dimension(18, 14)); hitOfLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitOfLabel.text")); // NOI18N hitTotalLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); hitTotalLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitTotalLabel.text")); // NOI18N hitTotalLabel.setMaximumSize(new java.awt.Dimension(18, 14)); hitTotalLabel.setMinimumSize(new java.awt.Dimension(18, 14)); hitTotalLabel.setPreferredSize(new java.awt.Dimension(18, 14)); hitButtonsLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitButtonsLabel.text")); // NOI18N hitPreviousButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back.png"))); // NOI18N NON-NLS hitPreviousButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitPreviousButton.text")); // NOI18N hitPreviousButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); hitPreviousButton.setBorderPainted(false); hitPreviousButton.setContentAreaFilled(false); hitPreviousButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back_disabled.png"))); // NOI18N NON-NLS hitPreviousButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); hitPreviousButton.setPreferredSize(new java.awt.Dimension(23, 23)); hitPreviousButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back_hover.png"))); // NOI18N NON-NLS hitNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward.png"))); // NOI18N NON-NLS hitNextButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitNextButton.text")); // NOI18N hitNextButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); hitNextButton.setBorderPainted(false); hitNextButton.setContentAreaFilled(false); hitNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_disabled.png"))); // NOI18N NON-NLS hitNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); hitNextButton.setPreferredSize(new java.awt.Dimension(23, 23)); hitNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_hover.png"))); // NOI18N NON-NLS pageButtonsLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageButtonsLabel.text")); // NOI18N pagePreviousButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back.png"))); // NOI18N NON-NLS pagePreviousButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pagePreviousButton.text")); // NOI18N pagePreviousButton.setActionCommand(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pagePreviousButton.actionCommand")); // NOI18N pagePreviousButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); pagePreviousButton.setBorderPainted(false); pagePreviousButton.setContentAreaFilled(false); pagePreviousButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back_disabled.png"))); // NOI18N NON-NLS pagePreviousButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward.png"))); // NOI18N NON-NLS pageNextButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageNextButton.text")); // NOI18N pageNextButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); pageNextButton.setBorderPainted(false); pageNextButton.setContentAreaFilled(false); pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_disabled.png"))); // NOI18N NON-NLS pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); pageNextButton.setPreferredSize(new java.awt.Dimension(23, 23)); pagesLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pagesLabel.text")); // NOI18N pageCurLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); pageCurLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageCurLabel.text")); // NOI18N pageOfLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageOfLabel.text")); // NOI18N pageTotalLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); pageTotalLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageTotalLabel.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(hitLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(hitCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(hitOfLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(hitTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(hitButtonsLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(hitPreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(hitNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pagesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pageCurLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pageOfLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pageTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(pageButtonsLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pagePreviousButton) .addGap(0, 0, 0) .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(hitCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(hitOfLabel) .addComponent(hitTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(hitLabel) .addComponent(hitButtonsLabel)) .addComponent(hitPreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(hitNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(pageButtonsLabel) .addComponent(pageTotalLabel) .addComponent(pagesLabel) .addComponent(pageCurLabel) .addComponent(pageOfLabel)) .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(pagePreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(0, 0, 0) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 377, Short.MAX_VALUE)) ); }// </editor-fold>//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JMenuItem copyMenuItem; private javax.swing.JTextPane extractedTextPane; private javax.swing.JLabel hitButtonsLabel; private javax.swing.JLabel hitCountLabel; private javax.swing.JLabel hitLabel; private javax.swing.JButton hitNextButton; private javax.swing.JLabel hitOfLabel; private javax.swing.JButton hitPreviousButton; private javax.swing.JLabel hitTotalLabel; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel pageButtonsLabel; private javax.swing.JLabel pageCurLabel; private javax.swing.JButton pageNextButton; private javax.swing.JLabel pageOfLabel; private javax.swing.JButton pagePreviousButton; private javax.swing.JLabel pageTotalLabel; private javax.swing.JLabel pagesLabel; private javax.swing.JPopupMenu rightClickMenu; private javax.swing.JMenuItem selectAllMenuItem; private javax.swing.JComboBox<IndexedText> sourceComboBox; // End of variables declaration//GEN-END:variables void refreshCurrentMarkup() { IndexedText ms = (IndexedText) sourceComboBox.getSelectedItem(); setMarkup(ms); } /** * Set the available sources (selects the first source in the list by * default) * * @param sources */ void setSources(List<IndexedText> sources) { sourceComboBox.removeAllItems(); setPanelText(null, false); for (IndexedText ms : sources) { sourceComboBox.addItem(ms); } if (!sources.isEmpty()) { sourceComboBox.setSelectedIndex(0); } } /** * Return the types of mark up sources that this viewer knows about. * Different sources will markup the text in different ways. * * @return currently available sources on the panel */ public List<IndexedText> getSources() { ArrayList<IndexedText> sources = new ArrayList<>(); for (int i = 0; i < sourceComboBox.getItemCount(); ++i) { sources.add(sourceComboBox.getItemAt(i)); } return sources; } /** * Get the source selected in the combo box * * @return currently selected Source */ public IndexedText getSelectedSource() { return (IndexedText) sourceComboBox.getSelectedItem(); } private void setPanelText(String text, boolean detectDirection) { if (text == null) { text = ""; } if (detectDirection) { //detect text direction using first 1024 chars and set it //get first up to 1024 chars, strip <pre> tag and unescape html to get the string on which to detect final int len = text.length(); final int prefixLen = "<pre>".length(); //NON-NLS if (len > prefixLen) { final int maxOrientChars = Math.min(len, 1024); final String orientDetectText = EscapeUtil.unEscapeHtml(text.substring(prefixLen, maxOrientChars)); ComponentOrientation direction = TextUtil.getTextDirection(orientDetectText); //logger.log(Level.INFO, "ORIENTATION LEFT TO RIGHT: " + direction.isLeftToRight()); extractedTextPane.applyComponentOrientation(direction); } else { extractedTextPane.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); } } else { extractedTextPane.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); } extractedTextPane.setText(text); extractedTextPane.setCaretPosition(0); } private void initControls() { hitPreviousButton.setEnabled(false); hitNextButton.setEnabled(false); } void scrollToAnchor(String anchor) { extractedTextPane.scrollToReference(anchor); } /** * * @param current, current hit to update the display with */ void updateCurrentMatchDisplay(int current) { if (current == 0) { hitCountLabel.setText("-"); } else { hitCountLabel.setText(Integer.toString(current)); } } /** * * @param total total number of hits to update the display with */ void updateTotaMatcheslDisplay(int total) { if (total == 0) { hitTotalLabel.setText("-"); } else { hitTotalLabel.setText(Integer.toString(total)); } } /** * * @param current, current page to update the display with */ void updateCurrentPageDisplay(int current) { pageCurLabel.setText(Integer.toString(current)); } /** * * @param total total number of pages to update the display with */ void updateTotalPageslDisplay(int total) { pageTotalLabel.setText(Integer.toString(total)); } void resetDisplay() { resetHitDisplay(); resetPagesDisplay(); } /** * reset the current/total hits display */ void resetHitDisplay() { hitTotalLabel.setText("-"); hitCountLabel.setText("-"); } /** * reset the current/total pages display */ void resetPagesDisplay() { pageCurLabel.setText("-"); pageTotalLabel.setText("-"); } /** * enable previous match control * * @param enable whether to enable or disable */ void enablePrevMatchControl(boolean enable) { hitPreviousButton.setEnabled(enable); } /** * enable next match control * * @param enable whether to enable or disable */ void enableNextMatchControl(boolean enable) { hitNextButton.setEnabled(enable); } void addPrevMatchControlListener(ActionListener l) { hitPreviousButton.addActionListener(l); } void addNextMatchControlListener(ActionListener l) { hitNextButton.addActionListener(l); } /** * enable previous oage control * * @param enable whether to enable or disable */ void enablePrevPageControl(boolean enable) { pagePreviousButton.setEnabled(enable); } /** * enable next page control * * @param enable whether to enable or disable */ void enableNextPageControl(boolean enable) { pageNextButton.setEnabled(enable); } void addPrevPageControlListener(ActionListener l) { pagePreviousButton.addActionListener(l); } void addNextPageControlListener(ActionListener l) { pageNextButton.addActionListener(l); } void addSourceComboControlListener(ActionListener l) { sourceComboBox.addActionListener(l); } /** * Update page and search controls for selected source * * @param source the selected source */ void updateControls(IndexedText source) { updatePageControls(source); updateSearchControls(source); } /** * update page controls given the selected source * * @param source selected source */ void updatePageControls(IndexedText source) { if (source == null) { enableNextPageControl(false); enablePrevPageControl(false); updateCurrentPageDisplay(0); updateTotalPageslDisplay(0); return; } updateCurrentPageDisplay(source.getCurrentPage()); int totalPages = source.getNumberPages(); updateTotalPageslDisplay(totalPages); if (totalPages == 1) { enableNextPageControl(false); enablePrevPageControl(false); } else { if (source.hasNextPage()) { enableNextPageControl(true); } else { enableNextPageControl(false); } if (source.hasPreviousPage()) { enablePrevPageControl(true); } else { enablePrevPageControl(false); } } } /** * update search controls given the selected source * * @param source selected source */ void updateSearchControls(IndexedText source) { //setup search controls if (source != null && source.isSearchable()) { updateCurrentMatchDisplay(source.currentItem()); updateTotaMatcheslDisplay(source.getNumberHits()); if (source.hasNextItem() || source.hasNextPage()) { enableNextMatchControl(true); } else { enableNextMatchControl(false); } if (source.hasPreviousItem() || source.hasPreviousPage()) { enablePrevMatchControl(true); } else { enablePrevMatchControl(false); } } else { enableNextMatchControl(false); enablePrevMatchControl(false); updateCurrentMatchDisplay(0); updateTotaMatcheslDisplay(0); } } /** * Scroll to current (first) hit after SetMarkupWorker worker completed * * @param source */ private void scrollToCurrentHit(final IndexedText source) { if (source == null || !source.isSearchable()) { return; } //scrolling required invokeLater to enqueue in EDT EventQueue.invokeLater(() -> scrollToAnchor(source.getAnchorPrefix() + source.currentItem())); } /** * Gets and sets new markup (i.e. based on user choose keyword hits or pure * text). Updates GUI in GUI thread and gets markup in background thread. To * be invoked from GUI thread only. */ private void setMarkup(IndexedText source) { setPanelText(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.setMarkup.panelTxt"), false); new SetMarkupWorker(source).execute(); } /** * Swingworker to get markup source content String from Solr in background * thread and then set the panel text in the EDT Helps not to block the UI * while content from Solr is retrieved. */ private final class SetMarkupWorker extends SwingWorker<String, Void> { private final IndexedText source; private ProgressHandle progress; SetMarkupWorker(IndexedText source) { this.source = source; } @Override protected String doInBackground() throws Exception { progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.SetMarkup.progress.loading")); progress.setDisplayName(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.SetMarkup.progress.displayName")); progress.start(); progress.switchToIndeterminate(); return source.getText(); } @NbBundle.Messages({ "ExtractedContentPanel.SetMarkup.error=There was an error getting the text for the selected source."}) @Override protected void done() { super.done(); progress.finish(); // see if there are any errors try { String markup = get(); if (markup != null) { setPanelText(markup, true); } else { setPanelText("", false); } } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error getting marked up text", ex); //NON-NLS setPanelText(Bundle.ExtractedContentPanel_SetMarkup_error(), true); } // catch and ignore if we were cancelled catch (java.util.concurrent.CancellationException ex) { } updateControls(source); scrollToCurrentHit(source); } } }