/* * #%L * gitools-ui-app * %% * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ package org.gitools.ui.app.heatmap.panel.search; import com.alee.laf.label.WebLabel; import com.alee.managers.notification.NotificationIcon; import com.alee.managers.notification.WebNotificationPopup; import org.gitools.api.matrix.IAnnotations; import org.gitools.api.matrix.view.IMatrixView; import org.gitools.heatmap.Heatmap; import org.gitools.ui.app.heatmap.panel.HeatmapPanel; import org.gitools.ui.core.Application; import org.gitools.ui.core.utils.DocumentChangeListener; import org.gitools.ui.platform.icons.IconNames; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.text.DefaultHighlighter; import javax.swing.text.Highlighter; import java.awt.*; import java.awt.event.*; import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; public class HeatmapSearchPanel extends javax.swing.JPanel { private static Highlighter.HighlightPainter redHighlightPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.RED); private final Heatmap hm; private final HeatmapPanel heatmapPanel; private Pattern searchPat; private final Color defaultSearchTextBgColor; private final Color defaultSearchTextFgColor; private String foundAnnotation; private String foundValue; private WebNotificationPopup notificationPopup; public HeatmapSearchPanel(Heatmap hm, HeatmapPanel heatmapPanel) { this.hm = hm; this.heatmapPanel = heatmapPanel; this.setBackground(new Color(255, 253, 247)); initComponents(); defaultSearchTextBgColor = searchText.getBackground(); defaultSearchTextFgColor = searchText.getForeground(); addComponentListener(new ComponentListener() { @Override public void componentResized(ComponentEvent e) { } @Override public void componentMoved(ComponentEvent e) { } @Override public void componentHidden(ComponentEvent e) { } @Override public void componentShown(ComponentEvent e) { searchText.requestFocus(); } }); searchText.getDocument().addDocumentListener(new DocumentChangeListener() { @Override protected void update(DocumentEvent e) { updateSearch(); } }); searchText.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { close(); } } }); searchText.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { searchNext(); } }); prevBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { searchPrev(); } }); nextBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { searchNext(); } }); highlightAllChk.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { updateHighlight(); } }); matchCaseChk.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateSearch(); } }); anyWordChk.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateSearch(); } }); rowsButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateSearch(); } }); columnsButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateSearch(); } }); setNotFound(false); } public void close() { if (searchRows()) { HeatmapSearchPanel.this.hm.getRows().clearHighlightedLabels(); } else { HeatmapSearchPanel.this.hm.getColumns().clearHighlightedLabels(); } setVisible(false); } private void setNotFound(boolean b) { if (b) { //searchText.setBackground(Color.RED); searchText.setForeground(Color.RED); textNotFoundLabel.setVisible(true); } else { //searchText.setBackground(defaultSearchTextColor); searchText.setForeground(defaultSearchTextFgColor); textNotFoundLabel.setVisible(false); } } private boolean checkMatch(IMatrixView mv, IAnnotations am, String label) { if (searchPat == null) { return false; } if (searchPat.matcher(label).find()) { foundAnnotation = "ids"; foundValue = label; return true; } if (am == null) { return false; } if (!am.getIdentifiers().contains(label)) { return false; } boolean found = false; for (String annotation : am.getLabels()) { String value = am.getAnnotation(label, annotation); if (value == null) { continue; } found = searchPat.matcher(value).find(); if (found) { foundAnnotation = annotation; foundValue = value; break; } } return found; } private void updateSearch() { StringBuilder sb = new StringBuilder(); sb.append("("); if (!searchText.getText().isEmpty()) { if (anyWordChk.isSelected()) { String[] tokens = searchText.getText().split(" "); if (tokens.length > 0) { sb.append(Pattern.quote(tokens[0].trim())); for (int i = 1; i < tokens.length; i++) sb.append("|").append(Pattern.quote(tokens[i].trim())); } } else { sb.append(Pattern.quote(searchText.getText())); } sb.append(")"); if (matchCaseChk.isSelected()) { searchPat = Pattern.compile(sb.toString()); } else { searchPat = Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE); } } else { searchPat = null; } setNotFound(false); updateHighlight(); } private void updateHighlight() { IMatrixView mv = hm; Set<String> highlighted = new HashSet<>(); boolean searchRows = searchRows(); boolean found = false; if (searchRows) { hm.getColumns().clearHighlightedLabels(); // Search rows if (highlightAllChk.isSelected()) { IAnnotations am = hm.getRows().getAnnotations(); int rowCount = mv.getRows().size(); for (int index = 0; index < rowCount; index++) { String label = mv.getRows().getLabel(index); if (checkMatch(mv, am, label)) { highlighted.add(label); } } hm.getRows().setHighlightedLabels(highlighted); } else { hm.getRows().clearHighlightedLabels(); } } else { hm.getRows().clearHighlightedLabels(); // Column search if (highlightAllChk.isSelected()) { IAnnotations am = hm.getColumns().getAnnotations(); int colCount = mv.getColumns().size(); for (int index = 0; index < colCount; index++) { String label = mv.getColumns().getLabel(index); if (checkMatch(mv, am, label)) { highlighted.add(label); } } hm.getColumns().setHighlightedLabels(highlighted); } else { hm.getColumns().clearHighlightedLabels(); } } } private void searchPrev() { IMatrixView mv = hm; int leadRow = mv.getRows().indexOf(mv.getRows().getFocus()); int leadCol = mv.getColumns().indexOf(mv.getColumns().getFocus()); boolean searchRows = searchRows(); boolean found = false; int rowCount = mv.getRows().size(); if (searchRows && leadRow == -1) { leadRow = rowCount - 1; } if (searchRows && leadRow != -1) { // Row search IAnnotations am = hm.getRows().getAnnotations(); int index = leadRow - 1; if (index < 0) { index = rowCount - 1; } while (index != leadRow) { found = checkMatch(mv, am, mv.getRows().getLabel(index)); if (found) { break; } index--; if (index < 0) { index = rowCount - 1; } } if (index == leadRow) { found = checkMatch(mv, am, mv.getRows().getLabel(index)); } if (found) { mv.getRows().setFocus(hm.getRows().getLabel(index)); showSearchNotification(); heatmapPanel.makeRowFocusVisible(); } } else if (!searchRows && leadCol != -1) { // Column search IAnnotations am = hm.getColumns().getAnnotations(); int colCount = mv.getColumns().size(); int index = leadCol - 1; if (index < 0) { index = colCount - 1; } while (index != leadCol) { found = checkMatch(mv, am, mv.getColumns().getLabel(index)); if (found) { break; } index--; if (index < 0) { index = colCount - 1; } } if (index == leadCol) { found = checkMatch(mv, am, mv.getColumns().getLabel(index)); } if (found) { mv.getColumns().setFocus(mv.getColumns().getLabel(index)); showSearchNotification(); heatmapPanel.makeColumnFocusVisible(); } } setNotFound(!found); } private void showSearchNotification() { String text = "<html>Found <b>" + searchPat.matcher(foundValue).replaceAll("<u>$1</u>") //+ foundValue.replaceAll(searchPat.toString(), "<u>" + searchPat.toString() + "</u>") + "</b> in <i>" + foundAnnotation + "</i></html>"; if (notificationPopup == null) { notificationPopup = new WebNotificationPopup(); notificationPopup.setIcon(NotificationIcon.text); } notificationPopup.setContent(new WebLabel(text)); Application.get().showNotificationPopup(notificationPopup); searchText.requestFocus(); } private void searchNext() { IMatrixView mv = hm; int leadRow = mv.getRows().indexOf(mv.getRows().getFocus()); int leadCol = mv.getColumns().indexOf(mv.getColumns().getFocus()); boolean searchRows = searchRows(); boolean found = false; if (searchRows && leadRow == -1) { leadRow = 0; } if (!searchRows && leadCol == -1) { leadCol = 0; } if (searchRows) { // Row search IAnnotations am = hm.getRows().getAnnotations(); int rowCount = mv.getRows().size(); int index = leadRow + 1; if (index >= rowCount) { index = 0; } while (index != leadRow) { found = checkMatch(mv, am, mv.getRows().getLabel(index)); if (found) { break; } index++; if (index >= rowCount) { index = 0; } } if (index == leadRow) { found = checkMatch(mv, am, mv.getRows().getLabel(index)); } if (found) { mv.getRows().setFocus(mv.getRows().getLabel(index)); showSearchNotification(); heatmapPanel.makeRowFocusVisible(); } } else if (!searchRows && leadCol != -1) { // Column search IAnnotations am = hm.getColumns().getAnnotations(); int colCount = mv.getColumns().size(); int index = leadCol + 1; if (index >= colCount) { index = 0; } while (index != leadCol) { found = checkMatch(mv, am, mv.getColumns().getLabel(index)); if (found) { break; } index++; if (index >= colCount) { index = 0; } } if (index == leadCol) { found = checkMatch(mv, am, mv.getColumns().getLabel(index)); } if (found) { mv.getColumns().setFocus(mv.getColumns().getLabel(index)); showSearchNotification(); heatmapPanel.makeColumnFocusVisible(); } } setNotFound(!found); } public boolean searchRows() { return rowsOrColumns.isSelected(rowsButton.getModel()); } public void searchOnColumns(boolean searchColumns) { rowsOrColumns.clearSelection(); rowsOrColumns.setSelected(columnsButton.getModel(), searchColumns); rowsOrColumns.setSelected(rowsButton.getModel(), !searchColumns); } /** * 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() { rowsOrColumns = new javax.swing.ButtonGroup(); closeBtn = new javax.swing.JButton(); searchText = new javax.swing.JTextField(20); prevBtn = new javax.swing.JButton(); nextBtn = new javax.swing.JButton(); highlightAllChk = new javax.swing.JCheckBox(); matchCaseChk = new javax.swing.JCheckBox(); textNotFoundLabel = new javax.swing.JLabel(); anyWordChk = new javax.swing.JCheckBox(); rowsButton = new javax.swing.JRadioButton(); columnsButton = new javax.swing.JRadioButton(); setFocusable(false); setRequestFocusEnabled(false); closeBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource(IconNames.close16))); // NOI18N closeBtn.setFocusable(false); closeBtn.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { closeBtnActionPerformed(evt); } }); prevBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource(IconNames.nextEventLeft16))); // NOI18N prevBtn.setText("Previous"); prevBtn.setFocusable(false); nextBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource(IconNames.nextEventRight16))); // NOI18N nextBtn.setText("Next"); nextBtn.setFocusable(false); nextBtn.setRequestFocusEnabled(false); highlightAllChk.setSelected(true); highlightAllChk.setText("Highlight all"); highlightAllChk.setFocusable(false); highlightAllChk.setRequestFocusEnabled(false); matchCaseChk.setText("Match case"); matchCaseChk.setFocusable(false); matchCaseChk.setRequestFocusEnabled(false); textNotFoundLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/img/SearchNotFound.png"))); // NOI18N textNotFoundLabel.setText("Text not found"); anyWordChk.setText("Any word"); anyWordChk.setFocusable(false); anyWordChk.setRequestFocusEnabled(false); rowsOrColumns.add(rowsButton); rowsButton.setSelected(true); rowsButton.setText("Rows"); rowsButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rowsButtonActionPerformed(evt); } }); rowsOrColumns.add(columnsButton); columnsButton.setText("Columns"); FlowLayout layout = new FlowLayout(FlowLayout.RIGHT, 10, 10); this.setLayout(layout); this.add(anyWordChk); this.add(highlightAllChk); this.add(matchCaseChk); this.add(new JSeparator(JSeparator.VERTICAL)); this.add(rowsButton); this.add(columnsButton); this.add(prevBtn); this.add(nextBtn); this.add(searchText); this.add(closeBtn); /*javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addComponent(closeBtn).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(searchText, javax.swing.GroupLayout.PREFERRED_SIZE, 174, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(prevBtn).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(nextBtn).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(textNotFoundLabel).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(rowsButton).addGap(8, 8, 8).addComponent(columnsButton).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(highlightAllChk).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(matchCaseChk).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(anyWordChk).addContainerGap(16, Short.MAX_VALUE))); layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(closeBtn).addComponent(searchText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(prevBtn).addComponent(nextBtn).addComponent(highlightAllChk).addComponent(textNotFoundLabel).addComponent(matchCaseChk).addComponent(anyWordChk).addComponent(rowsButton).addComponent(columnsButton)));*/ }// </editor-fold>//GEN-END:initComponents private void closeBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeBtnActionPerformed close(); }//GEN-LAST:event_closeBtnActionPerformed private void rowsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rowsButtonActionPerformed // TODO add your handling code here: }//GEN-LAST:event_rowsButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JCheckBox anyWordChk; private javax.swing.JButton closeBtn; private javax.swing.JRadioButton columnsButton; private javax.swing.JCheckBox highlightAllChk; private javax.swing.JCheckBox matchCaseChk; private javax.swing.JButton nextBtn; private javax.swing.JButton prevBtn; private javax.swing.JRadioButton rowsButton; private javax.swing.ButtonGroup rowsOrColumns; private javax.swing.JTextField searchText; private javax.swing.JLabel textNotFoundLabel; // End of variables declaration//GEN-END:variables }