1 /*
2  * Copyright (C) 2008  Genome Research Limited
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  *  @author: Tim Carver
19  */
20 
21 package uk.ac.sanger.artemis.circular;
22 
23 import java.awt.Color;
24 import java.awt.Dimension;
25 import java.awt.Font;
26 import java.awt.GridBagConstraints;
27 import java.awt.GridBagLayout;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.ItemEvent;
31 import java.awt.event.ItemListener;
32 import java.io.BufferedReader;
33 import java.io.File;
34 import java.io.FileNotFoundException;
35 import java.io.FileReader;
36 import java.io.FileWriter;
37 import java.io.IOException;
38 import java.io.Writer;
39 import java.util.StringTokenizer;
40 import java.util.Vector;
41 
42 import javax.swing.JButton;
43 import javax.swing.JCheckBox;
44 import javax.swing.JFileChooser;
45 import javax.swing.JFrame;
46 import javax.swing.JLabel;
47 import javax.swing.JMenu;
48 import javax.swing.JMenuBar;
49 import javax.swing.JMenuItem;
50 import javax.swing.JOptionPane;
51 import javax.swing.JPanel;
52 import javax.swing.JScrollPane;
53 import javax.swing.JTextField;
54 
55 import uk.ac.sanger.artemis.Entry;
56 import uk.ac.sanger.artemis.Feature;
57 import uk.ac.sanger.artemis.FeatureKeyPredicate;
58 import uk.ac.sanger.artemis.FeatureKeyQualifierPredicate;
59 import uk.ac.sanger.artemis.FeaturePredicate;
60 import uk.ac.sanger.artemis.FeaturePredicateConjunction;
61 import uk.ac.sanger.artemis.FeaturePredicateVector;
62 import uk.ac.sanger.artemis.FeatureVector;
63 import uk.ac.sanger.artemis.components.KeyChoice;
64 import uk.ac.sanger.artemis.components.QualifierChoice;
65 import uk.ac.sanger.artemis.components.StickyFileChooser;
66 import uk.ac.sanger.artemis.io.Key;
67 import uk.ac.sanger.artemis.io.Range;
68 import uk.ac.sanger.artemis.io.RangeVector;
69 
70 public class TrackManager extends JFrame
71 {
72   private static final long serialVersionUID = 1L;
73   private DNADraw dnaDraw;
74 
75   private KeyChoice keyChoice[];
76   private QualifierChoice qualifierChoice[];
77   private JTextField qualifierValue[];
78   private JCheckBox notQualifier[];
79   private JCheckBox showForward[];
80   private JCheckBox showReverse[];
81   private JCheckBox showAny[];
82   private TextFieldFloat trackSize[];
83   private TextFieldFloat trackPosition[];
84 
TrackManager(final DNADraw dnaDraw)85   public TrackManager(final DNADraw dnaDraw)
86   {
87     super("Track Manager");
88     this.dnaDraw = dnaDraw;
89     setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
90 
91     createMenu();
92     JScrollPane jsp = new JScrollPane(getPanelComponents());
93     getContentPane().add(jsp);
94     pack();
95   }
96 
97   /**
98    * Create a menu for the track manager.
99    */
createMenu()100   private void createMenu()
101   {
102     final JMenu menuFile = new JMenu("File");
103     final JMenuBar menuBar = new JMenuBar();
104     setJMenuBar(menuBar);
105     menuBar.add(menuFile);
106     menuFile.add(getExportTrackTemplateMenuItem(this, dnaDraw));
107     menuFile.add(getImportTrackTemplateMenuItem(this));
108     final JMenuItem closeMenu = new JMenuItem("Close");
109     closeMenu.addActionListener(new ActionListener()
110     {
111       public void actionPerformed(ActionEvent e)
112       {
113         TrackManager.this.setVisible(false);
114       }
115     });
116     menuFile.add(closeMenu);
117   }
118 
getImportTrackTemplateMenuItem(final JFrame f)119   protected JMenuItem getImportTrackTemplateMenuItem(final JFrame f)
120   {
121     final JMenuItem readTemplate = new JMenuItem("Import Track Template...");
122     readTemplate.addActionListener(new ActionListener()
123     {
124       public void actionPerformed(ActionEvent arg0)
125       {
126         StickyFileChooser fileDialog = new StickyFileChooser();
127         int status = fileDialog.showOpenDialog(f);
128         if(status == JFileChooser.CANCEL_OPTION)
129           return;
130 
131         final File fileRead = fileDialog.getSelectedFile();
132         if(!fileRead.exists())
133         {
134           JOptionPane.showMessageDialog(f,
135               fileRead.getName()+" not found.",
136               "Problem Reading File",
137               JOptionPane.WARNING_MESSAGE);
138           return;
139         }
140 
141         try
142         {
143           final FileReader reader = new FileReader(fileRead);
144           BufferedReader inputStream = new BufferedReader(reader);
145           String inLine = null;
146 
147           Track[] tracks = Wizard.getTracks();
148           int trackCount = 0;
149 
150           while ((inLine = inputStream.readLine()) != null)
151           {
152             if(inLine.startsWith("#") || inLine.trim().equals(""))
153               continue;
154 
155             if(trackCount >= tracks.length)
156             {
157               addTrack();
158               tracks = Wizard.getTracks();
159             }
160             tracks[trackCount].setPropertiesFromTemplate(inLine);
161             trackCount++;
162           }
163           inputStream.close();
164           reader.close();
165           refresh();
166         }
167         catch(FileNotFoundException e)
168         {
169           e.printStackTrace();
170         }
171         catch(IOException e)
172         {
173           e.printStackTrace();
174         }
175       }
176     });
177     return readTemplate;
178   }
179 
180   /**
181    * Menu Item with associated ActionListener for exporting the properties
182    * of this track.
183    * @param f
184    * @return
185    */
getExportTrackTemplateMenuItem(final JFrame f, final DNADraw dnaDraw)186   protected static JMenuItem getExportTrackTemplateMenuItem(final JFrame f,
187                                                             final DNADraw dnaDraw)
188   {
189     final JMenuItem saveTemplate = new JMenuItem("Export Track Template...");
190     saveTemplate.addActionListener(new ActionListener()
191     {
192       public void actionPerformed(ActionEvent arg0)
193       {
194         StickyFileChooser fileDialog = new StickyFileChooser();
195         int status = fileDialog.showSaveDialog(f);
196         if(status == JFileChooser.CANCEL_OPTION)
197           return;
198 
199         final File fileWrite = fileDialog.getSelectedFile();
200         if(fileWrite.exists())
201         {
202           status = JOptionPane.showConfirmDialog(f, fileWrite.getName()+
203                       " exists. Overwrite?",
204                       "Selected File Exists",
205                       JOptionPane.OK_CANCEL_OPTION);
206           if(status == JFileChooser.CANCEL_OPTION)
207             return;
208         }
209 
210         final Track[] tracks = Wizard.getTracks();
211         try
212         {
213           final Writer writer = new FileWriter(fileWrite);
214           Track.writeHeader(writer, dnaDraw);
215           for(int i=0; i<tracks.length; i++)
216             tracks[i].write(writer);
217           writer.close();
218         }
219         catch(IOException e)
220         {
221           e.printStackTrace();
222         }
223       }
224     });
225     return saveTemplate;
226   }
227 
getPanelComponents()228   private JPanel getPanelComponents()
229   {
230     final Track[] tracks = Wizard.getTracks();
231     GridBagLayout grid = new GridBagLayout();
232     final GridBagConstraints c = new GridBagConstraints();
233     c.ipady = 3;
234     c.ipadx = 5;
235 
236     final JPanel optionBox = new JPanel(grid);
237     keyChoice       = new KeyChoice[tracks.length];
238     qualifierChoice = new QualifierChoice[tracks.length];
239     qualifierValue  = new JTextField[tracks.length];
240     notQualifier    = new JCheckBox[tracks.length];
241     showForward     = new JCheckBox[tracks.length];
242     showReverse     = new JCheckBox[tracks.length];
243     showAny         = new JCheckBox[tracks.length];
244     trackSize       = new TextFieldFloat[tracks.length];
245     trackPosition   = new TextFieldFloat[tracks.length];
246 
247     c.anchor = GridBagConstraints.WEST;
248     c.gridx = 0;
249     c.gridy = 0;
250     optionBox.add(new JLabel("Track"), c);
251     c.gridx = 1;
252     optionBox.add(new JLabel("Key"), c);
253     c.gridx = 2;
254     c.gridwidth = 4;
255     optionBox.add(new JLabel("Qualifier"), c);
256     c.gridx = 6;
257     c.gridwidth = 1;
258     optionBox.add(new JLabel("Strand"), c);
259     c.gridx = 9;
260     optionBox.add(new JLabel("Size"), c);
261     c.gridx = 10;
262     optionBox.add(new JLabel("Position"), c);
263 
264     for(int i = 0; i < tracks.length; i++)
265     {
266       final Track track = tracks[i];
267       c.gridx = 0;
268       c.gridy = i+1;
269       c.anchor = GridBagConstraints.EAST;
270 
271       optionBox.add(new JLabel(Integer.toString(i+1)+" "+track.getEntry().getName()), c);
272 
273       c.gridx = 1;
274       c.anchor = GridBagConstraints.WEST;
275 
276       final Key key;
277       if(track.getKeyStr() != null)
278         key = new Key(track.getKeyStr());
279       else
280         key = new Key("-");
281 
282       Entry entry = dnaDraw.getArtemisEntryGroup().getDefaultEntry();
283       if(entry == null)
284         entry = dnaDraw.getArtemisEntryGroup().getSequenceEntry();
285       keyChoice[i] = new KeyChoice(
286           entry.getEntryInformation(),key);
287 
288       optionBox.add(keyChoice[i], c);
289 
290       c.gridx = 2;
291       notQualifier[i] = new JCheckBox("Not", !track.isNotQualifier());
292       optionBox.add(notQualifier[i], c);
293 
294       c.gridx = 3;
295       String qualifier = track.getQualifier();
296       final int n = i;
297       final JButton addQualifier = new JButton("ADD QUALIFIER");
298       addQualifier.addActionListener(new ActionListener()
299       {
300         public void actionPerformed(ActionEvent e)
301         {
302           c.gridx = 3;
303           c.gridy = n+1;
304           qualifierChoice[n] = new QualifierChoice(
305               dnaDraw.getArtemisEntryGroup().getDefaultEntry().getEntryInformation(),key, null, false);
306           optionBox.add(qualifierChoice[n], c);
307 
308           c.gridx = 4;
309           qualifierValue[n] = new JTextField(track.getQualifierValue(), 10);
310           optionBox.add(qualifierValue[n], c);
311 
312           optionBox.remove(addQualifier);
313           optionBox.repaint();
314           optionBox.revalidate();
315         }
316       });
317 
318       if(qualifier == null)
319         optionBox.add(addQualifier, c);
320       else
321       {
322         qualifierChoice[i] = new QualifierChoice(
323             entry.getEntryInformation(),key, qualifier, false);
324         optionBox.add(qualifierChoice[i], c);
325         c.gridx = 4;
326         qualifierValue[i] = new JTextField(track.getQualifierValue(), 10);
327         optionBox.add(qualifierValue[i], c);
328       }
329 
330       final JButton removeButton = new JButton("X");
331       Font font = dnaDraw.getFont().deriveFont(Font.BOLD);
332       removeButton.setFont(font);
333       removeButton.setToolTipText("REMOVE QUALIFIER");
334       c.gridx = 5;
335       optionBox.add(removeButton, c);
336       removeButton.setForeground(new Color(139,35,35));
337       removeButton.addActionListener(new ActionListener()
338       {
339         public void actionPerformed(ActionEvent e)
340         {
341           if(qualifierChoice[n] != null)
342           {
343             optionBox.remove(qualifierChoice[n]);
344             optionBox.remove(qualifierValue[n]);
345           }
346 
347           c.gridx = 3;
348           c.gridy = n+1;
349           optionBox.add(addQualifier, c);
350           qualifierChoice[n] = null;
351           optionBox.repaint();
352           optionBox.revalidate();
353         }
354       });
355       removeButton.setPreferredSize(new Dimension(35,
356             removeButton.getPreferredSize().height));
357 
358       c.gridx = 6;
359       showForward[i] = new JCheckBox("Forward", track.isShowForward());
360       optionBox.add(showForward[i], c);
361 
362       c.gridx = 7;
363       showReverse[i] = new JCheckBox("Reverse", track.isShowReverse());
364       optionBox.add(showReverse[i], c);
365 
366       c.gridx = 8;
367       showAny[i] = new JCheckBox("Any", track.isAny());
368       optionBox.add(showAny[i], c);
369 
370       c.gridx = 9;
371       trackSize[i] = new TextFieldFloat();
372       trackSize[i].setValue(track.getSize());
373       trackSize[i].setColumns(4);
374       optionBox.add(trackSize[i], c);
375 
376       c.gridx = 10;
377       trackPosition[i] = new TextFieldFloat();
378       trackPosition[i].setValue(track.getPosition());
379       trackPosition[i].setColumns(4);
380       optionBox.add(trackPosition[i], c);
381 
382 
383       c.gridx = 11;
384       final JButton colourSelection = new JButton("COLOUR");
385 
386       colourSelection.addActionListener(new ActionListener()
387       {
388         public void actionPerformed(ActionEvent e)
389         {
390           final JFrame frameColour = new JFrame("Track "+(n+1)+" Colour");
391           GridBagLayout grid = new GridBagLayout();
392           final GridBagConstraints c = new GridBagConstraints();
393           c.ipady = 3;
394           c.ipadx = 5;
395           c.anchor = GridBagConstraints.WEST;
396 
397           final JPanel optionBox = new JPanel(grid);
398           frameColour.getContentPane().add(optionBox);
399 
400           Color col = track.getColour();
401           if(col == null)
402             col = Color.red;
403 
404           final JButton colourButton = GeneticMarker.setUpColorButton(col);
405           c.gridx = 0;
406           c.gridy = 0;
407           optionBox.add(new JLabel("Pick a Colour:"),c);
408           c.gridx = 1;
409           c.gridy = 0;
410           optionBox.add(colourButton,c);
411 
412           final JCheckBox colourQualifier = new JCheckBox("Use colour qualifier");
413           if(track.getColour() == null)
414             colourQualifier.setSelected(true);
415           else
416             colourQualifier.setSelected(false);
417 
418           colourQualifier.addItemListener(new ItemListener()
419           {
420             public void itemStateChanged(ItemEvent e)
421             {
422               if(colourQualifier.isSelected())
423                 track.setColour(null);
424               else
425                 track.setColour(colourButton.getBackground());
426               dnaDraw.repaint();
427             }
428           });
429 
430           final JButton ok = new JButton("Apply Colour to All");
431           ok.addActionListener(new ActionListener()
432           {
433             public void actionPerformed(ActionEvent e)
434             {
435               track.setColour(colourButton.getBackground());
436               colourQualifier.setSelected(false);
437               dnaDraw.repaint();
438             }
439           });
440           c.gridx = 2;
441           c.gridy = 0;
442           optionBox.add(ok,c);
443 
444           c.gridx = 1;
445           c.gridy = 1;
446           c.gridwidth = 2;
447           optionBox.add(colourQualifier, c);
448 
449           c.gridx = 1;
450           c.gridy = 2;
451           final JButton close = new JButton("Close");
452           close.addActionListener(new ActionListener()
453           {
454             public void actionPerformed(ActionEvent e)
455             {
456               frameColour.dispose();
457             }
458           });
459           optionBox.add(close,c);
460 
461           frameColour.pack();
462           frameColour.setVisible(true);
463         }
464       });
465       optionBox.add(colourSelection, c);
466 
467 
468       c.gridx = 12;
469       final int trackIndex = i;
470       final JButton deleteTrack = new JButton("DELETE");
471       deleteTrack.addActionListener(new ActionListener()
472       {
473         public void actionPerformed(ActionEvent e)
474         {
475           int val = JOptionPane.showConfirmDialog(dnaDraw,
476               "Delete track "+(trackIndex+1)+"?",
477               "Confirm", JOptionPane.OK_CANCEL_OPTION);
478           if(val == JOptionPane.CANCEL_OPTION)
479             return;
480 
481           Wizard.deleteTrack(trackIndex);
482           getContentPane().removeAll();
483 
484           JScrollPane jsp = new JScrollPane(getPanelComponents());
485           getContentPane().add(jsp);
486           pack();
487 
488           setVisible(true);
489           update(Wizard.getTracks());
490         }
491       });
492       optionBox.add(deleteTrack, c);
493     }
494 
495     c.gridx = 0;
496     c.gridy = tracks.length+1;
497     c.gridwidth = 2;
498 
499     JButton updateButton = new JButton("UPDATE TRACKS");
500     optionBox.add(updateButton,c);
501     updateButton.addActionListener(new ActionListener()
502     {
503       public void actionPerformed(ActionEvent e)
504       {
505          update(tracks);
506       }
507     });
508 
509     c.gridx = 2;
510     c.gridy = tracks.length+1;
511     c.gridwidth = 2;
512 
513     JButton addTrackButton = new JButton("ADD TRACK");
514     optionBox.add(addTrackButton,c);
515     addTrackButton.addActionListener(new ActionListener()
516     {
517       public void actionPerformed(ActionEvent e)
518       {
519         addTrack();
520       }
521     });
522 
523     return optionBox;
524   }
525 
refresh()526   protected void refresh()
527   {
528     getContentPane().removeAll();
529     JScrollPane jsp = new JScrollPane(getPanelComponents());
530     getContentPane().add(jsp);
531     pack();
532   }
533 
addTrack()534   private void addTrack()
535   {
536     Entry entry;
537     if(Wizard.getTracks().length > 0)
538       entry = Wizard.getTracks()[0].getEntry();
539     else
540       entry = dnaDraw.getArtemisEntryGroup().elementAt(0);
541 
542     Wizard.addTrack( entry );
543     refresh();
544     setVisible(true);
545   }
546 
547   /**
548    * Update the tracks based on the Track Manager settings
549    * @param tracks
550    */
update(final Track[] tracks)551   public void update(final Track[] tracks)
552   {
553     // update tracks
554     for(int i=0; i<tracks.length; i++)
555     {
556       if(keyChoice[i].getSelectedItem().getKeyString().equals("-"))
557       {
558         tracks[i].setFeaturePredicate(null);
559         tracks[i].setAny(showAny[i].isSelected());
560         tracks[i].setKeyStr("-");
561       }
562       else
563       {
564         tracks[i].setKeyStr(keyChoice[i].getSelectedItem().getKeyString());
565         if(qualifierChoice[i] == null)
566         {
567           tracks[i].setFeaturePredicate(new FeatureKeyPredicate(keyChoice[i].getSelectedItem()));
568           tracks[i].setQualifier(null);
569         }
570         else
571         {
572           if(qualifierValue[i].getText().trim().equals(""))
573           {
574             tracks[i].setFeaturePredicate(
575                 new FeatureKeyQualifierPredicate(keyChoice[i].getSelectedItem(),
576                   (String)qualifierChoice[i].getSelectedItem(), !notQualifier[i].isSelected()));
577           }
578           else
579           {
580 
581             final FeaturePredicateVector temp_predicates =
582               new FeaturePredicateVector();
583 
584             //final StringVector words =
585             //  StringVector.getStrings(search_text, " ");
586 
587             final StringTokenizer tok = new StringTokenizer(qualifierValue[i].getText(), " \n");
588 
589             while(tok.hasMoreTokens())
590             {
591               final String this_word = tok.nextToken().trim();
592               final FeaturePredicate new_predicate =
593                 new FeatureKeyQualifierPredicate(keyChoice[i].getSelectedItem(),
594                                                  (String)qualifierChoice[i].getSelectedItem(),
595                                                  this_word,
596                                                  false,
597                                                  true);
598 
599               temp_predicates.add(new_predicate);
600             }
601 
602             FeaturePredicateConjunction key_and_qualifier_predicate =
603               new FeaturePredicateConjunction(temp_predicates,
604                                               FeaturePredicateConjunction.OR);
605 
606 
607             tracks[i].setFeaturePredicate(key_and_qualifier_predicate);
608           }
609           tracks[i].setQualifier((String)qualifierChoice[i].getSelectedItem());
610           tracks[i].setQualifierValue(qualifierValue[i].getText());
611         }
612         tracks[i].setAny(false);
613       }
614       tracks[i].setShowForward(showForward[i].isSelected());
615       tracks[i].setShowReverse(showReverse[i].isSelected());
616       tracks[i].setNotQualifier(!notQualifier[i].isSelected());
617       tracks[i].setSize((float) trackSize[i].getValue());
618       tracks[i].setPosition(trackPosition[i].getValue());
619     }
620 
621     // update viewer
622     updateDNADraw(dnaDraw, tracks);
623   }
624 
625 
updateDNADraw(final DNADraw dnaDraw, final Track[] tracks)626   private static void updateDNADraw(final DNADraw dnaDraw, final Track[] tracks)
627   {
628     dnaDraw.getBlock().removeAll(dnaDraw.getBlock());
629     final FeatureVector features = dnaDraw.getArtemisEntryGroup().getAllFeatures();
630 
631     for(int i=0; i<features.size(); i++)
632     {
633       Feature f = features.elementAt(i);
634       Vector myTracks = new Vector();
635 
636       for(int j=0; j<tracks.length; j++)
637       {
638         if(tracks[j].isOnTrack(f))
639           myTracks.add(tracks[j]);
640       }
641 
642       if(myTracks.size() < 1)
643         continue;
644 
645       Color col = f.getColour();
646       if(col == null || col.equals(Color.white))
647         col = Color.lightGray;
648 
649       RangeVector ranges = f.getLocation().getRanges();
650 
651       for(int j=0; j<ranges.size(); j++)
652       {
653         Range range = (Range) ranges.get(j);
654 
655         for(int k=0; k<myTracks.size(); k++)
656         {
657           Track myTrack = (Track)myTracks.get(k);
658           Block drawBlock = new Block(f.getIDString(),
659             range.getStart(),
660             range.getEnd(),
661             col,
662             myTrack.getSize(),
663             myTrack, dnaDraw);
664 
665           drawBlock.setFeature(f);
666           dnaDraw.getBlock().add(drawBlock);
667         }
668       }
669     }
670 
671     dnaDraw.repaint();
672   }
673 
674 }
675 
676