/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * 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/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.gui.dialog; import java.awt.Color; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JTextField; import edu.yu.einstein.genplay.core.manager.project.ProjectManager; import edu.yu.einstein.genplay.dataStructure.chromosome.Chromosome; import edu.yu.einstein.genplay.dataStructure.gene.Gene; import edu.yu.einstein.genplay.dataStructure.genomeWindow.SimpleGenomeWindow; import edu.yu.einstein.genplay.dataStructure.list.genomeWideList.geneList.GeneSearcher; import edu.yu.einstein.genplay.util.Images; /** * A dialog to search genes on a GeneListTrack * @author Julien Lajugie */ public class SearchGeneDialog extends JDialog implements ActionListener { private static final long serialVersionUID = -7154640426239852428L; // generated ID private static final Color NOTHING_FOUND_COLOR = new Color(230, 150, 115); // color of the textfield background when nothing is found private static JTextField jtfSearchGene; // text field for the input gene name private static JPanel jpOption; // panel containing the check boxes private static JCheckBox jcbMatchCase; // check box for the case sensitivity private static JCheckBox jcbWholeWord; // check box for searching whole word private static JCheckBox jcbIncremental; // check box for searching incrementaly (no need to click "Find") private static JCheckBox jcbChromosome; // check box for searching within the current chromosome only private static JButton jbValidInput; // valid button private static JButton jbNextMatch; // next match button private static JButton jbPreviousMatch; // previous match button private static JButton jbNextGene; // next gene button private static JButton jbPreviousGene; // previous gene button private static GeneSearcher geneSearcher; // object that searches the genes private static Color textFieldDefaultColor; // default color of the text field background /** * Creates and shows a {@link SearchGeneDialog} * @param parent parent component. Can be null * @param geneSearcher a {@link GeneSearcher} */ public static void showSearchGeneDialog(Frame parent, GeneSearcher geneSearcher) { SearchGeneDialog dialog = new SearchGeneDialog(parent, geneSearcher); dialog.setVisible(true); } /** * Private constructor. Creates an instance of {@link SearchGeneDialog} * @param parent parent component. Can be null * @param geneSearcher a {@link GeneSearcher} that searches the genes */ private SearchGeneDialog(Frame parent, GeneSearcher geneSearcher) { super(parent); SearchGeneDialog.geneSearcher = geneSearcher; // create the textfield jtfSearchGene = new JTextField(geneSearcher.getLastSearchedGeneName()); jtfSearchGene.putClientProperty("JTextField.variant", "search"); // retrieve the default background color of a text field textFieldDefaultColor = jtfSearchGene.getBackground(); jtfSearchGene.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { boolean show = false; if (jcbIncremental.isSelected()) { show = true; } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { show = true; } if (show) { // search a gene when the user type something in the input box Gene geneFound = SearchGeneDialog.geneSearcher.search(jtfSearchGene.getText()); Chromosome chromosome = SearchGeneDialog.geneSearcher.getGeneChromosome(); showGene(geneFound, chromosome); } } }); // create the match case check box jcbMatchCase = new JCheckBox("Match Case"); jcbMatchCase.setSelected(geneSearcher.isCaseSensitive()); jcbMatchCase.addActionListener(this); // create the whole word check box jcbWholeWord = new JCheckBox("Whole Word"); jcbWholeWord.setSelected(geneSearcher.isWholeWorld()); jcbWholeWord.addActionListener(this); // create the incremental check box jcbIncremental = new JCheckBox("Incremental"); // create the current chromosome search check box jcbChromosome = new JCheckBox("Search within the current chromosome only"); // create the option panel for the check box jpOption = new JPanel(); jpOption.setBorder(BorderFactory.createTitledBorder("Options")); jpOption.setLayout(new BoxLayout(jpOption, BoxLayout.PAGE_AXIS)); jpOption.add(jcbMatchCase); jpOption.add(jcbWholeWord); jpOption.add(jcbIncremental); jpOption.add(jcbChromosome); // create the Find button jbValidInput = new JButton("Find"); jbValidInput.setToolTipText("Move to the first match in the whole genome."); jbValidInput.addActionListener(this); // create the previous match button jbPreviousMatch = new JButton("< Prev match"); jbPreviousMatch.setToolTipText("Move to the previous match."); jbPreviousMatch.addActionListener(this); // create the next match button jbNextMatch = new JButton("Next match >"); jbNextMatch.setToolTipText("Move to the next match."); jbNextMatch.addActionListener(this); // create the previous gene button jbPreviousGene = new JButton("< Prev gene "); jbPreviousGene.setToolTipText("Move to the previous gene."); jbPreviousGene.addActionListener(this); // create the next gene button jbNextGene = new JButton(" Next gene >"); jbNextGene.setToolTipText("Move to the next gene."); jbNextGene.addActionListener(this); // add the components setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.gridy = 0; c.gridx = 0; c.gridwidth = 2; c.weightx = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; c.insets = new Insets(10, 10, 10, 10); add(jtfSearchGene, c); c.gridx = 2; c.weightx = 0; c.gridwidth = 1; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_END; add(jbValidInput, c); c.gridy++; c.gridx = 0; c.gridwidth = 3; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; add(jpOption, c); c.gridy++; c.gridwidth = 1; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LINE_START; c.insets = new Insets(3, 3, 3, 3); add(jbPreviousMatch, c); c.gridx = 1; c.gridwidth = 2; c.anchor = GridBagConstraints.LINE_END; add(jbNextMatch, c); c.gridy++; c.gridx = 0; c.gridwidth = 1; c.anchor = GridBagConstraints.LINE_START; add(jbPreviousGene, c); c.gridx = 1; c.gridwidth = 2; c.anchor = GridBagConstraints.LINE_END; add(jbNextGene, c); getRootPane().setDefaultButton(jbValidInput); setTitle("Find Gene"); setIconImages(Images.getApplicationImages()); pack(); setResizable(false); setLocationRelativeTo(parent); setAlwaysOnTop(true); } @Override public void actionPerformed(ActionEvent evt) { Gene geneFound = null; if (evt.getSource() == jcbMatchCase) { // case where the match case check box state changes geneFound = geneSearcher.setCaseSensitive(jcbMatchCase.isSelected()); } else if (evt.getSource() == jcbWholeWord) { // case where the whole word check box state changes geneFound = geneSearcher.setWholeWord(jcbWholeWord.isSelected()); } else { if (evt.getSource() instanceof JButton) { JButton source = (JButton) evt.getSource(); getRootPane().setDefaultButton(source); if (source == jbValidInput) { // case where the "go" button is clicked geneFound = geneSearcher.search(jtfSearchGene.getText()); } else if (source == jbNextMatch) { // case where the next match button is clicked geneFound = geneSearcher.searchNextMatch(); } else if (source == jbPreviousMatch) { // case where the previous match button is clicked geneFound = geneSearcher.searchPreviousMatch(); } else if (source == jbNextGene) { // case where the next gene button is clicked geneFound = geneSearcher.searchNextGene(); } else if (source == jbPreviousGene) { // case where the previous gene button is clicked geneFound = geneSearcher.searchPreviousGene(); } } } showGene(geneFound, geneSearcher.getGeneChromosome()); } /** * A match found in another chromosome than the current one AND in multi genome project can be annoying. * It may involve many loadings and GenPlay asks if the user really wants to change chromosome. * @param geneFound a match * @return true if the user allows (in specific case) to go to found gene, false otherwise */ private boolean canMove (Gene geneFound, Chromosome chromosome) { if (jcbChromosome.isSelected()) { if (!chromosome.equals(ProjectManager.getInstance().getProjectWindow().getGenomeWindow().getChromosome())) { return false; } } /*if (ProjectManager.getInstance().isMultiGenomeProject() && !geneFound.getChromo().equals(ProjectManager.getInstance().getProjectChromosome().getCurrentChromosome())) { Object[] options = {"Yes", "No"}; int n = JOptionPane.showOptionDialog(this, "The following match has been found in another chromosome,\ndo you want to continue?", "Chromosome changes", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[1]); return n == JOptionPane.YES_OPTION; }*/ return true; } /** * Colors the background of the input box when nothing is found. * Restores the default color when something is found or when nothing is searched * @param geneFound true if a gene is found, false if not */ private void setEditorColor(boolean geneFound) { if (geneFound) { jtfSearchGene.setBackground(textFieldDefaultColor); } else { if ((jtfSearchGene.getText() == null) || (jtfSearchGene.getText().isEmpty())) { jtfSearchGene.setBackground(textFieldDefaultColor); } else { jtfSearchGene.setBackground(NOTHING_FOUND_COLOR); } } } /** * Shows the result of the search on the browser * @param geneFound result of the search */ private void showGene(Gene geneFound, Chromosome chromosome) { if (geneFound != null) { if (canMove(geneFound, chromosome)) { jbNextMatch.setEnabled(true); jbPreviousMatch.setEnabled(true); jbNextGene.setEnabled(true); jbPreviousGene.setEnabled(true); // we want to see larger than the gene found int windowStart = geneFound.getStart() - ((geneFound.getStop() - geneFound.getStart()) * 3); int minimumDisplayableStart = - chromosome.getLength(); // we don't want the start to be smaller than the minimum displayable position windowStart = Math.max(windowStart, minimumDisplayableStart); int windowStop = geneFound.getStop() + ((geneFound.getStop() - geneFound.getStart()) * 3); int maximumDisplayableStop = chromosome.getLength() * 2; // we don't want the stop to be greater than the maximum displayable position windowStop = Math.min(windowStop, maximumDisplayableStop); SimpleGenomeWindow genomeWindow = new SimpleGenomeWindow(chromosome, windowStart, windowStop); ProjectManager.getInstance().getProjectWindow().setGenomeWindow(genomeWindow); setEditorColor(true); } } else { setEditorColor(false); jbNextMatch.setEnabled(false); jbPreviousMatch.setEnabled(false); jbNextGene.setEnabled(false); jbPreviousGene.setEnabled(false); } } }