package contextViewer; import genomeObjects.GenomicElement; import java.awt.Color; import java.awt.Dimension; import java.awt.FileDialog; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import org.sourceforge.jlibeps.epsgraphics.EpsGraphics2D; import moduls.frm.Panels.Jpan_btn_NEW; public class GeneColorLegendPanel extends JPanel implements MouseListener{ private static final long serialVersionUID = 1L; //Fields //Content-related private SharedHomology[] GeneList; private String ECronType; private GeneColorLegendFrame gclf; private String ItemsToShow; private RenderedGenomesPanel rgp; public RenderedGenomesPanel getRgp() { return rgp; } public void setRgp(RenderedGenomesPanel rgp) { this.rgp = rgp; } //graphics display-related //to determine dimensions / sizes private Dimension panelDim; private int HeaderHeight = 30; private int LegendUnitHeight = 30; private int WholeColorMappingHeight; private int WholeColorMappingWidth; private int VerticalUnderBuffer = 20; private int LabelSpacer = 10; private int VerticalSpaceHeaderColors = 10; private int LabelVerticalSpacer = 15; private int LongestAnnotation = 0; private int WidthBuffer = 30; private int LegendWidth = (600-2*WidthBuffer); private int RectangleHeight = 20; private int RectangleWidth = 41; private int SelectedEntryBuffer = 3; //constant - cluster width private int ClusterColumnWidth = 100; //font settings private Font fontStandard = new Font("Dialog", Font.BOLD, 10); private Font fontHeader = new Font("Dialog", Font.BOLD, 16); final FontRenderContext renderContext = new FontRenderContext(null, true, true); //export menu / mouse clicked private Point PlaceClicked; private boolean RectangleSelected = false; private JPopupMenu ExportMenu; //clickable rectangles private boolean[] SelectedRectangles; private Rectangle[] Rectangles; private int LastSelectedNode = -1; private LinkedList<SharedHomology> SelectedColors; //Constructor public GeneColorLegendPanel(RenderedGenomesPanel rgp, GeneColorLegendFrame gclf, LinkedList<SharedHomology> genes, String ShowOption){ super(); this.addMouseListener(this); //pre-processing this.rgp = rgp; this.gclf = gclf; GeneList = new SharedHomology[genes.size()]; for (int i = 0; i <genes.size(); i++){ GeneList[i] = genes.get(i); } this.SelectedRectangles = new boolean[genes.size()]; Arrays.fill(SelectedRectangles, false); this.Rectangles = new Rectangle[genes.size()]; this.ECronType = genes.get(0).getECRONType(); this.ItemsToShow = ShowOption; if (this.ItemsToShow.contentEquals("Complete")){ this.collectBioInfo(); } //process ready for display this.computePanelDimension(); this.computeRectangles(); this.sortColors(); //prepare export menu this.InitializeExportMenu(); } // ----- pre-processing ----------------------------------// //collect additional information, if necessary public void collectBioInfo(){ //check cluster numbers for agreement if (this.ECronType.contentEquals("annotation")){ for (SharedHomology SH : this.GeneList){ //collect all annotations HashSet<Integer> Clusters = new HashSet<Integer>(); for (GenomicElement E : SH.getMembers()){ Clusters.add(E.getClusterID()); } // if there was only one cluster ID among all annotations, map this annotation to the cluster. if (Clusters.size() == 1){ SH.setClusterID(SH.getMembers().get(0).getClusterID()); } else { SH.setClusterID(-1); //indicates a mixed case } } } else { for (SharedHomology SH : this.GeneList){ //collect all annotations HashSet<String> Clusters = new HashSet<String>(); for (GenomicElement E : SH.getMembers()){ Clusters.add(E.getAnnotation()); } // if there was only one cluster ID among all annotations, map this annotation to the cluster. if (Clusters.size() == 1){ SH.setAnnotation(SH.getMembers().get(0).getAnnotation().toUpperCase()); } else { SH.setAnnotation("multiple annotations exist"); //indicates a mixed case } } } } //determine appropriate dimension for panel, based on number of colors public void computePanelDimension(){ //default width: size of window Dimension d = this.gclf.getDim(); int DimTotalWidth = (int) d.getWidth() - 2*this.WidthBuffer; //longer width, if appropriate //annotation or annotation + cluster cases if (this.ItemsToShow.contentEquals("Annotations")){ for (int i = 0; i <this.GeneList.length; i++){ //null catching if (this.GeneList[i].getAnnotation() == null){ this.GeneList[i].setAnnotation(""); } TextLayout label = new TextLayout(this.GeneList[i].getAnnotation(),fontStandard,renderContext); //determine longest annotation, and re-do rendering if ((int)label.getBounds().getWidth() > this.LongestAnnotation){ this.LongestAnnotation = (int)label.getBounds().getWidth(); } } } else if (this.ItemsToShow.contentEquals("Complete")) { for (int i = 0; i <this.GeneList.length; i++){ //null catching - no annotation provided if (this.GeneList[i].getAnnotation() == null){ this.GeneList[i].setAnnotation("none"); } TextLayout label = new TextLayout(this.GeneList[i].getAnnotation(),fontStandard,renderContext); //determine longest annotation, and re-do rendering if ((int)label.getBounds().getWidth() > this.LongestAnnotation){ this.LongestAnnotation = (int)label.getBounds().getWidth(); } } } //longsize parameter int LongSize = this.LongestAnnotation + this.RectangleWidth + this.LabelSpacer + 2 * this.WidthBuffer + this.ClusterColumnWidth; //update the width according to longest case if (LongSize > DimTotalWidth){ DimTotalWidth = LongSize; } //determine height of the table area this.WholeColorMappingHeight = (this.LegendUnitHeight * this.GeneList.length); //total height + width depend on other parameters int DimTotalHeight = this.HeaderHeight + this.VerticalUnderBuffer + (this.LegendUnitHeight * this.GeneList.length); //set dimension this.setPanelDim(new Dimension(DimTotalWidth,DimTotalHeight)); //set size of this panel. this.setPreferredSize(this.panelDim); } //add clickable rectangles public void computeRectangles(){ //add a rectangle for each for (int i = 0; i < GeneList.length; i++){ if (this.ItemsToShow.contentEquals("Clusters")){ Rectangles[i] = (new Rectangle(this.WidthBuffer - this.SelectedEntryBuffer, HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors - this.SelectedEntryBuffer, (int) (ClusterColumnWidth * 1.5), RectangleHeight + 2*this.SelectedEntryBuffer)); } else { Rectangles[i] = (new Rectangle(this.WidthBuffer - this.SelectedEntryBuffer, HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors - this.SelectedEntryBuffer, (int)this.panelDim.getWidth() - 2* this.WidthBuffer + 2*this.SelectedEntryBuffer, RectangleHeight + 2*this.SelectedEntryBuffer)); } } } //sort colors by increasing cluster ID number or alphabetical annotations public void sortColors(){ if (this.ItemsToShow.contentEquals("Clusters") || this.ItemsToShow.contentEquals("Complete")){ //bubble-sorting by cluster ID for (int i = 0; i < this.GeneList.length-1; i++){ for (int j = 0; j <this.GeneList.length-1; j++){ if (this.GeneList[j].getClusterID() > this.GeneList[j+1].getClusterID()){ SharedHomology sh = this.GeneList[j]; this.GeneList[j] = this.GeneList[j+1]; this.GeneList[j+1] = sh; } } } } else { //bubble-sorting alphabetically by annotations for (int i = 0; i < this.GeneList.length-1; i++){ for (int j = 0; j <this.GeneList.length-1; j++){ int Comparison = this.GeneList[j].getAnnotation().compareTo( this.GeneList[j+1].getAnnotation()); if (Comparison > 0){ SharedHomology sh = this.GeneList[j]; this.GeneList[j] = this.GeneList[j+1]; this.GeneList[j+1] = sh; } } } } } // ----- drawing-related ---------------------------------// //paint all components public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; this.draftLabels(g2d); this.draftColorEntries(g2d); this.draftBoundingRectangles(g2d); } //draw labels public void draftLabels(Graphics2D g2d){ //color label TextLayout color = new TextLayout("Color",fontHeader,renderContext); color.draw(g2d,this.WidthBuffer,this.HeaderHeight); this.RectangleWidth = (int) color.getBounds().getWidth(); //appropriate header TextLayout header; if (this.ItemsToShow.contentEquals("Annotations")){ header = new TextLayout("Annotation",fontHeader,renderContext); } else if (this.ItemsToShow.contentEquals("Clusters")){ header = new TextLayout("Cluster ID",fontHeader,renderContext); } else { header = new TextLayout("Cluster ID",fontHeader,renderContext); TextLayout header2; header2 = new TextLayout("Annotation",fontHeader,renderContext); header2.draw(g2d,this.WidthBuffer + this.RectangleWidth + this.LabelSpacer + this.ClusterColumnWidth, this.HeaderHeight); } //paint headers on the panel header.draw(g2d, this.WidthBuffer + this.RectangleWidth + this.LabelSpacer, this.HeaderHeight); //labels TextLayout label; if (this.ItemsToShow.contentEquals("Annotations")){ for (int i = 0; i <this.GeneList.length; i++){ label = new TextLayout(this.GeneList[i].getAnnotation(),fontStandard,renderContext); label.draw(g2d,this.WidthBuffer + this.RectangleWidth + this.LabelSpacer, HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors+LabelVerticalSpacer); } } else if ((this.ItemsToShow.contentEquals("Clusters"))){ for (int i = 0; i <this.GeneList.length; i++){ String ClusterNumber; if (this.GeneList[i].getClusterID() == -1){ ClusterNumber = "mixed"; } else if (this.GeneList[i].getClusterID() == 0){ ClusterNumber = "none"; } else { ClusterNumber = Integer.toString(this.GeneList[i].getClusterID()); } label = new TextLayout(ClusterNumber,fontHeader,renderContext); label.draw(g2d,this.WidthBuffer + this.RectangleWidth + this.LabelSpacer, HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors+LabelVerticalSpacer); } } else { for (int i = 0; i <this.GeneList.length; i++){ String ClusterNumber; if (this.GeneList[i].getClusterID() == -1){ ClusterNumber = "mixed"; } else if (this.GeneList[i].getClusterID() == 0){ ClusterNumber = "none"; } else { ClusterNumber = Integer.toString(this.GeneList[i].getClusterID()); } label = new TextLayout(ClusterNumber,fontHeader,renderContext); label.draw(g2d,this.WidthBuffer + this.RectangleWidth + this.LabelSpacer, HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors+LabelVerticalSpacer); TextLayout label2 = new TextLayout(this.GeneList[i].getAnnotation(),fontStandard,renderContext); label2.draw(g2d,this.WidthBuffer + this.RectangleWidth + this.LabelSpacer + this.ClusterColumnWidth, HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors+LabelVerticalSpacer); } } } //draw color entries public void draftColorEntries(Graphics2D g2d){ //print each color for (int i = 0; i <this.GeneList.length; i++){ g2d.setColor(this.GeneList[i].getColor()); g2d.fillRect(WidthBuffer,HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors, RectangleWidth, RectangleHeight); g2d.setColor(Color.BLACK); g2d.drawRect(WidthBuffer,HeaderHeight+(LegendUnitHeight*i)+VerticalSpaceHeaderColors, RectangleWidth, RectangleHeight); } } //draft bounding rectangles public void draftBoundingRectangles(Graphics2D g2d){ g2d.setColor(Color.RED); //paint appropriate nodenumbers for (int i = 0; i < SelectedRectangles.length; i++) { if (SelectedRectangles[i] == true){ g2d.draw(Rectangles[i]); } } g2d.setColor(Color.BLACK); } // ----- export menu-related ---------------------------------// //create the pop-up menu object private void InitializeExportMenu(){ //create action listener ActionListener exportAction = new ActionListener(){ public void actionPerformed(final ActionEvent evt) { // SAVE WHOLE CONTEXT try { final BufferedImage buff; //draw image and save picture if (evt.getActionCommand().equals("Save legend as JPG")){ buff = drawBufferedImage("jpg"); savePicture(buff, "jpg"); } else if (evt.getActionCommand().equals("Save legend as PNG")){ buff = drawBufferedImage("png"); savePicture(buff, "png"); } else if (evt.getActionCommand().equals("Save legend as EPS")){ String EPSString = drawEPS(); saveEPS(EPSString); } } catch (Exception e) { JOptionPane.showMessageDialog(null, "Unable to export the legend as an image.", "Image Writing Problem",JOptionPane.ERROR_MESSAGE); } } }; //set export menu this.ExportMenu = new JPopupMenu(); //create menu items final JMenuItem me0 = new JMenuItem("Save legend as JPG"); final JMenuItem me1 = new JMenuItem("Save legend as PNG"); final JMenuItem me2 = new JMenuItem("Save legend as EPS"); //add action listeners me0.addActionListener(exportAction); me1.addActionListener(exportAction); me2.addActionListener(exportAction); //build menu ExportMenu.add(me0); ExportMenu.add(me1); ExportMenu.add(me2); } //method to save picture private void savePicture(BufferedImage buff, String extension) { String sPath; String sNameNoExt = Jpan_btn_NEW.getFileNameNoExt(); final FileDialog fd = new FileDialog(gclf, "Export " + extension.toUpperCase() + " Image", FileDialog.SAVE); fd.setFile(sNameNoExt + "." + extension); fd.setVisible(true); if (fd.getFile() != null){ sPath = fd.getDirectory() + fd.getFile(); final File OutputFile = new File(sPath); try { ImageIO.write(buff, extension, OutputFile); } catch (IOException e) { JOptionPane.showMessageDialog(null, "Image Writing Error", "The picture could not be created.",JOptionPane.ERROR_MESSAGE); } } } //generate JPG or PNG image private BufferedImage drawBufferedImage(String extension){ if (extension.contentEquals("jpg") || extension.contentEquals("png")) { Graphics2D g2d; final double width_Mon = this.getSize().getWidth(); final double height_Mon = this.getSize().getHeight(); final BufferedImage buff = new BufferedImage((int) width_Mon, (int) height_Mon, BufferedImage.TYPE_INT_RGB); g2d = buff.createGraphics(); this.paintComponent(g2d); g2d.dispose(); return buff; } else { return null; } } //produce an EPS private String drawEPS(){ EpsGraphics2D g2d = new EpsGraphics2D(); this.paintComponent(g2d); return g2d.toString(); } //save an EPS private void saveEPS(String EPS){ String sPath; String sNameNoExt = Jpan_btn_NEW.getFileNameNoExt(); final FileDialog fd = new FileDialog(gclf, "Export EPS Image", FileDialog.SAVE); fd.setFile(sNameNoExt + ".eps"); fd.setVisible(true); if (fd.getFile() != null){ sPath = fd.getDirectory() + fd.getFile(); final File OutputFile = new File(sPath); try { BufferedWriter bw = new BufferedWriter(new FileWriter(OutputFile)); bw.write(EPS); bw.flush(); bw.close(); } catch (IOException e) { JOptionPane.showMessageDialog(null, "Image Saving Error", "The picture could not be saved.",JOptionPane.ERROR_MESSAGE); } } } //mouse click methods // ----- mouse-click related -----------------------------// @Override public void mouseClicked(MouseEvent e) { //update place clicked this.PlaceClicked = e.getPoint(); //select a set of genes / clusters in the main panel if (SwingUtilities.isLeftMouseButton(e) || SwingUtilities.isMiddleMouseButton(e)){ //clicked on the legend. this.rgp.setClickedOnLegend(true); //initialize boolean[] SelectedAfterClick = new boolean[SelectedRectangles.length]; Arrays.fill(SelectedAfterClick, Boolean.FALSE); //reset selected color list appropriately. SelectedColors = new LinkedList<SharedHomology>(); //update with the current existing set, if appropriate if (SelectedRectangles != null){ if (e.isShiftDown() == true || e.isControlDown() == true){ SelectedAfterClick = SelectedRectangles; } } //draw a box around the correct coordinate for (int i = 0; i < this.Rectangles.length; i++){ //System.out.println(RectanglesSurroundingLabels[i].getMinX() + " and " + RectanglesSurroundingLabels[i].getMinY()); if (Rectangles[i].contains(PlaceClicked)){ if (e.isShiftDown() == false && e.isControlDown() == false){ SelectedAfterClick[i] = true; //no button } else if (e.isShiftDown() == false && e.isControlDown() == true){ if (SelectedAfterClick[i] == true){ SelectedAfterClick[i] = false; } else { SelectedAfterClick[i] = true; } } else { if (LastSelectedNode != -1){ //determine relative location of selected node to current shift+clicked node if (LastSelectedNode <= i){ for (int j = LastSelectedNode; j<= i; j++){ SelectedAfterClick[j] = true; } } else { for (int j = LastSelectedNode; j >= i; j--){ SelectedAfterClick[j] = true; } } } else { SelectedAfterClick[i] = true; //no previous selected node } } //update SelectedRectangles = SelectedAfterClick; //update last selected node LastSelectedNode = i; } } //update list in other frame LinkedList<Color> currentlySelectedGeneColors = new LinkedList<Color>(); //update list for (int i = 0; i <SelectedRectangles.length; i++){ if (SelectedRectangles[i]){ SelectedColors.add(GeneList[i]); currentlySelectedGeneColors.add(GeneList[i].getColor()); } } //update list in other frame. rgp.setCurrentlySelectedGeneColors(currentlySelectedGeneColors); //redraw this.repaint(); this.rgp.repaint(); } //export pop-up menu if (SwingUtilities.isRightMouseButton(e)){ //trigger pop-up menu display this.ExportMenu.show(e.getComponent(), e.getXOnScreen(), e.getYOnScreen()); //reposition appropriately this.ExportMenu.setLocation(e.getXOnScreen(),e.getYOnScreen()); } } @Override public void mouseEntered(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseExited(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mousePressed(MouseEvent arg0) { // TODO Auto-generated method stub } @Override public void mouseReleased(MouseEvent arg0) { // TODO Auto-generated method stub } // ----- getters + setters --------------------------------// public SharedHomology[] getGeneList() { return GeneList; } public void setGeneList(SharedHomology[] geneList) { GeneList = geneList; } public Dimension getPanelDim() { return panelDim; } public void setPanelDim(Dimension panelDim) { this.panelDim = panelDim; } public LinkedList<SharedHomology> getSelectedColors() { return SelectedColors; } public void setSelectedColors(LinkedList<SharedHomology> selectedColors) { SelectedColors = selectedColors; } public boolean[] getSelectedRectangles() { return SelectedRectangles; } public void setSelectedRectangles(boolean[] selectedRectangles) { SelectedRectangles = selectedRectangles; } }