resources/about.html000644 001750 001750 00000006664 14205451130 015405 0ustar00cyrilcyril000000 000000

SPVIEW

General Disclaimer

SPVIEW (SPectrum-VIEWer) allows to assign experimental high-resolution molecular spectra. The input elements are:

The data are plotted in the center of the window (data area) and the peaks and predicted lines are displayed as ticks in the upper part (pred/exp area). The peaks and/or predicted lines ticks can be associated to one of the data files so that they follow its drawing modifications (X shifts). The association is shown by a common color.

Each assignment is either of frequency or intensity type. Each type is specified by a mark which should be either a space (not assigned with this type), a '+' (to be fitted with this type) or a '-' (to be ignored in the fit with this type). Experimental ticks are topped by global frequency and intensity marks. Each global mark is either a space (if no assignment is of this type), a '+' or a '-' (if all assignments of this type have this mark) or a '?' (if assignments of this type do not have all the same mark).

A so-called EXASG string is associated to each assignment. It is used by the fit job to select the lines to be taken into account. The initial default value of this string for new assignments is 'SPVIEW_EXASG'. This default value can be modified.

The pred/exp area ticks can be selected with the mouse and in this way predicted lines can be assigned to each experimental peak (see Popup menus below). Once this is done, one can save the assignment file (Assignment menu).

Popup menus:

Contact

Website

resources/JFSelPred/jmiaddassf.html000644 001750 001750 00000000315 14000365526 020147 0ustar00cyrilcyril000000 000000

Add pred assignment(s) to exp

Add the selected predictions as frequency assignments to the selected experimental peak.

resources/JFSelPred/jmipredno.html000644 001750 001750 00000000201 14000365526 020023 0ustar00cyrilcyril000000 000000

Select

Unselect all predictions in a list.

sources/org/spview/gui/JFCreatePeakFile.java000644 001750 001750 00000025340 14176571005 021640 0ustar00cyrilcyril000000 000000 package org.spview.gui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.Point2D; import java.io.File; import java.util.ArrayList; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.filechooser.FileNameExtensionFilter; import org.spview.filehandler.FortranFormat; import org.spview.filehandler.DataFile; import org.spview.peakfinding.DigitalFilter; import java.awt.Toolkit; ///////////////////////////////////////////////////////////////////// /** * Window for peak file creation. */ public class JFCreatePeakFile extends JFrame implements ActionListener { /** * */ private static final long serialVersionUID = -7784282622048412967L; private final String workd; // working directory // private final JButton jbfile; // spectrum file choice private final JLabel jlnfile; // show spectrum file name private final JButton jbclose; // cancel button private final JButton jbfind; // create button private final JTextField jftfwidth; // jftf for polynom degree private final JTextField jtfthreshold; // jftf for height threshold private final JCheckBox inverseButton; private final JTextField xuncvalue; private final JTextField yuncvalue; DataFile dataf; // variables private int filterWidth; // filter width private float threshold; // height threshold private String nfile; // spectrum full file name private Point2D uncert; // X and Y uncertainties ///////////////////////////////////////////////////////////////////// /** * Construct a new JFCreatePeakFile. * */ public JFCreatePeakFile(DataFile dataf) { super("Peak File Creation"); // main window setIconImage(Toolkit.getDefaultToolkit().getImage(JFCreatePeakFile.class.getResource("/pixmaps/spview.png"))); addWindowListener(new WindowAdapter() { // clean end public void windowClosing(WindowEvent e) { dispose(); // free window } }); workd = System.getProperty("user.dir"); // working directory this.dataf = dataf; nfile = this.dataf == null ? "" : this.dataf.getname(); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setLocation(100, 100); // location setSize(405, 254); // size // panels getContentPane().setLayout(new BorderLayout()); JPanel jp = new JPanel(new BorderLayout()); jp.setBorder(null); getContentPane().add(jp, "Center"); // panels JPanel pouest = new JPanel(new GridLayout(6, 1, 5, 5)); JPanel pcentre = new JPanel(new GridLayout(6, 1, 5, 5)); JPanel psud = new JPanel(); // spectrum file jbfile = new JButton("Spectrum file..."); jbfile.setToolTipText("Select the spectrum file to process"); jbfile.addActionListener(this); jlnfile = new JLabel(nfile); // polynom degree // JLabel jldwidth = new JLabel("Filter Width:"); jftfwidth = new JTextField("7"); jftfwidth.setToolTipText("This is the filter width (in # of points) used for the convolution."); // height threshold // show height threshold JLabel jlthreshold = new JLabel("Height Threshold:"); jtfthreshold = new JTextField("0.0"); jtfthreshold.setToolTipText("This is the line height threshold above which lines are detected."); pouest.add(jbfile); pcentre.add(jlnfile); pouest.add(jldwidth); pcentre.add(jftfwidth); pouest.add(jlthreshold); pcentre.add(jtfthreshold); Box boxsud = Box.createHorizontalBox(); // add buttons to box jbfind = new JButton("Find Peaks"); boxsud.add(jbfind); jbfind.setToolTipText("Create peak file"); jbfind.addActionListener(this); boxsud.add(Box.createHorizontalStrut(15)); // south jbclose = new JButton("Close"); jbclose.setToolTipText("Cancel peak file creation"); jbclose.addActionListener(this); boxsud.add(jbclose); psud.add(boxsud); // insert panels jp.add(pouest, "West"); // Component verticalGlue = Box.createVerticalGlue(); pouest.add(verticalGlue); JLabel jtuncx = new JLabel("X uncertainty"); pouest.add(jtuncx); JLabel jtuncy = new JLabel("Y uncertainty"); pouest.add(jtuncy); jp.add(pcentre, "Center"); inverseButton = new JCheckBox("Down"); inverseButton.setToolTipText("Check this option when spectrum is bottom oriented."); pcentre.add(inverseButton); xuncvalue = new JTextField("0.001000"); xuncvalue.setToolTipText("x uncertainties are written in the peak file output."); pcentre.add(xuncvalue); xuncvalue.setColumns(10); yuncvalue = new JTextField("10.0"); yuncvalue.setToolTipText("y uncertainties are written in the peak file output."); pcentre.add(yuncvalue); yuncvalue.setColumns(10); jp.add(psud, "South"); } private static double getMinData(double[] data, int size) { double minData = Double.MAX_VALUE; for (int i = 0; i < size; i++) { minData = Math.min(minData, data[i]); } return minData; } private static double getMaxData(double[] data, int size) { double maxData = Double.MIN_VALUE; for (int i = 0; i < size; i++) { maxData = Math.max(maxData, data[i]); } return maxData; } private static String removeExtention(String filePath) { // These first few lines the same as Justin's File f = new File(filePath); // if it's a directory, don't remove the extention if (f.isDirectory()) return filePath; String name = f.getName(); // Now we know it's a file - don't need to do any special hidden // checking or contains() checking because of: final int lastPeriodPos = name.lastIndexOf('.'); if (lastPeriodPos <= 0) { // No period after first character - return name as it was passed in return filePath; } else { // Remove the last period and everything after it File renamed = new File(f.getParent(), name.substring(0, lastPeriodPos)); return renamed.getPath(); } } private boolean checkValue() { if (dataf == null) { return false; } filterWidth = Integer.parseInt(jftfwidth.getText()); if (filterWidth <= 0) { JOptionPane.showMessageDialog(null, "Filter width has to be > 3"); return false; } // height threshold try { threshold = Float.parseFloat(jtfthreshold.getText()); } catch (NumberFormatException nfe) { // format error JOptionPane.showMessageDialog(null, "threshold is not valid"); jtfthreshold.setText("0.0"); return false; } if (threshold < 0.0) { JOptionPane.showMessageDialog(null, "Threshold has to be >= 0.0"); jtfthreshold.setText("0.0"); return false; } float xUncert, yUncert; try { xUncert = Float.parseFloat(xuncvalue.getText()); } catch (NumberFormatException nfe) { // format error JOptionPane.showMessageDialog(null, "X uncertainty is not valid"); xuncvalue.setText("0.001000"); return false; } if (xUncert < 0.0) { JOptionPane.showMessageDialog(null, "X uncertainty has to be >= 0.0"); xuncvalue.setText("0.001000"); return false; } try { yUncert = Float.parseFloat(yuncvalue.getText()); } catch (NumberFormatException nfe) { // format error JOptionPane.showMessageDialog(null, "Y uncertainty is not valid"); yuncvalue.setText("10.0"); return false; } if (yUncert < 0.0) { JOptionPane.showMessageDialog(null, "Y uncertainty has to be >= 0.0"); yuncvalue.setText("10.0"); return false; } uncert = new Point2D.Float(xUncert, yUncert); return true; } private File pickAFileName(String nameSuggestion) { // This method creates an XML File to save the file(s) added in the project. // choose the project file JFileChooser jfcfile = new JFileChooser(nameSuggestion); // default choice directory FileNameExtensionFilter filter = new FileNameExtensionFilter("Peak list file (*.asg)", "asg"); // Only files // .spv jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only jfcfile.setDialogTitle("Save Peak List"); jfcfile.addChoosableFileFilter(filter); // add the filter created above jfcfile.setAcceptAllFileFilterUsed(false); // All types of file are NOT accepted jfcfile.setSelectedFile(new File(nameSuggestion)); if (jfcfile.showSaveDialog(this.getParent()) == JFileChooser.APPROVE_OPTION) { File selectedFile = jfcfile.getSelectedFile(); if (!selectedFile.getAbsolutePath().endsWith(".asg")) { selectedFile = new File(jfcfile.getSelectedFile() + ".asg"); } if (!selectedFile.exists() || (selectedFile.exists() && JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(this.getParent(), "File " + selectedFile.getAbsolutePath() + " already exists! Overwrite?", "Save", JOptionPane.YES_NO_OPTION))) { return selectedFile; } } return null; } private boolean savePeakList(ArrayList peakPositions) { String newFilename = removeExtention(this.nfile); File pickedFile = pickAFileName(newFilename); if (pickedFile != null) { FortranFormat.printAssignmentFormattedOutput(peakPositions, uncert, pickedFile); return true; } return false; } ///////////////////////////////////////////////////////////////////// /** * Process events. */ public void actionPerformed(ActionEvent evt) { // Spectrum file if (evt.getSource() == jbfile) { JFileChooser jfcfile = new JFileChooser(workd); // default directory jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only Container parent = jbfile.getParent(); int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select if (choice == JFileChooser.APPROVE_OPTION) { File f = jfcfile.getSelectedFile(); nfile = f.getAbsolutePath(); dataf = new DataFile(nfile); // full exp file name; dataf.setType("dataread"); dataf.read(); } else { nfile = ""; } jlnfile.setText(nfile); return; } // cancel if (evt.getSource() == jbclose) { dispose(); return; } // find peaks if (evt.getSource() == jbfind) { if (checkValue()) { int pt = inverseButton.isSelected() ? -1 : 1; if (inverseButton.isSelected()) { double yInv = getMaxData(this.dataf.getY(), this.dataf.getNbxy()) + getMinData(this.dataf.getY(), this.dataf.getNbxy()); threshold = (float) yInv - threshold; } ArrayList peakPositions = DigitalFilter.findPeaks(this.dataf.getX(), this.dataf.getY(), pt, threshold, filterWidth); if (peakPositions == null) { JOptionPane.showMessageDialog(null, "No peak detected!"); } else { JOptionPane.showMessageDialog(null, "Number of detected peaks: " + peakPositions.size()); if (peakPositions.size() > 0) { if (savePeakList(peakPositions)) { dispose(); } } } } } } } sources/org/spview/point/PredXPoint.java000644 001750 001750 00000003502 14205637625 021215 0ustar00cyrilcyril000000 000000 package org.spview.point; /** * This class allows sorting prediction file X data. */ public class PredXPoint implements Comparable { private final double x; // frequency private final double y; // intensity private final String jsyn; // assignment /** * Construct a new PredXPoint. * * @param cx frequency * @param cy intensity * @param cjsyn prediction assignment */ public PredXPoint(double cx, double cy, String cjsyn) { x = cx; // frequency y = cy; // intensity jsyn = cjsyn; // assignment } ///////////////////////////////////////////////////////////////////// /** * Get frequency. */ public double getX() { return x; } /** * Get intensity. */ public double getY() { return y; } /** * Get prediction assignment string. */ public String getJsyn() { return jsyn; } /** * Compare frequencies only. * * @param cpxpt the PredXPoint to compare to */ public int compareTo(Object cpxpt) { if( ! (cpxpt instanceof PredXPoint) ) { // not the right object throw new ClassCastException(); } double delta = ((PredXPoint)cpxpt).getX() - x; // compare frequencies if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } return 0; // equal } } resources/PanAff/jmidefexasg.html000644 001750 001750 00000000237 14000365526 017710 0ustar00cyrilcyril000000 000000

EXASG

Define the default EXASG string for new assignments from the list.

resources/000755 001750 001750 00000000000 14325453166 013407 5ustar00cyrilcyril000000 000000 sources/org/spview/000755 001750 001750 00000000000 14325453236 015162 5ustar00cyrilcyril000000 000000 sources/org/spview/gui/ThresholdDialog.java000644 001750 001750 00000001624 14046454633 021672 0ustar00cyrilcyril000000 000000 package org.spview.gui; import javax.swing.*; import java.util.regex.Pattern; public class ThresholdDialog { private static final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); public double getThreshold() { String result = (String) JOptionPane.showInputDialog( null, "Select the prediction threshold you want to apply.\nNegative value for no threshold.", "Prediction threshold", JOptionPane.PLAIN_MESSAGE, null, null, PanAff.getThreshold() ); if (isNumeric(result)) return Double.parseDouble(result); else { return -Double.MAX_VALUE; } } private static boolean isNumeric(String strNum) { if (strNum == null) { return false; } return pattern.matcher(strNum).matches(); } }sources/org/spview/filehandler/FileParser.java000644 001750 001750 00000015306 14046454633 022345 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.io.File; import java.io.IOException; import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class FileParser { private final ArrayList pathlist = new ArrayList<>(); private final ArrayList attrlist = new ArrayList<>(); private final ArrayList AttributeOfFilelist = new ArrayList<>(); private final ArrayList yReverseElement = new ArrayList<>(); private final ArrayList hideElement = new ArrayList<>(); private final ArrayList xShiftState = new ArrayList<>(); private final ArrayList yShiftState = new ArrayList<>(); private final String minX; private final String maxX; private final String minY; private final String maxY; private final String indexColorPred; private final String indexColorExp; public FileParser(String filename) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); File fileXML = new File(filename); Document xml = builder.parse(fileXML); Element spvFile = xml.getDocumentElement(); getAttributeList(spvFile, this.AttributeOfFilelist, "File", "AttributeOfFile"); getAttributePath(spvFile, this.pathlist, this.attrlist); getTagValueList(spvFile, this.yReverseElement, "yReverse"); getTagValueList(spvFile, this.hideElement, "hide"); this.indexColorPred = getTagValue(spvFile, "indexColorPred", "0"); this.indexColorExp = getTagValue(spvFile, "indexColorExp", "0"); getTagValueList(spvFile, this.xShiftState, "xShiftState"); getTagValueList(spvFile, this.yShiftState, "yShiftState"); this.minX = getTagValue(spvFile, "xmin", "0.0"); this.maxX = getTagValue(spvFile, "xmax", "0.0"); this.minY = getTagValue(spvFile, "ymin", "0.0"); this.maxY = getTagValue(spvFile, "ymax", "0.0"); } private static void getAttributeList(Node n, ArrayList array, String string, String attribute) { if (n instanceof Element) { Element element = (Element) n; if (n.getNodeName().equals(string)) { array.add(element.getAttribute(attribute)); } // The child nodes of the current node are handled. int nbChild = n.getChildNodes().getLength(); // A list of these child nodes is retrieved NodeList list = n.getChildNodes(); // The program checks into the list for (int i = 0; i < nbChild; i++) { Node n2 = list.item(i); // if the child node is an Element, it is handled if (n2 instanceof Element) { // Recursive use of the method to handle the node and its children getAttributeList(n2, array, string, attribute); } } } } private static void getTagValueList(Node n, ArrayList array, String string) { if (n instanceof Element) { Element element = (Element) n; if (n.getNodeName().equals(string)) { array.add(element.getTextContent()); } // The child nodes of the current node are handled. int nbChild = n.getChildNodes().getLength(); // A list of these child nodes is retrieved NodeList list = n.getChildNodes(); // The program checks into the list for (int i = 0; i < nbChild; i++) { Node n2 = list.item(i); // if the child node is an Element, it is handled if (n2 instanceof Element) { // Recursive use of the method to handle the node and its children getTagValueList(n2, array, string); } } } } private static String getTagValue(Element eElement, String sTag, String defaultVal) { if (eElement.getElementsByTagName(sTag).item(0) == null) { return defaultVal; } NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes(); Node nValue = nlList.item(0); return nValue.getNodeValue(); } private static void getAttributePath(Node n, ArrayList pathlist, ArrayList attrlist) { String type; if (n instanceof Element) { Element element = (Element) n; if (n.getNodeName().equals("path")) { pathlist.add(element); type = element.getAttribute("Type"); attrlist.add(type); } // The child nodes of the current node are handled. int nbChild = n.getChildNodes().getLength(); // A list of these child nodes is retrieved NodeList list = n.getChildNodes(); // The program checks into the list for (int i = 0; i < nbChild; i++) { Node n2 = list.item(i); // if the child node is an Element, it is handled if (n2 instanceof Element) { // Recursive use of the method to handle the node and its children getAttributePath(n2, pathlist, attrlist); } } } } /* getter and setter */ public ArrayList getAttributeOfFilelist() { return AttributeOfFilelist; } public ArrayList getPathlist() { return pathlist; } public ArrayList getAttrlist() { return attrlist; } public ArrayList getyReverseElementslist() { return yReverseElement; } public ArrayList getHideElementslist() { return hideElement; } public ArrayList getxShiftElementslist() { return xShiftState; } public ArrayList getyShiftElementslist() { return yShiftState; } public double getMinX() { return Double.parseDouble(this.minX); } public double getMinY() { return Double.parseDouble(this.minY); } public double getMaxX() { return Double.parseDouble(this.maxX); } public double getMaxY() { return Double.parseDouble(this.maxY); } public int getIndexColorPred() { return Integer.parseInt(indexColorPred); } public int getIndexColorExp() { return Integer.parseInt(indexColorExp); } }resources/pixmaps/bottom.png000644 001750 001750 00000006115 14000365526 017075 0ustar00cyrilcyril000000 000000 PNG  IHDR szzzTXtRaw profile type exifxڭWY& 98b ro7I*5IZ7s?#JE%Ք.brn?:"wQcg@Ώr<*w}_/jYWFܟgWw9϶7ޞc_ G3p^TKq!J 1ع K/?mH2FNd%dԍ_ApBkbMo5}h+c3/\\*w`iL eNXB:k{`.pPhu|Ewy t3 \ 퓿2Q8#B<3  ȄLT7 iS4@Vɱ G̉3,)8(!9KYJ(pI%RjJ5qM5Zj"P*Z`HZlR˭ڤ#|zSϽe?nQL?J3NiYfk+xWYuɝ+kY5e,ں` o[x-'1geMU|)gW%$X 8=wțxsJSs_y(,TLPذ`"ړ fid~%:u=+SM8f$|lL}〈8RyГ(T݈xK͖XKc7?$~=Lj>vumah m ,a<-| :?0,G fܼ<C5`8zHx_,ؤ_u8 #^k2|`\ %E}l3aǬ[ YRCN$ZvK=BR[I혇>'lԞE;XOl^Txe]!&pia/H"7<sdI:OҶ-fUf 3P]4h륐ҠL>R p@ܫU448)uFGr g@a,P8X<@>j9U1C *n,Ҵ99 gM(J"N?F9\9`3; mj%$J,{!Ci檅/A9d9;&^u*YZ$WNd آ$VnJNe6[GTS_óLlt+ILCUn4vm5ݏ)Ia=}ibMZ J$ʁM5Է4hFE d ؅am{~6!r[vGwSon 'gIcv},ZKBMR IĨӊ|| u{?5m5u(Ļk&ޏ0C|р.ڔ<5uGmuy) /ȭ+ٯ"wd`X:O99SÙ(#H2zR||HG1sӒ`/=@WE'x~Y&]Z~Ag6hd"oCOgӎ! h/C f}f<gIhNgc]4AŁ+oN;NsF,uyl-C*קѝmE[N68FV *j'<)ZsB'POVJ=5~a  hBxxbKGD pHYs.#.#x?vtIME &.U IDATXåWk\U}oBtH1 MF4i -"V nvJ)A\*B…@KDJ$ԏEB7v,35cw߽Ǜ$w=;Wa OZȫ"2)""R"?DWAԛ-j"rRDR)$33"riFwyϊ!)@?3{5ftDO|#"ceDTKDuu"b@6]bfykywBp?DQu\xD8FGGID3RL KXY`)}^D^p.۷o?+e(`)Oa̯ ߬䜃q9囁ZT*89p!"B`3"RH8bB)n`93#cdY6yw%-}j߾}#y/iJi "nDEYk93(8,"E1A\<'MH)uX+[( "%wލ 0ThR:rFc͞ [E&l⇹9D!1uֆ^ ƕRJ e713=%'upMQJr40hStƍW10`eej,W?ԙbf8FZcuujuKEPJdDDƘI IR IgQRb"jZ\o*NoFN% csw=WHvݿ7mj5QAP*7vѰR{}w&?+JG!Iر nkye{?SSSU܏i4Mesضm8Xkq F|/xj7Z-;:::EsQXGeZk "/..njιڽ)HZ2`" ԝ;w.=&''cf~{9w 7J'6Mԗgbbb/3S̼s=<o4A KIENDB`resources/JobPlay/jmiyreverse.html000644 001750 001750 00000000216 14000365526 020170 0ustar00cyrilcyril000000 000000

Y-reverse

Reverse the Y-axis direction for a data file.

resources/JobPlay/jmiloadhitras.html000644 001750 001750 00000000305 14000365526 020455 0ustar00cyrilcyril000000 000000

Load (as stick) prediction file

Load a prediction file in HITRAN format to plot it as sticks in the data area.

resources/pixmaps/up.png000644 001750 001750 00000006044 14000365526 016216 0ustar00cyrilcyril000000 000000 PNG  IHDR szzzTXtRaw profile type exifxڭW[#) {@<3boߔ{fc].J_0삤K>|Ͼ>|~`<~?yWB ^.N> ګgy,y(1GSHbJ)j9d1sɵphaRbIRJpZt3jm| MZlVZO]z^z<@8yQ'MHi)34,.hmʫzg}a{谦m v"@`<)4+gWXSή( aF R`IJ݃oys7ĜSRw7j;7 /46Lr'}l#h~ q\RC.lutVpx8IcaB_,æ!+s,{3#n)e@$pB ]U\AGh3X4LPx('MrՉ`t*5se8 u: C54YUT>د$zp/Usnhj ):A~eRĊxn#x1YcƘb ÐӟCb2`8Oʤ[pbzF!4g8H!뢞Jb Qګ*Ģ1K4-)Q Vs'&wmh864lz8#Q%= [5'TlvtgpQTj뽖Y† ^EkRYIDQIVj:ulA1̡e30l;6AӘStC66tåhY35/ih?e\>\@f"~̪W)/v dʅj4+Z-UKVʩj>n<1т -uORy}TZ0H#%B>B_4aW0c(8DxDxݐ0B# c .k?OgIk#nh/b֎6RXφ󞌯c]vKFGC#h޶${h@Q)P(Fa HVm Y;&Ddp ÿ0Ί(dG]Y o40C/T4GS^pq7i5gHvC؉T _ݎ>sHv&HwqkINvN_nzJ#-|wα^ ;kq^NnbUacH;i@Fp{Ċi CauC&rX`O; LGbKGD pHYs.#.#x?vtIME 'l i5IDATXíWAk\Uι73LHBa 1qQm ҅EMH KеEDH nB44#2)޻fǝyssw. N4h323zoXtЄfi|`ND^8vaf03RZCk}G)߭#:Kܴsy9D"ch(`1ݻwo @0"("{ J)039kmqT*sM)rz cǎU$"gB9cEZ \1).eJZw;LNNș0r$I >~K1v]T*(7oP!r\n~,=k->NSSJ=no<^#"_3 ˲qA_0 -"eDkW>|bQ1BYAk]'ι&_&f/^DB ZXc? ,,,`rbbgۍ"LLL`n~@\l`=YD ({:~8fggwZtѹxy4}A}-"^'}KDp6Ȓ۹ "PJbDQ, !CDy[򥡵FeC;Y7ឤ&T>Q!M DHӴЍ=)nFyj-tMg񈨰"7T*!MӂHӴ\)\^__w/YSa@x[k*$;3oԄD%-A$E3&#^Yf[[[2AD^ Ax+4b;Bf׻wND<|ІuQ"򺈐o4ѫ0z,}FD=5fJ)uғ(tF {-fѣ[#N{13G=~%E"NǻKIDQjDh։h~t:3"jBIENDB`sources/org/spview/gui/OpusDialog.java000644 001750 001750 00000003413 14211661567 020662 0ustar00cyrilcyril000000 000000 package org.spview.gui; import org.spview.filehandler.OpusFile; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class OpusDialog extends JFrame implements ActionListener { Object selected; public OpusDialog(String[] string, String initialSelectionValue) { JPanel panel = new JPanel(); JButton button = new JButton("Information"); button.addActionListener(this); setLayout(new BorderLayout()); panel.add(button); selected = JOptionPane.showInputDialog( null, panel, "OPUS File selector", JOptionPane.PLAIN_MESSAGE, null, string, initialSelectionValue ); } public Object getSelected() { return selected; } @Override public void actionPerformed(ActionEvent e) { String Title = "

File Information

"; String InsParam = OpusFile.InstrumentParameters(); String AcqParam = OpusFile.AcquisitionParameters(); String SampleParam = OpusFile.SampleOriginParameters(); JEditorPane editorPane = new JEditorPane( "text/html", Title + InsParam + AcqParam + SampleParam ); editorPane.setEditable(false); editorPane.setCaretPosition(0); // Put the editor pane in a scroll pane. JScrollPane scrollPane = new JScrollPane(editorPane); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scrollPane.setPreferredSize(new Dimension(800, 600)); JOptionPane.showMessageDialog(this, scrollPane, "Information", JOptionPane.PLAIN_MESSAGE, null); } }README.md000644 001750 001750 00000002133 14206515406 012645 0ustar00cyrilcyril000000 000000 SPVIEW ===== > Copyright © < 2015, Christian Wenger > Copyright © 2015-2021, Cyril Richard > <> Summary ------- SPVIEW is a multiplatform Java application that allows graphical assignment of high-resolution molecular spectra. It is possible to load, display and manipulate experimental and simulated spectra (XY ASCII or [OPUS](https://www.bruker.com/en/products-and-solutions/infrared-and-raman/opus-spectroscopy-software.html) formats) as well as stick spectra in various formats (including HITRAN format). Lines can be assigned graphically using the mouse. Assignments can also be modified or removed. Local simulations can be performed in order, for instance, to help assignment in partly resolved line clusters. SPVIEW is also able to produce peak lists from an experimental spectrum. Contributors are welcome. Programming language is java. Source download --------------- You can get SPVIEW source code from the release archives on the latest version from git: git clone https://gitlab.com/labicb/spview.git resources/JobPlay/jmiloadexpf.html000644 001750 001750 00000000272 14000365526 020130 0ustar00cyrilcyril000000 000000

Load peak/experiment file

Load the experimental peak list to plot it as ticks in the pred/exp area.

resources/pixmaps/backward.png000644 001750 001750 00000002623 14000365526 017347 0ustar00cyrilcyril000000 000000 PNG  IHDR szzZIDATXW[hTGfKݤRC(^i. Z/mOEP>I-A>OҒ&$!.q7Ls

Hide

Hide a data file.

resources/JobPlay/jmisaveproject.html000644 001750 001750 00000000354 14000365526 020654 0ustar00cyrilcyril000000 000000

Save Project

Create a new SVP project. This file contains all the file paths used in SPVIEW. It can be read while the files remain in the same place.

resources/PanAff/jmiexasg.html000644 001750 001750 00000000172 14000365526 017227 0ustar00cyrilcyril000000 000000

EXASG

Set EXASG string from a list.

sources/org/spview/filehandler/TDSFile.java000644 001750 001750 00000011245 14046454633 021541 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.awt.geom.Point2D; public class TDSFile { public static Point2D extractXY(String str) { double cx, cy; // TDS pred-as-stick // check line length if ((str.length() != 97) && // TDS with population (str.length() != 101) && // C3vsTDS with population (str.length() != 92)) { // D2hStark // length error return null; // skip it } // if (str.length() == 92) { // D2hStark if (str.charAt(18) != ' ' || str.charAt(28) != ' ' || str.charAt(32) != ' ' || str.charAt(36) != ' ' || str.charAt(40) != ' ' || str.charAt(44) != ' ' || str.charAt(48) != ' ' || str.charAt(52) != ' ' || str.charAt(56) != ' ' || str.charAt(60) != ' ' || str.charAt(76) != ' ' || str.charAt(80) != ' ' || str.charAt(84) != ' ' || str.charAt(88) != ' ') { // format error return null; // skip the line } try { // read data cx = Double.parseDouble(str.substring(0, 18)); // Double for X cy = Double.parseDouble(str.substring(19, 28)); // Double for Y Integer.parseInt(str.substring(29, 32).trim()); // validity test Integer.parseInt(str.substring(33, 36).trim()); // validity test Integer.parseInt(str.substring(37, 40).trim()); // validity test Integer.parseInt(str.substring(41, 44).trim()); // validity test Integer.parseInt(str.substring(45, 48).trim()); // validity test Integer.parseInt(str.substring(49, 52).trim()); // validity test Integer.parseInt(str.substring(53, 56).trim()); // validity test Integer.parseInt(str.substring(57, 60).trim()); // validity test Double.valueOf(str.substring(61, 76)); // validity test Integer.parseInt(str.substring(77, 80).trim()); // validity test Integer.parseInt(str.substring(81, 84).trim()); // validity test Integer.parseInt(str.substring(85, 88).trim()); // validity test Integer.parseInt(str.substring(89, 92).trim()); // validity test } catch (NumberFormatException e) { // format error return null; // skip the line } } else if (str.length() == 101) { // C3vsTDS if (!str.startsWith(" ", 24) || str.charAt(33) != ' ' || !str.startsWith("% ", 47) || str.charAt(54) != ' ' || !str.startsWith("% ", 68) || str.charAt(85) != ' ') { // format error return null; // skip the line } try { // read data cx = Double.parseDouble(str.substring(0, 15)); // Double for X cy = Double.parseDouble(str.substring(15, 24)); // Double for Y Double.valueOf(str.substring(28, 33).trim()); // validity test Integer.parseInt(str.substring(36, 40).trim()); // validity test Integer.parseInt(str.substring(40, 43).trim()); // validity test Integer.parseInt(str.substring(43, 47).trim()); // validity test Double.valueOf(str.substring(49, 54).trim()); // validity test Integer.parseInt(str.substring(58, 61).trim()); // validity test Integer.parseInt(str.substring(61, 64).trim()); // validity test Integer.parseInt(str.substring(64, 68).trim()); // validity test Double.valueOf(str.substring(70, 85)); // validity test Double.valueOf(str.substring(86, 101)); // validity test } catch (NumberFormatException e) { // format error return null; // skip the line } } else { // TDS (97) if (!str.startsWith(" ", 24) || str.charAt(31) != ' ' || !str.startsWith("% ", 45) || str.charAt(50) != ' ' || !str.startsWith("% ", 64) || str.charAt(81) != ' ') { // format error return null; // skip the line } try { // read data cx = Double.parseDouble(str.substring(0, 15)); // Double for X cy = Double.parseDouble(str.substring(15, 24)); // Double for Y Integer.parseInt(str.substring(28, 31).trim()); // validity test Integer.parseInt(str.substring(35, 38).trim()); // validity test Integer.parseInt(str.substring(38, 41).trim()); // validity test Integer.parseInt(str.substring(41, 45).trim()); // validity test Integer.parseInt(str.substring(47, 50).trim()); // validity test Integer.parseInt(str.substring(54, 57).trim()); // validity test Integer.parseInt(str.substring(57, 60).trim()); // validity test Integer.parseInt(str.substring(60, 64).trim()); // validity test Double.valueOf(str.substring(66, 81)); // validity test Double.valueOf(str.substring(82, 97)); // validity test } catch (NumberFormatException e) { // format error return null; // skip the line } } return (new Point2D.Double(cx, cy)); } public static String extractAssignment(String str) { String cjsyn; if (str.length() == 101) { // C3vsTDS cjsyn = str.substring(28, 40) + " " + str.substring(49, 61); } else { // TDS (97) cjsyn = str.substring(28, 38) + " " + str.substring(47, 57); } return cjsyn; } }resources/JobPlay/jmixshift.html000644 001750 001750 00000000325 14000365526 017632 0ustar00cyrilcyril000000 000000

X-shift

X-shift of a data file and the associated pred or exp ticks, if any. The shift area is defined through click and drag.

sources/000755 001750 001750 00000000000 14221341350 013042 5ustar00cyrilcyril000000 000000 resources/JFSelPred/jmilssetp.html000644 001750 001750 00000000214 14000365526 020052 0ustar00cyrilcyril000000 000000

Local simulation

Set parameters for local simulation.

resources/PanAff/jmizoompasy.html000644 001750 001750 00000000250 14000365526 017776 0ustar00cyrilcyril000000 000000

Scale

Pred-as-stick Y-axis zoom. The zoom area is defined through click and drag.

resources/JobPlay/jmiloadtdsas.html000644 001750 001750 00000000302 14000365526 020276 0ustar00cyrilcyril000000 000000

Load (as stick) prediction file

Load a prediction file in TDS format to plot it as sticks in the data area.

resources/JFSelPred/jmilshide.html000644 001750 001750 00000000234 14000365526 020012 0ustar00cyrilcyril000000 000000

Local simulation

Hide local simulation based on selected predictions.

resources/pixmaps/spview.icns000644 001750 001750 00000336274 14211661562 017275 0ustar00cyrilcyril000000 000000 icnsis32d[# 7//215" 41B'&0bQ[s\J/]nBCnKSX|b6f"}yzEaImQm+SE\ W     bQZwNdqEEqNSX{d1xyEaI!Tm+SE\N     bQZxLfqFFrNSX{d0 xyEaITm+SE\s8mk77;;;;77il32X?nvttssrrqppw@&yL !#!!."PQB)>!f K,awjJ* &3MSzG)%D ;%>k !!"  !6 965659 <;,1 (7 ! 1J;1 7VQ&~"!@+1V㊓892M􌕙fh+*<+SX*ijCKZiX9$ ON[]hJ)Xl/,H286Wr0P$#0 T Η>* &341SzG  G/D ;%>A! !!" %'$%%$' )(!%1J;!%VQ&~"!@+1V㊓9' "M􌕙fh+**SX*ijCKZiX'ON[]hJ)Xl H286Wr0P T Η>* &3#!SzG 0 D ;%>, !!" h8mk &dɝd%pp|{++VUononXV/.}{"!qp('ecǝec('qo"!|{/.WVomnmUT+*{zpo%dɝd%it322,3 :TTSSQQPOOMMLLJIIHHFFEDDBBA@@>>==;;:9977655443246549 И/b_gʁ̀׀؁U#R\m     s~pz9C1(pA(([A|Q c[)( _wӖtх"ơQx<' Ԇ +Z[X d I# X01 7ACmޙuyL6R / 1hoP' k4t /;+̔]DŽJU /̀_<0 U$ue /=kmqvJ"B /?D? jƄVf,$ /km|^# ^K! ##"#1EM /&!#"$<,V̘ ¤N ((p /=J<Y D /B:kn Mt#ﻃ< fg /򽃼/Asf? YƇ C /ln0K v.$ />T̕#a ȍ UM /-!4* ȍ 4 m /{r}n ȍ * /:?YN "]4ȏXa /UQSRTn]]Ď : . 6@)̙T;Ɏ  27 KDz= yeb Yvn}+Y|=(.n> FJ@/@JNMMNMLKJJKJP:IMNH +MME<<=>?@ABCDBAG :KL"MI,2LD EL+@N#'N>'O7=N#&3LBIG "19 GG N<;BL&P2>9O1P(=)(0P);O%A.2tх ơQ%P3CK ' Ԇ +Z[Xd I N=IF 1 7ACmޙuyLJDN> / 1hoP' kEJ$P5 /;+̔]DŽJ=N/P* /̀_<0 U$4P$9O /=kmqvJ"*P/BL /?D? jƄ VO8 HG /km|^# ^K! ##"#1LAM? /&!#"$<,V̘ ¤N  HG "P6 /=J<YAL.P, /B:kn Mt#ﻃ< 9O8O  /򽃼/Asf? YƇ/P*AL /ln0K $P5HG />T̕#a ȍ N>M? /-!4* ȏJF"P5 /{r}n ȏDK ,Q* /:?YN "]4ȏ=N6P /UQSRTn]]Đ4O+?L . 6@)̙T;Ɏ*M@FE 27 KDz= yeLFJ; Yvn}+Y|= HEL+1PH"    /2, +2543232326'145144/)*+,-.-,0'3342"3./3+55*6%)5" 3- 10! '005)(,3 6"*'6!6))( 7(5, "tхơQ6"-3 ' Ԇ +Z[Xd I 5) 2/ 1 7ACmޙuyL  2.5* / 1hoP'  k.2 6$ /;+̔]DŽ J*5 7 /̀_<0 U $#6'6 /=kmqvJ "7 ,3 /?D? jƄ V6& 10 /km|^# ^K! ##"#14,4+ /&!#"$<,V̘ ¤N 006$ /=J<Y,47 /B:kn Mt#ﻃ<&6&6 /򽃼/Asf? YƇ 7,4 /ln0K6$ 10 />T̕#a ȏ5*4* /-!4* ȏ 2/7$ /{r}n ȏ.37 /:?YN "]4ȏ)5$6 /UQSRTn]]Đ#5*3 . 6@)̙T;ɐ4+0/ 27 KDz= ye302( Yvn}+Y|=1/4!61  t8mk@/Sx̳wS.$Y⾎X$KҒJ NL +)FEQPHF-+ EC -+_[ !#!! `]1. JF 2/LHUQJG/, PLNJ'$ZW 0-UQyuɵyuTP0- ZW'$MIPL /,JGTPKH1. IF 0-_\  #!  ]Z,* DB ,*GEPNED*߈( LK JёI #WὍV# .Rv˲vQ- ic08g! jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2d#Creator: JasPer Version 1.900.1R \@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP e߁Mβ̺䠙'*53'/@"g_(m@wSrurd`l ~߁N@VK^3 \wEA6f^n Nj$q{ha?߁N@lRD/pO KF0Bkh][mz7Y*0O߂8.nFeA bS{j렦 9N[7^IӚGB% >8 PgAT D1}M4׃m9Z vzE$q)8w+Nx H+6C% [#ktyo@r`B~ڞ> PuVA\$:9)! qG$>z=[CW4@ؿ2@pc4dtb.4׭nA01cwv~Il1A&$3|)> PuVX! W+33A !Dx4@STLئALj*A01cwvW՞ xΉ0~q6ӉϜ <,DoI»8@v ж\i8ؒf";,LlJbi"Cx_XG3$Rdx4UpdY]Aʿ AY<'m5{|~ ąX$ PG` ?tߛ%O͌>f =U`'bvj{ػ"Wo*Hd_h*$7$s}XU.g@=kF-g]Kl5 π8FŞ2u+rT73{Qq@w=5TbJ>UhBm3C/$i:jX7Rwڹ[?©X|⑌=ē PQ_4zaBd[SߚOp+3e{oNzER󻦨uXJ"[X}m 7 IKJ_M3фe2znxީh*& eE|u!j^@l.iͬ{4_c"3)lĉ4*t%\_pCd88tibJ6.U#06:lu]`OvFBK~E"rA(*l8!X}tSߚh+3矣=]6;!P>^C[AU#ʧMB5"Y ڡM,3T[pHL/@)1~ h*& eE|u!l$]v#}Ɯ}^cĥ$Ѵq(ѱ3oL*=TjȾ<0W. bJ6.U#0@d[x<vjQ_J455X(shV=Drݝ.8Hl7ߛ̊81F12"/Rg;&zǟG+z+{vM12R,iQNnIF.6X2ɣ T k}PH e~nT]]30-R7pܻގ*-9 ަm@zƐ ٦F^t!>SmP#0T6V2+df]^_mghiuwMϦ'sFmaƽn+c4 ɳLMOHqB|S?3h\I}&" (.U&>qػd#\b#O3ߞ_G}뛔#@i1#:'EOĝ+6~ܴq4!`iYv62\n/g_r3)8BK3*=G+RI[ݕ0J!u-v]7pz4~ QF۝].*C8@ivA:W*&|5lJ%^=oS#Tkc|9Ir^\I!;J9 Sy:9)>"Z~yuۻI8NC]㧿EkJpqPE [d SYڱ JM`zжc*x<鶠JYlC~(LN[+kz%C`$9>oKYFKA qiy޾ Ck_e{dsW.f醘 ?=luJl:O }8!K[fY~0m,vT;]uud4Hv*%Qo*/MoѾ:ҥhEno>GT2>B T% ]EIc4\"qu*\\̺RPnB Aʺ-dla7fGi4ߧSL1mn}lP0|n2H8|_^ }_96:qL` iޫObJ1׆P#W'ͥ`ZovQFtb/-ŬWA[Gpx;5x$4ECg VlN 52 Fq8 mx";ٻFji o:J`\zk9P 61eg /َ(o`Tho+Y_h˛-Y,/ܗGsZCb$ v󍨢NMcGГv_0ij?)7)#wz]na.Jw64Q nzYdW/ˋXܔ/ao ФQ{pdQT|e7+o̪z([!XLyWߝ~w~z^$f{a ?JCf C\4^=N6t*@ +ڱi4,)=O9-x/ow_8wml#{'aHenn0u]M1qseloT!y*uuhԽ D˸"NS2g& 6 󡗑?)㕗]nx65f0(z)^thI!Rȣ=[Z Da{$ N'lj!#RPnB Aʺ-dla7fGѷ `909N}o{w~kd/p-?oyEE1ck. y<vL?<3 ?㻿SҰ3_D Si+G8@'b[_#RCmBs#G}nJ%s N9CUKR@|J$ewQ,wP>*関J`\zk9ުs<ב2 Dai(TCwE6-=CB=,)yy.Z%_kF Od0]X&|~=5Yi|J7!;!aDڿ;[wjk+;{fUBt)jԩjy=AГMaͳNƌG\l(gհ@vawߝ~w~4yJ@lлi3 \$kAAJsA2U$hoYO$CJ@tycuB ™J {,cJ֫D19}%o\(  =@ ii hkn/qg*tq3Dhd:%D veFاWDTADVPjzB.QqEsǹ|\"G:kIf خ21)B@g iYS /(OTݶ g1qp_$y;_dB }=Կ{ȹ"y]NۖP1wZDeSC^94NT}<g !?43&].󟱈&Wur,7SbV+l"mV3kz5 JeM03^`'K![vH^2O >@{Wz6#Vw~fʊ//'>ΌYhX2x])_' IN } /2F͆\Vz? >)m‡HLk[ x& ^xVo &Q-H!S- Sy.6dBS\@ j Qo\ 2u#틽\cv"MyŲQ!+Içt?>;PK^ג(Qm9zx eNM6_boA*ZGB}_&}}9PncR2E@C sfhv%4/NUtG°/"m<)dXo?c*,w(CcȟЖB OD8fh;͍n5bWg*mhW&" "H~͉8\T;e4e|S[$^a'\iF;q yuԦ<2)Ƭ%7s2V~ 3zXASB}]1:dTe?JGlQ.8|SuBL:[^ծ K0^0(vTKHPd3.p)"1n3*j#F4(n ,#A-7 @wesw fNΑ mF3+:\ݏKlջ Cv9 :}V}HD.M2tW^s}`ᔟPX0lNJ}_ck#'{Rɔ_O⤶]j.KQD=oTFr@9M&ڝxp8z-HYY<=(Z4}a]h!hr<ïOA@K,>/2d̷9'8 9w6$Bf5J_l!rmnxD;%d+ įD#S)mڹF?70#"6X|E'ˏi(ZU|_ 1`.pp@yj톖I G~-TOr\5by>tYc4  8C:&\l_գ2L7Mta_#Vů[8WgC_8bqH [ﴠ&#*u\WkF+LEPfJ *.eՖ8|puW0)?Z9UTt?ޅ v .=X׭Uh$9KiѾ~a"wև> ;.x}g2jed#5]sg)\*tLI /I?sP;oC'6hv E˛c;̧n(b9+sv, ^ i2ɨ$淼_shtF=õZxa3X#dk/x< @tB0gtCgp.hnF۷Ox_k4b/̰+A5~'%>EԠ;M_k% !K\) 3(4jty}DJM4-IZ3S0}VxźId6`hM[ =vPI*3NS TdD{d# +@X ätkFS0WAzՕzÈ8*y~VU{(CgOo=}k\C簌h `^+̢$T9</ \{Jѫ[_UC':`EitCuӣbsk<.:4:1nw j<secW+V&Mh% 2X9eӻ bꐏH)ۄbKVѐ$!mW6E3wGjƧF]&85-aMI^E!vTk?sqIG!X`!dxc\S?bnm;0n Y"^3J qdp5;啨-bE^yZYy2ۊn%Y0y;=4|m*ᦰ Ђ _7ms 9  nor0/ 5HqǢk elr~*G'4-0 ɛzW:)={bZ}ʿ7~H2ނq]lŇbFUx܂q >T:阠)N<\) zx[R ܛaљdW+92li=wGPTݖq̀\e G9ȑP?5ʌM,?"u Z ?gd ^8} 6u/ʾlŠdj{ξۥDӚoJ 4\N١$j>SW3tO$“ ΂{i.?oiZDnd"xQt|;Fja2Ə< 6} tM' W$Ŀ 0bm,q"!"TecGEtAe^-Ykީ͉Q&{K ڛ>AԤzb#ѷkޚGȌЀ6Q 2S d8/5T*5*HEšW[b|D6V4(3ѭJax lz|;F SƼGY<)˦Gyo&S֔%< lӇmT4 @!}|/d:|ԸQ[p oNY+"f@ãtCFS0WAzՕzÈ8*y~VU{;{7ь#GYT\ ! TFW*4 ni[r c7 y)"gҼnr)|4(~`妡 %d !";Ľ:,Y &*ױ?7*'&LzQ? H![l8TGV~g+EYl1`Rm‰R/;DDHkm:H|(Ͷ/v.<9CT>lO>k=:!(1ev?#ܫ}А [)OOtC( mVr0F0{ 8CHvس>DK:=[>aΒB?#:D q!8p^32`%c3wGjƧF]&85-aMI^E!vTk?sqIG!X`!dxc\S?bnoCbpphڥVXQU NYf\7M/a."amV4d}emئm)9,TbRn~䉻4l䋓"dݕR3a2ufڅW6Dtv¡K$%0Jrye&OA*&aYhP.G}!72@h{W7@(t7%L"qdQygӐY\,uBr~@uR Ä;ԌW\JVvI*1ò 1ARr}BkU'O]T΀ ߌ'y.hť]JG7c3S@ ctM.ٶ5Fݭ)2#'!}2A@}ī֭Il'6CRG)&4Ҙ];X-E* S( RɃ,*ӆmP1ՒauJn- 8C[//}^܉c7[ך>UK~qʊ>?ߞߞ (&CsQldO.ؽA$c±Lxx.%vʤNmpIƃ඗a!rOEqaƕ^Y$ .JkvyAA[KtdI:rxT/6 p=ioR Qͯ dFEᘾv^ƮD4*S$o{{e2$1ME%>6qs#Gu+ly&pmQTk7űwz%`k.zWzU-ݲ\iTZ˹|"HK%yg<5WB urZ|'3k(?Q|kk]\+Ϊ mrsÏdyn: w) [& ׾~.Zm^672n#g7<]Z`W}^7u~ _jY(Gpѱh1<&nZfm=get9}2#P$ɶI}2n;;sqj߆Xge!/aeث6zW-}< ~Avf6u9T{bJT}IxenbvcJȟO)=IK/Euz$M-P*Q$ќbgp{aΒ(dVb챋3T"N2.JZOJ:n\6_Wۡ;'.ƼPͧFhD2{qf:S R$b)#XO wl3-]MͱQjq4F]B+b:/\ ~ B ӟJ T?i3qsE4j΅ӭ?Fvr~] ꪀngsXY!bg]O*}a:C_m5gftY5*/G?1u E2󶨠A8(86?Za$sl'-/6u?[bp⷟'n b"!]]DʂꄢG\A^ >q삁̛7vkվ33zaQi5Ǯ/ҬKgwT+2d%Y47|F(Ryʔ7vR֍9P~Kzvm!s|LJfg"t9CuK7 u_^-_诒 zJsK߸6p/ox o565luc1l oosgƲۑ6|}1TAKmX=f/>"u; et>mdQ%3np xjڻR}}'/۠߷OW|omʙ%EXE5D}ka'Me\-\ o,`-TiV]A#bq((b7w}It}x̧]4ґ7gJ, yU褽^Ҡ6́ږ9]J[w-<]UL_kӘ-8H {4NJ3bZ@%T)CcqI;.:ыKKhR:srMϝ4Mh :?aD~O/Hkj oK~j:/i\2?[KO/|k9v#xٵņ=5+c.bPq4SA8`+@9',10\,$'A.\EKsn\991mL[ŮQ\9(m$^LƝͯN#USvQH}si_]y5bEY2p,5Y.*5[Qam^ndOUf=Xr~دmjn]΄/:|@%mO)̽*D }8z! h۟%r;sW[mZTkML?Hqϗ0i*=T=CXq< T?8J1'@#‘F+8*1@{IIh,eWv<;-}-kv s MmZ<g sbuDT OVÍI۠HN߰:\Qcs~2jNbV$ ֯{rEF9⹈ZY esL]?ՊOMZ}(TuQ`>HëbZ7Yɇ9W14>8ނamJM$ifP4fP2_?4j fE ntLZEbr+lҁ_9㠡LAa Iu<ޘ{3ߙ?_K*kZ4AEZ1q;p|ќGyII8_/-\$UzK& {IJZn *,b^xōHF(3/IR M<W[I!=r"BUۉ?܃kԋ](o>jlp^ΠcG}O gX3cK^0xC7|rR A(Q]zŅ:P4_x흈؟F4(ajL/˧PM\.bScvUfer,ygaܝk moiTBe0\yůu4ƯrBu?PSoT1~߶-rNly1wݖxTC lF?e58PG;"=׸q2--$: G9s7Ͻ#=Szm1FS?^ܱq1Ī`b߈~NM̸&ЇMyѢF/0jx8uq8vz9HgevMHOd*}XSuWIFa;j=Z>N4X1܂Hz=e:f(Bz`\ѡ+d _wY5L/`n%F[REAW/U]tfxHx~Sud"!ϓ!# KT%<$"eBhNA4fk8I1)m^4@4fNی/DA1y r(#'Pr ͉&&&.S 6$yZU)w(Ce.JU|a Ej.CΏ&ZAVf;FVACTo `@/vd4;z:6 <(m[P Nn+i}å(] @r_A`Y%8'6(M%sf%dhĝ̍\-W1lfnR:)ȷ)aW i<9t t %.C1̙qMn3(W$ҚIRD?Livye9MN*[8 @/ĪD-ʗC$-5 M 1ktظVJh1s2!6[S vͯ~FI{+@ig7֘YWv?!@+ij_%vZ3 cebeIWQ4] 8ҏ۰ct{ۢx~\%˂W  TsIT=?B:R~E)j ŀJgm.zCwS &.Ņ;SH /;Uid^ZIQw%B]oL1 0tSjVZ\oAYa|*D%,LnA}HEF!Y:eT'15# xgC4&"u;ڂ~Z?&°QtEx|1[ogM ֗ռz7s!s!]9$Υ!ƒ]i}d?~,|\rp9 EliѥK+!nJ) ɱ|M~`(W_aC:GZuOvo!AwXͨwl,bWgtg}r=nmtU I>;5>%A17hĎqPCk07Xc1 N8 m 5gepc{lkC7v6@L`6a̜yzsGK i)a6"TD_h0 kCLS'7&DP_Y ^He&.Qȿ c|*Ԙ͢#5?;j.r~mF#9<4g k9ތl#52aVb&,fa})Ѣ[};7KǸdc$ĉNԇH<70 ,2PHCud&z/T|%r6q)%ܑtCD#X5ENQv:|8/Z{i&7-%0r(^iU2ŽbƗőFSH>B6:Uag6.PW2uDgU|)qvY)h:z6BwH yƩϑ`6 9ODlq~E_:%ȄYL~)s ebeun=s˷Xz0n;qWʫ;[n29VՎ@{IIh,eWv<;-}-kv s MmZ<g sbuDT OVÍI۠HN߰:\Qcs~2jNbV$ ֯{rEF9⹈ZY esL]?ՊOMZ}(TuQ`>HëbZ7Yɇ9W14>8ނamJM$ifP4fP2_?4j fE ntLZEbr+lҁ_9㠡LAa Iu<ޘ{3ߙ?oF;6H9!y:EN|IaӸ JȝNo5Ln ,Xy W|ϤGK93 M-c7FB~@+B'+T>[&-x|6։{f JUIA.٧FUQFK2V֨[SdFGJcD[V oB\q< U[V@sFFH l,^ ~<:MЍH`P"~NX:QW`*BS=PfʣX-aU##Vk-"/y 4ě"yRjc'' 7U;Q21n&EEnV&V b~F}xN1ʒ)ӣPu.{u'|g z5MPc2˪@ +WnYoRwٲ#6b+ N*[8 @/ĪD-ʗC$-5 M 1ktظVJh1s2!6[S vͯ~FI{+@ig7֘YWv?!@+ij_%vZ3 cebeIWQ4] 8ۯct[_ۢx~_\%˂W  TsIT=?B:R~E)j ńb{eC_V_m2@_%CC1Mȉ STi"jY-x$䂽N ȘU [@4( -+X`s&¦JmĩS|&^1P/+oV ;oyE}e mUqa)u/e&"Al:.jbΛ~A@ z`U@!m\k<hCK}} %:/j~,|\rp9 EliѥK+!nJ) ɱ|M~`(W_aC;j˘7(@|F\*·6I뽙R~2Eun||@ܗߓňP7˄#7)dD)/e*G_ (aiEy8\Y$# fNo[ 9Tzn ktM%Җa6Ct;j.r~mF#9<4g k9ތl#52aVb&,f>x&}2(Z_:*9p'Ş"mrOzw yMOg ,[ img?9ϫ{j9m0#~n̕ȿ`PDe?wc9Wlae+ofs?y5pǂ'Վ@{IIh,eWv<;-}-kv s MmZ<g sbuDT OVÍI۠HN߰:\Qcs~2jNbV$ ֯{rEF9⹈ZY esL]?ՊOMZ}(TuQ`>HëbZ7Yɇ9W14>8ނamJM$ifP4fP2_?4j fE ntLZEbr+lҁ_9㠡LAa Iu<ޘ{3ߙ?oF;6H9!y:EN|IaӸ JȝNo5< \$ ޺abXZ:/JkgPiMN@'J'|~F(u6}o#֢2SnhAt 鈠 r$6p NGhfc 2XI1|A+pфj 0P,]%͔ {tfCWC$W Dq&܏#pNIdd*A.٧FUQFK2V֨<+`>{Sȯ0 !-EHLg&ok RyA- pwG_7OєPӒpĦq4{:iK3P0gy8 /nH.k6nl F떒u&5tLb}krgNܛ:g0Ħ^Z@] r{kM}~@x2DWV4g-sa4Hh;%޹L=JHRDh ڿZ2R6 UT%M`Jj儨?ޛ}&N;y?ޱޞ'EDgTvrq=yZCK;m+* C~F crHV3|6C#ZgO9=* n+i[bf,r3?m4*(~خ9 te=5c?Tshm쮣,z[aC9-u1e F[9r.H a5L_}t lZָA`|nhb? J6tEDW4+cS"#5y8r*|)G_"Kipܹ*U㔾蔏Mc; }\%2A ޸ rVdM]-aW!aoQxuT }Dxx@B3b L6Y45)K"d=ۮcxȂ:6O;)^ Dojpu%ln{Al|L*z:|~'-YĥqXz7f70ɏ97HcT` zZw(67\5mM!|k ‚*j_=;(qX0/ǘTf:f4,O'#751l<= 'F0Gݎ *n~;D1n^Q_1,LN2|- M𢚞_Ũ~.'İ6!u)|lik[˂Nޘ=6VLJdk1<=So{ٷ,?Y1zӕg N&2:RC/03^-ycinc>b.#d䁥l8.GrwQE.6(α `SL._M=2LP5 ?tJagCӞԐ9> ZURU0+sayiϔ (W8V;'0x6ǜθUl_ 7' %f4ijD~EA'M 'Y{i a&&3w"GFٵF{vxB8 K@NS)w߄y< Tl=HCeOd)fnQA;u!Sr#vP;n1ŁikDO2^-%nq ar!) Cb%$i`Nj$/Hof^U,q~͋@bمS"s>]H{.vﰣJ~Ε iҚHR4V),?Livye9MN*[8 @/ĪD-ʗC$-5 M 1ktظVJh1s2!6[S vͯ~FI{+@ig7֘YWv?!@+ij_%vZ3 cebeIWQ4] 8w+e}o~ t#Xu۟mѨtQb=:r=:K~PV,l:!k[[CH5"w7'yKV`)i>^AH󮽄66k&L X/c6aM v3l1dË Uz ץ'6|̊ 'QG >!xgi =V{$^$r ⰾTm lz61Vk fL 2L_>tn6 x"w#.A=WWsq=yKxBE iHW ~XRG.})Hء4hGGV_Sh[k6hSY!ekM\0rN֧ra~ Q*: Ịq?mCnM&WG2;wԪ1&[i&8 } ̀W52? ew!RE4o40`{ns\A`߱kA &j@ѻ0gnTX&K_^AH, X#jw yBpˀ'n =RpڔQ[Fd%>+y>nI]BicI!YVJ ǽZόt\[+dq=lE XܴI&(ܑ q 蘻N` WA)V6sV<~-Ձzqxm4ؾ|9p A.s(d 0H*o .9EYPV baq8 ]JzAS^gZmossS1*oMV~bAEk(eܤ{4>ŗ.Ӥ}ueK__4˓b:&5r Nt,xfd*S5Yʜ)* hZUC@#,Z1P^-q;* ([ rvnQMdO %g/In?4,y<;}g M V{6@P3]$A L/oʿC@{q<"R~av!)FC^Q=]r-Tb;GWLh"Vݣ-; :_٣2cێ{,^5Sf!-@:J52Hi]xݱ[nk+n %mEm4:y//}r)toXr*80ی*ea-k1AV.!@ rUWЕ<ø.]!E802h}T|,H=W,%lW2%#\vr>6Ѽ1MȒm1'3"jdUtez8io߅"PT~̩wi[pf y^P\1]j_1X}DM[`U HXW(i '1|?)ޘ?O ulW^_h1HΌ`1IzH(Bgt, 35!0߅nQ]rGj6ƞK@[m9As,*.\+3Ŷ;}ދt)7le+ ]^P$=-Cyu/ h!(g/}UؑimU=$2"6oH`朷s_`̈ɑ-Ɖ?\ߛ5O͔hDN!\=oLpg;؊ ^«vy%F)F}^'lcKqX Sm4vd % ԒAVYCOٽͳL?ho ș |ZXț"kòԅu.]W|fЫhW;a`k>GU6ک6nFPT1e,kQW&߈bJ ;WfJaFM-;*è2k<v#_&Kd0 K{L{AFngpE~jHmġlNe#ߚOxi^C:sQST 8:Ore|.6&^Zu3r^Rmbm^.1>RFfQxzSI?ho ȣ((Y̫`? 5nQ#yC@XrAU@߷\*-_57dlz͌w]$f!>9x5/k+YbJ6.>VKyF M]r~ @\ϫ8g*F)[Y<_LA j.P,]GV_ߚOli^C:sQRn]:SR=S9Wa)[1QbAύ3o2٥O\Zea)62e~, :ho ȣ((Y̫^yĝnH|kp >g~M_FV ML P\G=u%N2bZȩ$қR"ye}Ɏn bv?]'%*?fFퟢ30-R7pݕ^!6U[Ķgo1&K5X*ћLYW͚M,3ӳM鵁ITjt~w"g| ,E6^δR wMϦ'sFmaļj#fኋwTTi mPR~MXϼ&f?p䉵jiz UƖkPr]PjvJAC/ߞߞo?8XIޛD* dP xC uY ITQ - dMY[al+*_<2(q޳*UyVJbn^ =DҾ$&^m2T*;(Gl,/?) dղP/{Loj'9Ľ!J['+.3U+ŧ[C*4(BHrDjT'}gN܃NMXR &,!E<=->{8L2-kҝfZU W11~oDž'lxZivJJrh1y](.HHܔx6qE"(8haߞ_y1>ގJLj&#gi8y/dnȜ/tS<+-(ܼ p-`҇zv(j^}YO(__X2;c/_ >Q[LȣERJӽCXUZ Tu=w.-l`wXhLZ\VR51=GR˙e l U]Jc1%OEHX !,0`аY/? Hw-=`evr7 |7@(C6r'9uZҋUbtL\Tg4#\[8Eg fvbpClB)CE7n'뀛$ʻwS +|*ꢃ7[aoЧ퉝ZJf Rh/UsdtjBxu_aC/,n. L=I +kSKxgŒvmC[dڔvWDbydګ46+2^8Rs/\x)_Ghʠ͈AJ D&7M*4%&ƼK ~er_ i("8jygV@*0N##8͊źY+Ke+`pk1\]„b9-`xy+XP@7|$+0 ZԾh_ ZQ|A?!:IhO(8lp{L ߝ~w~0y1>ގJLj&#gi8y/dnȜ0@ 6mߛ'wQElѐHS<)TٯT#?[V/gb>sL lfpHt6iF0|d{h'vr7 |7@(C6r+ɇmP;\[$l6F gfEdf$ZqB% lJ8/o}.>6l"ϵ=F1# i*S*;r@Lw'x4PZCeFCf̵6F/ۘtŒvmC[dڙF RE,?È!?겼VK qєD H{4}]F6oco+!+q970+zھ MKҀ[ >s&C+ 6k+X}1|fielY$QZѿ^dFD82{J[O kkK,B}<@+oѷ/5ZǤ CKߝ~w4yA*i O=K~6riB˨Ac Je-12eԘQQV܉59(dX;9uc6zJ5 iD Sױ.ΆTr06jc\QPQ;1:Y7ț~C]bL93.'¹Ox{t^Y VYmS3FbA2?/xSQt Q6ao+y"M?'*gǦOίj@K 4o?ѻz9mהY3W}W8Hj?6oROn!BBR?cU-SUnd)#èt?>s/d;IE0pņ4spɑ>EHX\5tEKM!80 ?2&O!##GƱGV8BmȪ1|\rрLmѱ@ Zr% Z@RrdǸc{J ^/{ܔ=#<5g}ڃSOWpnBEݛU IPխI{(p*SP;2TWw ag"z398ꔶYW܂}\JI}S ghhU!Z͖i¦OwCMiMKN'}d}vkv[ &n R+Nq7و\pѪ '$ o }pQK>l":bC鼗X+:HCz#2ozAY!J#mqiFaD$W ~ xޔMxOSjY%< h=Zl Cʸ+AbMCQ=.pSg}^"7塑{XnGS,Z j-ÃL9Ɩ+W^G(aD*2JwmO7gȲQ*+X0_(ZN,s%?N7@hwW= m *дI**1ܝ=E7ϹN D L (!*j *AiյQ/uY&GBrDz.7/E+ndaZᠦ= $#\I_SpW砑*qM(`Y+׭nJJA@ep!rIALuq,PNl@(vPY:mQ 2pX_Zo辭]V<-."RrDTp-)2]MC#KbE]SjdFFpwIaUGA3!tbP׊^T m(ӻﴜ?#7uP!IPlSZdyCɀqIqcMli/w@`y9ɨCaz w),Fnٛ?4sU90oƠc|3ܣlkI + &a /L)Czʾgj:f}o #~,Nf{jʐH>R-hֱPcæ9tB+ Q9ȥ=cSУJ7Ś('ďtk46$:) m>Qr=9N}o6/ibC5{YE4﷤7d_T[\J Sb4ZNu^} e4~IϞmo[n_><"J9(pc3F+J̵#⥔۰Xuj=ya3O{f|0gS.cĢ}IMxPS_X\sKAЕfb@˷Юf@]aYEǯуd'W qoOu:p{;&1N[3(]I I+)9n,gșr^+@i8p=9:2sWNo˦3]FcPݜZhINoˏAqW&is?g*LTӏJK |^QDIWR|wK{5Ka]] wcK^)Q5IէlfealH9ng)jgY|ⴄ(W/:yxAHaOJm:tl##.̟=[:360rB'q^Xoo€: ɭ͡LWA q ;xnSOǹDj[џ"ͳD9Z(h' HN (j aixea5kx փOd<3MX^b@D˃7d[%rdd&A+:rcEDǎ̵K;aP4v7=_X0{TOblP{4f"3Vrq-7֬~b+ ]^c=vCəRE&Ǜ-%u2o4TL(j&åtoA`+ Q9ȥ=cSУJ7Ś('ďtk46$:) m>Qr=9N}o6/ibC5뻂OÏJu˳_Raor叆>'ךU<:H35XxނߚP< k Z2I_Ϭ.`,8km'FCfvBk"y3B }rʞqXv 쑯JtHrkϑ2Df& BcD 9|yaud"A4mj3g@8)k/ӐzǣO[.)2R-y˗)IyE`ګBTZe6k[S$銟F\c jGeP/+087%K$Sp֙e'Lvb1IzUZ(2F'z&seOS u|a=cw72sWNo˦3]FcPݜZhINoˏAqW&is?g*LTӏJK |^F$r{)@iEIbl'jrac.‰Z4fJ%g0q {@U|i([~ϙlShoJZY+e$ô}3d+8}4Hcݐq>L8ij$ݘxe OX[բ@8}#{S(i1UKbPz@ ["I>r6ά H\?}oh ,kߜs9򓒋rUaz5tmZ5t*n`h=ODa}4g}:'> H-_Ü]>l䒠FNX ڍ:? L %t^ Iť\f&PcUU1ׁ ,',T7⩙Kjh RF>ea5kx փOd<3 L<'+wP)3 m6ʣ֗q= E12trn)75N)u|ֺo}%DHhvqGLô>%t\ boъe@Tǧz^?GP4n*kDeV5&±Zڧ6; -R#/mN_)`)_ !E[ a-n_g >v`ixpPb'-^E?bÜNH̯lF>4_&Dv+Q{_) kbky Au[)A۪Y`:n hݐl|P bp,tPӷV,'Aa(3NNxTn vSfz̝-¿ ziy,bvĭ d:`j!E!9r8'Q O@2–7ߞ_G`RնDnXȕ4N:=L*`+ȿ훨>R2K%GqEJC,Iԉܫ]{Hwerex;:0m22*NU5y=edL^p˷U݂Bհ\% PGKۘ m>ҋyt=q3431NU2 rX88||FyyV.^Ew[0zR+BK6: ߖq?wx/"#,BuX~7?$8XHA aFPI@-EzC:+^4<҃ڥcpP(9֘fJbuMbM'Ju nF8ic#PI*a8.3}?kE؛V .\f^#Ғno@ o{X `vIĺX° Β{m"8 r3 O/mGii /EeiҴrޡ ֬ Uxiʘ(S$i_A-' )0,YID쩜qZ.L |pue~KB*wT4ewF6 U}]GaޯĪHS!h/?wBJX|8"fWS^(:C>_I%#Ս@`eֱV,4N.䠥ACV/P] Y¼6T w+j|>~!,cWB<+k E,%2XY3Xb]3]`ʄA 2*›hCIL݃#Vrf9@'(/\_` SbD Vnz!:7ydR!ɞyN^w&T$M6s7j$M ]x QLX婬{fϦ )iNlKQz n_;<%ᵕ%T[:4Y Cܧ,w4re={m!H3 =oV={\ Is'7.NQ#(EĔup,u(Gif@\-?E ۞Bt?wcQ\qf(y/VmnExd'k Xϝ=[CHY*J!A%rP)uXNꍚNO[ AeJbu}v(?D(j{m;SE\F35ighb2# *Y ӨQƤG67GU3s1Ow=kM{4y3:2i臻 |FM%RWD9U=#ːF#UbqG<ڵy6wɑb!E@7 u"@Y׉6ZڵkD6Hf"G))X"K(pCJMLՒ^@O_l?g]cj/7۠ƥw@Ai=Qg biWr v0!\?n׺;>T2Vp );׉7Y' uPaƄ _9# NS\z և<Ypkf7Zt)Iuo6R:mj؋/ +3:bF3+C DIvUQ1Q|WPPC,A(u955QkƀW5igY0 !ڕXVVFlWGhbro_'pjWA ˿mat {J[2WN#o%|ș$O3td*'hϠloPCTUþgV"_SS~? ,!x>VKBFyj[0+l@xnܓDH GMcxǛlZRZdaS yv&oUMMT)OWfb `n=RT$֔D˦?ハ]}|g&QE 0=Lp\hϭEZZt6~v!ZĈc:ST mUW~\&.AĊ~6̜T{ݟ^-[ӣx!hGLY(l4_G/W Noƶ*njz,vCFe.Is GAC >.&Hw `67*@{.Z/^]2Т-W({.{hkE ֕Ώz iA0(=$y-~E9[IE8TpPB9Gy#ޤ'̝گX*y4BP'xˣ1Ԗs/ $@tYh7VhtzVqBIuԳ2bNq,F!x_]ۤ7d/wlG/܆Dނ0Y-!B ǏDG҃Hso ˤu6!1(//aq&uk+Z:eie[w%܍6&gA?%ޚʦҴoC6WnWhԨwe=9vx\&+jLUU!Ek"+Г>_aܖNKs) 5&[ k,g:#!nч7 *Md`HS׺-Ou^B'ԍ[=ԫn;[.fuh H+X>U*#%ѣ#jlݳ/kA m:ř{i"IySi/vzjf1Ks!ﭨ5m-7.~/ϧ!.dBL?8G0ۖ pVG2Gus%th4iVOydS. %gYc;׈S1D l{G54y½Y%K 8ACfb6z)iP+/]Wշ}V5dy>w9D)UeE3j+zJtIur^3R9β+{! ]h:V ]кn@OZl}BO, WKe}m48݆PKS8ztV`02xoU #wQ$UYXh(]RE+FhVzK?TX\1FcvxA/gl-'V#<̶ BIJP@{I-?^bD΄sun.ڬBxU]J)G$X.= ^cmߍ" |^xzM=z0b[jVI F!̶WLok^8'sZ29݃%fUcYV,hP4+GC'Fء-yJ{-S3Gp&I_qj(WHC9u?{HI9Z NbZ(Ŷ* M`ȉoMȨ3 iS )xϵ5@WB^w׭90L hO,G5W N? lGsga+ $)%d>Q܏W/z֚yQ+okN`qSXQvjFQ琋4>v`<[ME^$' IFʇu+{@H!Tܪw,;/GߤF@pvҪsqI knVǶr*]QqΓ[nx ^ItNi#w'x (R;oՁ[Hjd2ދsMN]NxH+Y0˄hGD\XU܁)U6!Rf=O ]`=>-Ss奏rU{?WA/ /=xƪNo3x!Q&Nco6Ipࠧ[˿Jg.6sYJYr~|{̀XO+L8n5tߪhr,w GiN2:8[u$ ؾD .UrǵAB~|RP{ȴPKi)?80r6 f9Wlbe3u*a`$BR)iU2O7Ѧ(tR LvxA@AG@.*5E9lk:"ư_n4`("*5S#E՟=o+3]j6 gn/?O[ [G3gYL#+&nQ/س4{vxBsxk%$(PLmIp@%%)C9զDzrn*wCyihRT+;1Er=Vx2+v 8S\7p6VT\+d!a0 &~b;kD1D%nQZ=/O:]聞.1w7y`-Av?waD@oܗ^0ugq1aHŢk~9TdI4Ŝ6uo1ƛ%~#g@1܎47å'()!7Z'i5"%NB^Up\[?ia@lild2$ݹ9bZ,g _82 , S3 ~ERTKRq/} h{rRiwOTkK!3AAi:D75͚P#oG߻Kdʫ?ԗR*$qmnh:PwJvR=}kI p^ױdʯYWZ4])7ҍ1.Ly?&Y"S0]no᣿f?gTCI4o۠%˃Tve_͠ ׳;Vw{<+㬗Zh[x)-478t2 {xC#XeHlTڤ ,R5j+?g~Ef>ve++I՗#L7+g]T*6D\8-r/&>d04bJy}80RM}tR:)p6Bn봊Yͭ*W+XvK,mFqz t9QLmNlz{+xE[F(o!Aʘ<36sۻ*O]d ۆLg|HA M0HāP-EA3PhBQnK-=Ċ+"{mk1w!Nf}`qCws=\E?ǫn](_S( *)&ˡ.H@j䒥,7iv{P)h7ǃM/RȔPA1;Q5`㯺qZwX6\vZ^^-%VL2qt0_,bjitjg,SBJ"7d2K+Cp6  qGA tcMѦzvVo@{M <}%\^&e@HΰU#U\dX fQeGe9 &ȔyIHsGk6yMڟu" <3Jd*.tY<;"ѻiݐ]HzoP}BlE  $Q5ԧ`\RR]XSj=~}Yk):M'k_3eisΌhdxtE1N@ʓ2@HqF}+pEq,vJ LQ >6bW]3m wP<^gE1d9I⑦PBda%*n&B! ;3E僓y]K/ ӢL|=wpŜ;`y!u :v2:7Xh `67*@{.Z/^]2Т-W({.{hkE ֕Ώz iA0(=$y-~E9[IE8TpPB9Gy#ޤ'̝گX*y4BP'xˣ1Ԗs/ $@tYh7VhtzVqBIuԳ2bNq,F!x_]ۤ7d/wlG/܆Dނ0Y-!B ǏDG҃Hso ˤu6!1(//\MGJ-Bˇ(jQouzGZGsVvDW4$bœ7y*Ub Do]^fPp?cF9dTtM@x3.^E1{z[A8z w҃RF1KcD BzB/1`+=@)4amI QkݰVM󁑵6o,(] 5{&bfF^|.$UACʂ6Aby*5OPX만 haIyo*ך0u1RP)D,E^ eao?T#g<$̍Ȍr6u"5 ۘWB0^̚7éV !/|̔;INyLA[`_MWKe}m48݆PKS8ztV`02xoU #׬lSȻWe1R?'M[0ة8+WBF-FnngSGE?ye=02͖j}sΖ0Ј,?.XB9` @jzv?%!t ^sJ~y7$OϷ1=n Bvs׵l׺ꕡ7?~{Q.V/pΠkRS78懈+SG?= OD;ԿJ4'WSg .I<?2u{uM7gI9Z NbZ(Ŷ* M`ȉoMȨ3 iS )xϵ5@WB^w׭90L hO,G5W N? lGsga+ $)%d>Q܏W/z֚yQ+okN`qSXQvjFQ琋4>v`<[ME^$' IFʇu+{@H!Tܪw,;/GߤFwheswM+Ts a{=͙ 5^H'2+ @MFA710} %"9 tQ=lZWPll%I"~ݑ.Q+(7.O[(¨<3ZKh6j zm|yecf|ӿ3#d5,XWBD=l'*[*A7IyJꤖ;l lE\_9xB#}aI?d)mOLB(k&F5pCcHJP즤<@ѐ*' OPJx\ħ+"aEM휜2@њ9IT˅}^: gGך(|o'()Ur0Vۿ7M苡c?ia@lild2$ݹ9bZ,g _82 , S3 ~ERTKRq/} h{rRiwOTkK!3AAi:D75͚P#oG߻Kdʫ?ԗR*$qmnh:PwJvR=}kI p^ױdʯYWZ4])7ҍ1.Ly?&Y"S0]nob?g᩟RchM/A%˃Tve_͠ ׳;Vw{F 5kh3YygۢR@g- 0 ]m>jBMY.y#%_ŖYk3 YX|? :xnRC7;v>NݽA1:7HA M0HāP-EA3Axw3]sipL=6C,GJVV3iZV1v-+6 ou;.:wyTJ:}'ࠇ75]N;jIg6/Ԡo*Tϕ0}ТzLLABOՋYgM oCRV82'c6#[kmJ[*hw 5 PW3WA tcMѦzvVo@{M <}\.F/3L Jg':=ي23.oՋ %LFʺ` bw9Ԛ!Fk ATCÁ-@6.] HhA%A3t$.$O.SDp]c#)bN bm2å[uK$A:hV9B}(etD3^6Y}Ձ&bawV%Wȹ/MĶw֧ ?Dٙ>0njR_)FFI!Q^ n묪ךV[+!F/-.;g'uHnrZ!B1S>Nafhc|` R+? `67*@{.Z/^]2Т-W({.{hkE ֕Ώz iA0(=$y-~E9[IE8TpPB9Gy#ޤ'̝گX*y4BP'xˣ1Ԗs/ $@tYh7VhtzVqBIuԳ2bNq,F!x_]ۤ7d/wlG/܆Dނ0Y-!B ǏDG҃Hso ˤu6!1(//\MGJ-Bˇ(jQouzGZGs #}'Гo@:̕c>}NgoEIV^YaxaՀAD^t;}_l앝r \SLzbZG5FaMҺna1-}H1`|'?}(^5Pտ1 S~ $Q\؜o/XD'Tb>g`XV9#Lr^/qGW6@0lCNr8F_#am{4E?AP$Kg^%p9>>5xJN5r6>m-_e:@ 1mIf*:l# l'qF(jEr2fZAxILFz+1¢]7!pcVrJIlg6i\!q9`R\͵K]^K'sj q(g3IFeZQ|ҰWմX5ܡN-OCI 򞱊rKyhM*OJM5NvّSfh3Xs̔ J(_WKe}m48݆PKS8ztV`02xoU #׬lSȻWe1M+4;9CQ(Ek (wwr< ~&ة9Q)%P?8/*M@cZG\i3?ԧL%ͼ6ۙ[{aaί[⹥?@ן&]&`&P).Cp:]R<&Os%8Po~HncrtXaZ 2ĥGbzg|㇒Gt\;_6?`"llq u7o%];]I9Z NbZ(Ŷ* M`ȉoMȨ3 iS )xϵ5@WB^w׭90L hO,G5W N? lGsga+ $)%d>Q܏W/z֚yQ+okN`qSXQvjFQ琋4>v`<[ME^$' IFʇu+{@H!Tܪw,;/GߤFwheswM+Ts a{=͙K{-] h#_M%hOdVag?K) wЛ;O;p籛I0dH7+j+I)9PH\G2ɲNZ(Iwxd/A|gK4 ZgͤsWz9g4l 㒿#!ВcuYy\&kĨ+{\~\߄+;܎Lb#!)6 FmY}(tP7x޿<)->9 xtS!}%Pt5D a&b(4žS3f;Uenb$?^YIM\"'N^FMӄQG&szظa3u2~G5;KiJ1,!u'DCToˆEDžuS9WX<ON! `V'bG3gYpԭ%ƅΟb&3]dӄRӒ4&H=|ZA1FazD/d0cJ7pt9^ׅ8j " en Vt]zh<~#!CX6(&fRO ZSqtR.1ɶJQςsI7/ʏeU.n @zEm2b9nƙ#NN?ia@lild2$ݹ9bZ,g _82 , S3 ~ERTKRq/} h{rRiwOTkK!3AAi:D75͚P#oG߻Kdʫ?ԗR*$qmnh:PwJvR=}kI p^ױdʯYWZ4])7ҍ1.Ly?&Y"S0]n?7_C5h_COA/᠟A?; (2:~ˌv׎U>2 _+|Byx͖őn H30&_]97]]1j U(q q2j3Iǿ@oy(?:ޑvՋG= X>H/͂F.+kNCffK-e*OWt#HFwtR͂R!hk#~.^C[%$ ݡ﷧VrT;Hs qu `_Qo4!Bzc 05>BpGwrѴw=F\`Z`H(~'kZ)b"6>0~5UMDJd%vHF6Z)J xh(މaed:H";xG`smHwu*dQQUmHbMnoNީMnnxo 3yݥ+ F EaP"c9 B{)¢sĔ]_),<<ZK_E*l]OTHREg yuhl{B b.PakyMH\TBk> f_3̍+-^1:W̶#"(jG RmJiMč ?L[g jG7ioo&DxlpYװ>oݘHz4pᥫj;"܂jNb5zo1!"MO+(<·X7`_<Cn`3xv^1s_g*9ZMF؄2}oUq#“7/&bPx]Жw ɅDϳx1 ٽgUwT6[[]=hWc  K@sA@DtU~$[G#EF %:n4޾̿J7[tx =T V-hG RE3v;8*:QCb)"Vp27r U].S%[^ BDoP62un0 @~-صA> $hσ0?{0[ּXs0L/_%7G`Kԑk404oQx'I@kؼs"C;s9/DEc=OOFshtA?M[{fWܰs[ s$1 (HV1K[*GZ`G*A sʩ8k^?WW|CZFia> $_-WEjs5v.avqXA{nkxroc ю΄=k 7i 2o'/QA P^!TکT[)Z*?RXXQ%̜mΈ> g1,` ԨzƉdl)ni"F@YⰦ(+ ?=Qt K|b«w79'ORWN9!kMEQ1rUx"[BbO/!3U\`28+|ү]y!h@cS, ~et#ϊ@J&pk,jk]qR*xExMYfq5V#:W*7QAYfPnCo~&r3dbX>n 4ޛp1Fsz$zeQ:y Q%/L+yk1MV)5h> գ r{ Vͅ,EYtUKt9s&4;kJÝY|jb,Y#Kwӓ8gIiNr^BbR6omRw%\h>qNP *|eYCF΃8 ET-;JQuqwV,+K!мImqϕܵo ZZ?8kn#~e}l\7C+ƹdY@A)E 9\^[W uF- nK8m4M>f'@ < VG,\v&)MaZڛ~U떞λ*a-M j 0xY|,^˘q2wBz c84۪ ʜM[Eڎ۠{^2 m JǠ.8ܞ a.ݩ4p3J"\(ucDXPR9j*G4-!۾gXmF72)@Hj[_9σtpR 1)̫= !YycM)K6$-awc5#p#Ŷ@ҧH&mY+o#@ eUJMm ]Y'̊^'T;Ux1OYxzȸ%BϮY$ϟ:`1F&1OIDuZIf-OaLE&BV{g/@)?]{1ztGrqrtt^x[=x e _~446IGjSdQ\)ޢrKO~㭧-Oeh5 *w羿e ꁔVc-?Џ.tKk~v152Ggޟ۵_ܿۧv$?gv?>V%Yiԥ52v/huO׬͍YZdv'8vBՖk󢞏Dن|P #jO^!}<۹2y[f}!޹]:x$ڔN+h0l eFEnP*9u*NxPZbv 9>$l;Ԥ`lr:yCx Mȸ>]'F-ȡ &Mw̑8;Ӎp{' O_;o؞PrDftoG:F!I,s&`M%KXWdK'8zفkD6 t8y&!2BLnsO@Va}':r̼+ R`@Sݭ+{RNxB }qnکu"wKRkDA>< ]匳"cIn1an~'hN.* (ࣕYV8^C͊7B6نB"g@"e6mzACf^w+J-)|eX`$l ֛ ,0ߐJ?o;891$HiQRUC"5nn<9)JZX,$F-) ]3X#O?).E}Xf]뺯d+:0`Gt(%&T0Bć@D/m]Hً-{iJ%T"Mdq]Jjم&.8]#m?K AebuOF+z۾#}xzaGorr+:ӄ.Ga^[BKViKZ[ , o zm?~oW^C~uFYe78V&"$AJid-^(b"YDk)ѸR #NU_,n{x[if;;1 ?Ϟ4^cjլT8)ۥ=RrH}~fe81r#X$J'(8?):4}TCa!d}P ̴߱FB9ǗgҀ_5:yמHɶZǼ?#`k=gړT$*2; }v8OZ,rCۅ/rVQ mHrE9V?őK[):2-Ծԟl )0p.|y(aCG|e+@=k UOn`P2dS0PkPħB$qUb ou4ꚧ5)K~~M&! MbM:I :TËUl (]ma7/P&X$$*ij p$ZY[Mzܫ}z@ӡ _ZF >Q ^oHvJ. jemR1׾hy/@xĝH _F$:CobGT5&o暔»#ڸev%84gf]y p(dUk#2Ab O473g!w `q :Ze(ZUn؇g#g#8*?x<ū|:p oK4fK$b1JroL& :%D.ė*|V2rӅQuѬ_ԁc YS(5H4Wr~9A,$*2uqMZ&3 ssi2I'4@8FX4яW1d?7K@O>|>tL7蓙_0d]Zu7e,)oi@`fUi"5#MX:d-|6S:p>+g%,:BP!^f;P  bR puC"8UO_o1' &E8S(sK`[z BtrB|L$gpY=%`$.nCu_{y3 ]:[Coʗū#%eB@t&1DڸjiO$D3+;崝Z_L+nJX?jRvEP ]|+` q=[y}c_%\#U$ ^)r?HCK',@ȳS q7-HzDfkԋ{֬vGCQVk6ӴAiM#)bj^E8(GNB 4EXNb.~ĊL:ٹũAAP6'PC<<7ޓS+xۙUO]ܽs$5-Do'rHϣ#~98C.l*w^os]*Ydv a#mT2q#JP./m3aHpjs+b+I#)}( Jܼʠ+Pv7ϼ=:3rwl6 5Wk!0AsߎZd|Ozo)tiu ڶ J;gBYêe(bn)E=a,$uw4Ҩ6Fo,6ןVqRuf:"Uẃ'N׺f v.*~/BVfΥW91})):"?)쒭i-OY FJ÷ɻ.:wݎ5*RwWߠBm҆SYkHCci"M+H0?<*!:c>+[fZ$ԻSɿ4$%6Ŋdڽir^ w_kz_yސc4٩41|eF+ 5f2Xlhs5qP`4p=KV3 Ǭ! .Yݪ [r,*AWA=v` z0eDuf.FV 7'Hf%VŻ2Ti^!f.)ϛ(Ƞh `(R" GfPyy03NM)/5ŮTϕ4("Y%{#On)UKj NQB2l| (L&+]Es]GoPɌoM1'7C۸rDkSK)աp)%P3 ,b۰_AvDiWT3vxq1'󝾼O_%?Q_<<T -lch~/8 CVhn\7Dؐ*~kҳIjp w0t9х#Pn\~O&d+Qfު75R3aU_j(Uvts~H̗(ȸPO;ǰ級x\Ctfv qr.il@:g]KB;)Ȏ74lĤ_AY(?%_/PvCfUᐋ"wBÿn7kP{-v&MY,cA o3o+h5m~!1,Bb,}ʜL՛*B؏y-Ti,Vb-֦ 8* `mߚRZBs[YkK- N#N+Ζ; 3fda :#t%gCz6DL}dLM1/ƚqȫ RKK DڛA6bYKS\zJ4m8K%uD}bbaR4s5I'h[z:L,>E|Lk|6wS8jܷtq#m͕fk" >IX!nLSSINUHZU| WW'veUOn+K3ĕ~Om\MӻFGwޢU&rlkPm2?z,. IO=Қ1ڡi䀘O3 ~;-5_}7xn9UMrߑvvMI9E$G'nhV QNo4W|~b1Z;HPplA4"{C*$_P%|&/ׂBztَi7xaVh_cS۶i%e:a$xkU"CC:V岯bIS MSNO;P>#TPlACj@?ѹ eb*NJӐB>A28Ԃ5 _%9ͳ=`7xN Z5ROTC>QEKO fBK*7fdB"ǵV`[}.ـ7 f}?'wu $ϒzu3?[TQl~ LoūA'ɱ@۠oS_|k:kf%~JLUI :Wʷxn*w&e(F1 %RW47Xp_,F#(MAϔZת4+ꎚ/2S b~H*!0[?>®dQ_jV>f[_I7~}m\xc2d?[1|R7> l:,<Ûj?'ҢM60Ƭ asڪ+jU)H7W:s=b2)8|)<8) 01lϐ4i AG'ѥS X߰6Ix09؏;3kht'GQ‰!lCy r̭e}嘠+guf}%Y:s?g7>pM s+zi2M-jJ@t!{ʉ^\|lr ^$"poߙR+qơ 졫QvH l?~]S>Aa>`Kɞux׳Kpcz*5DJVsx=t5A)-9hS̴߸l`ۓnP8*ϨI5BN [9zxD ^ο_*1@Y%sڴbupTqwe}kZw 78 v~霳Y"VĽfN]}y a?U|NnN+rm4 ҕT.N߱ nOkt@gG8;-HFU 4)i` OC}>Azw2F# GYfO5;[y^``r>8m`X0-3 mZufWfqD#̃XIQLxay+VO;XCu%.1Pc_Џ۷;ۻR?\$n~ "dܟW/Inx%Yiԥ52v/huO׬͍YZdv'8vBՖk󢞏Dن|P #jO^!}<۹2y[f}!޹]:x$ڔNAnv N/QX"M8et|F{44svy5}a&]޷Fgg٩CބSޱX.Gg ף0p%ƗI&g)Dj.;Mԕ|T !I,s&`M%KXWdK'8zفkD6 t8y&!2BLnsO@Va}':r̼+ R`@Sݭ+{RNxB }qnکu"wKRkDA>< ]匳"dیV[M5d>񷦘WAE ΁WE+w ˛xqJGk?/[jTӱv{ c[W`W z"OvSk!hNH\߻U%y'm3b6LV{ tc|7A8-6rMX¾X4=Ỉ[!,sQ{Փ:*= 7 /Ǡ?fR}zJ Ū_œmIlFZe_l'5p@u{k6y+e&57KZq'|cK!l|=xxzg0O"nA6+; @BKVh3N9Eo zm?~oW^C~uFYe78V&"$AJid-^(b"YDk)ѸR #NU_,n{x[if;;1 ?Ϟ4^cjլT8)ۥ=RrH}~fe81r#X$J'(8?):4}TCa!d}P ̴߱FB9ǗgҀ_5:yמHɶZǼ?#`k=gړT$*2; }v8OZ,rCۅ/rVQ mHrE9V?őK[):2-Ծԟl )0p.|y(aCG|e+@=k UOn`P2dS0PkPħB$qUb s(N#5qΦr^f~#m|qmc$dY4~g 6ɡ'b?7| & )xw4/ƕ3cA`c[;xX/J9v=ח-=t,bpm0ޞ,jE85`ԁc YS(5H4Wr~9A,$*2uqMZ&3 ssi2I'4@8FX4яW1d?7K@O>|>tL7蓙_0d]Zu7e,)oi@`fUi"5#MX:d-|6S:p>+g%,:BP!^f;P  bR puC"8UO_o1' &E8S(sK`[z BtrB|L$gpY=%`$.nCu_{y3 ]:[Coʗū#%eB@t&1DڸjiO$D3+;崝Z_L+nJX?jRvEP ]|+` q=[y}c_%\#U$ ^)r?HCK',@ȳS q7-HzDfkԋ{֬vGCQVk6ӴAiM#)bj^E8(GNB 4EXNb.~ĊL:ٹũAAP6'PC<<7ޓS+xۙUO]ܽs$5Ī#I8)20'oeعW3Qu ׿z*''%VY1UثqHn*PU4 J܉[g''bM K-/eF)`ysmx"WȞ.wbĆwĞ$+7Lq8.ZV@&AՖƧ©Ax+̡; ͖I|+dRg`Π{K*WMްa;_{!xB%ӭyǤAA3X?6%lW8V-\x w|]xxMȆpIu2MNnA񟑊*Y˗rz"L,v$Lw`BCc`q@ŜME@#́؀/{|1뷜)N(¾w)`!=ʙďS %ץ0%K] ySB)ܿ6i#-Jo sei \0e.S)LQxV@/^yBNQ ɩi D\+}+ V|'t7 zۑXy*xX3A(пml9{*6]r]zyj SYkHCci"M+H0?<*!:c>+[fZ$ԻSɿ4$%6Ŋdڽir^ w_kz_yސc4٩41|eFw)1fתPӈs`ܳFE, l uȵ} ٳY9Up.l)*n\z o%2*^ܗD6W^7s5Hq G2b=]BI< U@ȗ84+ܥUYT9o5.a]KB;)Ȏ74lĤ_AY(?%_/PvCfUᐋ"wBÿn7kP{-v&MY,cA=us7B%HED%-u3xAd.Jv;S}}W8Zʥ-""& V=<~WNDu=3O{XARj1oY- >eMVbF6=ɧub(t)1\d[6&/pUPn; k µz)94Cۮ)}~IWe꥿v (4 ([*599Iv0j%W6huuo,jz 3 zD j܏&>H_zҞ10Pf' 2 ^}Ó{(K7 b{SINUHZU| WW'veUOn+K3ĕ~Om\we1Gmd?}@Rgutk*zT(F ;F(ki8*B$HWA i.hMUaK!Pȇk7t$?l'KQ ečSΒ;DդMz/Hj]CB>:y(pL:;܉co1;7gFedaV7֧fG@8d7G {MF(T/%IєE2%z,iZ?8ms%2}\bɞAaP3Jj9)<} "X0xD^bP<_Ek}n~ѯ_ͳ=`7xN Z5ROTC>QEKO fBK*7fdB"ǵV`[}.ـ7 f}?'wu $ϒzu3?[TQl~ LoūA'ɱ@۠oS_|k:kf%~JLUI :Wʷxn*w&e(F1 %RW47Xp_,F#(MAϔZת4+ꎚ/2S b~H*!0[?>®dQ_jV>f[_I7~}m\xc2d?[1|R7> l:,<Ûj?'ҢM60Ƭ asڭ@^KCHRU҇))볙ލc- -CT6Tu(ƾ~}G5J2 z| ~êLhB; B3gZ zסSƺ_̊ By̩@5Xi4tL-.#ɇ3|V5E<mY:3z=KM iyf%}t]S>Aa>`Kɞux׳Kpcz*5DJVsx=t5A)-9hS̴߸l`ۓnP8*ϨI5BN [9zxD ^ο_*1@Y%sڴbupTqwe}kZw 78 v~霳Y"VĽfN]}y a?U|NnN+rm4 ҕT.N߱ nOkt@gG8;-HFU 4)i` OC}>Azw2F# GYfO5;[y^``r>8m`X0-3 mZufWfqD#̃XIQLxay+VO;XCu%.1Pc_Џ۳oۻJ?\$n~Mstar~,U?U%Yiԥ52v/huO׬͍YZdv'8vBՖk󢞏Dن|P #jO^!}<۹2y[f}!޹]:x$ڔN,c4$iǟurOn;R|rT8p`&Hds jYuO[YE$0rQ*>:mQs: Hz0Xwx{I&g*!0+ 4T{fBL<mR)!IuZ d wĚњjJY r:,y)tMWZXj1!I,s&`M%KXWdK'8zفkD6 t8y&!2BLnsO@Va}':r̼+ R`@Sݭ+{RNxB }qnکu"wKRkDA>< ]匳"dیV[M5d>񷦘WAE ΁WE+w ˛xu]oVg!śGS!+O`W z"OvSk!hNH\߻U%y'm3b6LV!E1ϩ #Օ W ^e3$,KvjX[J\|a*h3;_sȟ!:yÀ1߭8Q ]'j-{LsehK#gA :!3-.GА Ae k2y_? w'TÆeB]: =K%G6k3ŵ}'3$SȠ19M2xbdH6bSSѲvvf&B=L}zeTgCh>1LI*E`l4J$NGMt)ԁc YS(5H4Wr~9A,$*2uqMZ&3 ssi2I'4@8FX4яW1d?7K@O>|>tL7蓙_0d]Zu7e,)oi@`fUi"5#MX:d-|6S:p>+g%,:BP!^f;P  bR puC"8UO_o1' &E8S(sK`[z BtrB|L$gpY=%`$.nCu_{y3 ]:[Coʗū#%eB@t&1DڸjiO$D3+;崝Z_L+nJX?jRvEP ]|+` q=[y}c_%\#U$ ^)r?HCK',@ȳS q7-HzDfkԋ{֬vGCQVk6ӴAiM#)bj^E8(GNB 4EXNb.~ĊL:ٹũAAP6'PC<<7ޓS+xۙUO]ܽs$5Ī#I8)20'oe8;lE0Dv~c/M\y:Ɖ-wӏa@xJ&Bn풼*|8?Pݯ;&ƻSp`kxEG1d]+xJnvjg_~HK`(Hϙ?h82i'e.Cq/|k!ys(`\ x$&r9%s@EvT..Lu~p D|>\>I(Ch)D{OK_Ѳ#"LHiY 0o-e_Y˗rz"L,v$Lw`BCt4 KFBWXqD-[O64]sXmrnm[&_6@2q#D$@n"jUufƐPP8PG='DmkY4mFKñTPVw|xdp+\@RuP)aEgAvhtlK υ~k0^1'#,Ђ"cֵ- ;7$Ġ6Rrh?EzzD$i99,: [.fKy$zQ3tr9Zk *,`7 ^a{Euq'cg[), 1]^I:$"#qhV, YKt/iǴ6ޮA!8a4*m32e0"e)ԱsX#$ʏn 7!S`~9o`UsI"ݸs|00cҭܳ Mބ>SYkHCci"M+H0?<*!:c>+[fZ$ԻSɿ4$%6Ŋdڽir^ w_kz_yސc4٩41|eFX`^rG?r@jDb}y9($O۫~BT%yS@r94>X@aeS)հ@0K B<g,eՙpn)s}1O! .Yݪ [r,*AWA=v` z0eDuf.FV 7'Hf%VŻ2Ti^!f.)ϛ(Ƞh `(R" GfPyy03NM)/5ŮTϕ4("Y%{#On)UKj NQB2l| (L&+]Es]GoPɌoM1'7C۸rDkSK)աp)%P3 ,b۰_AvDiWT3vxq1'󝾼O_%?Q_<<T -lch~/8 CVhn\7Dؐ*~kҳIjp w0t9х#Pn\~O&d+Qfު75R3aU_j(Uvts~H̗(ȸPO;ǰ級x\Ctfv qr.il@:g]KB;)Ȏ74lĤ_AY(?%_/PvCfUᐋ"wBÿn7kP{-v&MY,cA=us7B%H]Ddya"[BT;d * sb|OINjI2܅ެ Nsvun?Վx6/JD񘈛G"9uYMʪ /wy/l,X]2haN@ɐ@XGvMas&g)7/Ǧ3j#sDR6[,kԙi@r\s2U,AGaO"nWE!͏!J?SINUHZU| WW'veUOn+K3ĕ~Om\we1Gmd?}@Rgutk*zT(F ;F(ki8*B$HWAauZz<-c^N^vM;-?JIs^ne xRr <.[HT饒 3r1O^>忂sv_a;ٙťY-u8ӳ A?Rxt:B(,uF0< w~될jkDXBx]j@~ PGR3E1z[r9xs&amIgThd2ί^Qq%!QD"Fͳ=`7xN Z5ROTC>QEKO fBK*7fdB"ǵV`[}.ـ7 f}?'wu $ϒzu3?[TQl~ LoūA'ɱ@۠oS_|k:kf%~JLUI :Wʷxn*w&e(F1 %RW47Xp_,F#(MAϔZת4+ꎚ/2S b~H*!0[?>®dQ_jV>f[_I7~}m\xc2d?[1|R7> l:,<Ûj?'ҢM60Ƭ asڭ@^KCHRBo4)xU{( ۉi,ՂOFAe[1!3z< B,eFt>{c!#i)92 g6+axJP({ltOI$X(pﳺeWps>>7S)]a$*9]S>Aa>`Kɞux׳Kpcz*5DJVsx=t5A)-9hS̴߸l`ۓnP8*ϨI5BN [9zxD ^ο_*1@Y%sڴbupTqwe}kZw 78 v~霳Y"VĽfN]}y a?U|NnN+rm4 ҕT.N߱ nOkt@gG8;-HFU 4)i` OC}>Azw2F# GYfO5;[y^``r>8m`X0-3 mZufWfqD#̃XIQLxay+VO;XCu%.1Pc_47>wW[cO=l~_~'x =m/e mpcm~3j_ܿ #T.!CN]¯Qld9W/G7HF0ƲQZ>YR!FRS g[Ӽ.sFpW'ʓH|Qߓ`E# _dB6&&(^+(<ϘAqJO2?A^E< ;?**ev}RS2v"kaGf_f}q&[W`L  #]h9.g\?`@1q][cGJq1N+}R~cw6$$5Y& yr2EOyP=ڢLMbAB0)uMP!byweax*W9~%@X@S& &<;N/@B%' 럨0K-j5#9!(-V:9Y f}=-k%nc?y$@L!4N׎*馿%*L 𬝾b0'kZa̘&>_$wo0H5IQ!a χ{0"qiδ7ǐ8@*j~H =2b)scP•oQ%eD:ߢcw$' T} x0le1Ew]Ow{{*zƅE":q;/!ZmdZշh٫U%SցF!^qbr?od*06 2խ [k'v|&I?3b67 6W:kj`ihZz֧⮘@VE{@LU7/^:Pţe*uI>7@z|O\PO4n@C&80ANֽB.|]?! Eg1miK|sߛX1N6%j]s3^r+up?FH(A &ѶIQBXe|1 ,n|?fUd&*ϭd#N|$K8ҭcWBeQӋ|u^ADEr:ADƎ/WT| >98菨R<mֱq X-Pqz*,k%( MGhʃ+m(fcݎhp1RgL77[bz[Aǹ)zB>Y.5V<6jq%Aʳ%HjA-$p@7Čζ^8g˟ډ.75P  cq8r~4zXv1 arkX )AXP?FCkӏ5.ec Xn*IkoC.tihuDߥ*wnMt[/Û'܎íQp;zuEERɬ,eC0K37sV ӏ ᴽu]5=28 c8Qw^/xGp ?Ez8}cҽxyЩ޽qsWR(2DΤv% 5%-YhM\6=_ 5JO^pi6-n+Tl6͆1aҫ~ V[wpG~h#Joӊ65K\ #v\[g{#5Pw ?NbW8i |3- ªfhrX GR_Au .UnxELa|^H1*A`Ȧ=}!Tq67)&WPșK yl4|IB9ԄpRbi -43{)23tH\#~f#F$_2[0W $x͎|w^W,v3q%SoۧPgN=%-H  B;bXʼKVi 6Jj_S%p(-)^ǏO-B|0q8IDK~CpVX#8S $ Aqs_פ:| 4 ) C\D~ѼYj^! :bl&]Q v ,s#J {U ōPQ79I5PY}dB*ybCEbӪcaN͎ Ep7b6xH4)lwm2^3;t$&5OCA5D6fL$Rp֭<~οw&Z8W>OE&,lw| 1v0}eF|o[@RӪ {>`oc1d0&)&5FfWyyp@16rf~14ݞl~Wúye]d[0k 9ZլPhgEh^mMiTJܧW6 ۘ临@8爊ؑފT(K&b}Z~*wQ'e҅ i MVVZh5Lk+%`0Fڜ![Ƕz6%_aSjq;.G;Aօ c&%GϡU$̣W@ ǂ91rE Yq\drK]] BM/)ʭOjQZ%W ٱߛsX'WY|*՛-9Mr?٤+ A].V.x%hM|q$i)uк5yKTT1B)9@ܠS]O~Sk-  RA.h'ߤ iU:~>CNN[_?xJ`,)^mvFjBC0Ej'斄.I=NT"ҡR*>Jj{OȬ6((*9tEt X/2f<FS-"Of&nNuQ3.3*_hq@ngE=S' u ?*{Se;p@#ӗ{ v)_F \z jsheQcP:dߗ(Ko# ]Qj$+<#Է!PI~z E||AOC>?z/?jL&k[dfsQ|i3ϰxQ]<4BłdGUݾ.}Jhk*śoDOC;&jsX!Z~\+C{l(g5ĂKc.RT`ދ!M8=Ǹ~x.)ު6?˹ݔҍڌ5d sgWi*y:HY'o}!cJkb~'8 -!c9۩*)[Q)+KZfR)KCLqѦBQ1AD+>k=d⮊6Q K^2^V +*[|@V+8.mk T CW͈SaW{&JI:|7 }$QFYi4N: ](/BiN~vo 8P{iNTqGa;D)MP"kK^HG՚aP,Jx#g+?|Jqb]˳/gtfG6([, 2 9E i7DԊ+Q[]uϹ牍\M)ne7~IF5֎L P&I_X{wԁO\xLpw>%]+asources/org/spview/filehandler/ExpFile.java000644 001750 001750 00000043740 14202736504 021642 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; /* * Class for experiment file. */ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import javax.swing.JOptionPane; import org.spview.point.ExpJsynPoint; import org.spview.point.ExpXPoint; //////////////////////////////////////////////////////////////////// /** * This class defines experimental data. */ public class ExpFile { private final String name; // file name private final String lnsep; // line separator private double[] x; // X data private double[] y; // Y data private String[] sdobs; // frequency and intensity standard deviations string private ArrayList[] alejsynpt; // frequency mark, intensity mark, assignment, associated pred, comment private String[] exasg; // EXASG private int nbxy; // nb of points private String str; // to read file private BufferedReader br; //////////////////////////////////////////////////////////////////// /** * Construct a new ExpFile. * * @param cname name of the file */ public ExpFile(String cname) { name = cname; // file name nbxy = 0; // nb of points lnsep = System.getProperty("line.separator"); } //////////////////////////////////////////////////////////////////// /** * Read file. *
validity is tested following FORMAT 3000 of eq_tds.f and eq_int.f (assignments.t) */ @SuppressWarnings("unchecked") public boolean read() { double cx; // to keep X double cy; // to keep Y // to keep points, unknown nb of points ArrayList alnuo = new ArrayList<>(); // line number ArrayList alx = new ArrayList<>(); // frequency ArrayList alfaso = new ArrayList<>(); // frequency mark ArrayList aly = new ArrayList<>(); // intensity ArrayList alsaso = new ArrayList<>(); // intensity mark ArrayList alsdobs = new ArrayList<>(); // frequency and intensity standard deviations string ArrayList aljsyn = new ArrayList<>(); // assignment ArrayList alexasg = new ArrayList<>(); // EXASG ArrayList alcomm = new ArrayList<>(); // comment String cfaso; // current frequency mark String csaso; // current intensity mark String cjsyn; // current assignment int strl; // line length try { br = new BufferedReader(new FileReader(name)); // open while ((str = br.readLine()) != null) { // line read strl = str.length(); // line length if (strl < 31) { // line too short continue; // skip it } try { cx = Double.parseDouble(str.substring(6, 18)); // Double for X cy = Double.parseDouble(str.substring(20, 31)); // Double for Y } catch (NumberFormatException e) { // format error continue; // skip the line } if (str.substring(1, 6).trim().length() == 0) { // line number has to be defined invalid("empty line number"); return false; } try { Integer.parseInt(str.substring(1, 6).trim()); // validity test } catch (NumberFormatException e) { // format error invalid("not an integer in >>>" + str.substring(1, 6) + "<<<"); return false; // invalid read } // keep it alnuo.add(str.substring(1, 6)); // line number alx.add(cx); // frequency cfaso = str.substring(19, 20); // frequency mark aly.add(cy); // intensity if (strl < 103) { // complete with spaces up to 103 characters str = (str + " " + " " + " " + " " + " " + " " + " " + " ").substring(0, 103); } csaso = str.substring(32, 33); // intensity mark // standard deviations if (str.substring(33, 43).trim().length() != 0) { // frequency standard deviation try { Double.valueOf(str.substring(33, 43)); // validity test } catch (NumberFormatException e) { // format error invalid("not a double in >>>" + str.substring(33, 43) + "<<<"); return false; // invalid read } } if (str.substring(43, 49).trim().length() != 0) { // intensity standard deviation try { Double.valueOf(str.substring(43, 49)); // validity test } catch (NumberFormatException e) { // format error invalid("not a double in >>>" + str.substring(43, 49) + "<<<"); return false; // invalid read } } String cstr = str.substring(33, 49); if (cstr.trim().length() == 0) { // shrink if empty cstr = ""; } alsdobs.add(cstr); // assignment cjsyn = str.substring(51, 72); if (cjsyn.trim().length() == 0) { // shrink if empty cjsyn = ""; } // assignment has to be single if (cjsyn.trim().length() != 0 && // not empty and aljsyn.contains(cjsyn)) { // found JOptionPane.showMessageDialog(null, cjsyn + lnsep + "Assignment duplicated in file" + lnsep + name); return false; // invalid read } // frequency mark, intensity mark, assignment test if (cjsyn.trim().length() == 0 && !(cfaso.equals(" ") && csaso.equals(" "))) { // no assignment => no freq or int mark invalid("no assignment but a frequency and/or an intensity mark"); return false; // invalid read } if (cjsyn.trim().length() != 0 && (cfaso.equals(" ") && csaso.equals(" "))) { // assignment => freq mark and/or int mark invalid("an assignment but no frequency or intensity mark"); return false; // invalid read } if (!cfaso.equals(" ") && !cfaso.equals("+") && !cfaso.equals("-")) { // freq mark = space, + or - invalid("invalid frequency mark (must be space, + or -)"); return false; // invalid read } if (!csaso.equals(" ") && !csaso.equals("+") && !csaso.equals("-")) { // int mark = space, + or - invalid("invalid intensity mark (must be space, + or -)"); return false; // invalid read } alfaso.add(cfaso); alsaso.add(csaso); aljsyn.add(cjsyn); // str.substring(72,73) is blank separator // EXASG string cstr = str.substring(73, 103); if (cstr.trim().length() == 0) { // shrink if empty cstr = ""; } // assignment, EXASG test if (cjsyn.trim().length() == 0 && cstr.trim().length() != 0) { // EXASG without assignment invalid("no assignment but an EXASG"); return false; // invalid read } if (cjsyn.trim().length() != 0 && cstr.trim().length() == 0) { // assignment without EXASG invalid("an assignment but no EXASG"); return false; // invalid read } alexasg.add(cstr); // str.substring(103,104) is blank separator // comment cstr = ""; if (strl > 104) { cstr = str.substring(104, strl); } cstr = cstr.trim(); // suppress leading and extra spaces (!!!) alcomm.add(cstr); } } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null, "IO error while reading file" + lnsep + name + lnsep + ioe); return false; } finally { // close if (br != null) { try { br.close(); } catch (IOException ignored) { } } } // save data // nb of read lines int nbll = alx.size(); // nb of points if (nbll == 0) { // no point JOptionPane.showMessageDialog(null, "No valid data found in file" + lnsep + name); return false; // invalid read } // sort according to x, nuo, y, sdfreq then sdint ExpXPoint[] expt = new ExpXPoint[nbll]; for (int i = 0; i < nbll; i++) { expt[i] = new ExpXPoint(alnuo.get(i), // line number alx.get(i), // frequency alfaso.get(i), // frequency mark aly.get(i), // intensity alsaso.get(i), // intensity mark alsdobs.get(i), // freq and int standard deviations aljsyn.get(i), // assignment alexasg.get(i), // EXASG alcomm.get(i)); // comment } Arrays.sort(expt); // sort // previous, current double prevx = -1.; String prevnuo = ""; double prevy = -1.; String prevsdobs; double prevsdfreq = -1.; double prevsdint = -1.; String prevjsyn = ""; double curx; String curnuo; double cury; String cursdobs; double cursdfreq; double cursdint; String curjsyn; // find the number of single lines // same x, nuo, y , sdfreq and sdint nbxy = 0; // nb of single lines for (int i = 0; i < nbll; i++) { curx = expt[i].getX(); curnuo = expt[i].getNuo(); cury = expt[i].getY(); cursdfreq = expt[i].getSdfreq(); cursdint = expt[i].getSdint(); curjsyn = expt[i].getJsyn(); if (curx == prevx && curnuo.equals(prevnuo) && cury == prevy && cursdfreq == prevsdfreq && cursdint == prevsdint) { // same line implies assigned if (prevjsyn.trim().length() * curjsyn.trim().length() == 0) { // multi-assignment => no empty assignment JOptionPane.showMessageDialog(null, "Invalid data found:" + lnsep + "multi-assignment => no empty assignment" + lnsep + prevnuo + " " + prevx + " " + prevy + " " + prevjsyn + lnsep + curnuo + " " + curx + " " + cury + " " + curjsyn + lnsep + "in file" + lnsep + name); return false; // invalid read } } else { nbxy++; // new single line } // to compare with the next one prevx = curx; prevnuo = curnuo; prevy = cury; prevsdfreq = cursdfreq; prevsdint = cursdint; prevjsyn = curjsyn; } // create arrays x = new double[nbxy]; y = new double[nbxy]; sdobs = new String[nbxy]; alejsynpt = new ArrayList[nbxy]; exasg = new String[nbxy]; prevx = -1.; prevnuo = ""; prevy = -1.; prevsdobs = ""; prevsdfreq = -1.; prevsdint = -1.; int j = -1; for (int i = 0; i < nbll; i++) { // for all lines curx = expt[i].getX(); curnuo = expt[i].getNuo(); cury = expt[i].getY(); cursdobs = expt[i].getSdobs(); cursdfreq = expt[i].getSdfreq(); cursdint = expt[i].getSdint(); // nuo has to be unique // same nuo => same x, y and sdobs if (curnuo.equals(prevnuo) && (curx != prevx || cury != prevy || cursdfreq != prevsdfreq || cursdint != prevsdint)) { JOptionPane.showMessageDialog(null, "Invalid data found:" + lnsep + "same line number => same x, y and sdobs" + lnsep + prevnuo + " " + prevx + " " + prevy + " " + prevsdobs + lnsep + curnuo + " " + curx + " " + cury + " " + cursdobs + lnsep + "in file" + lnsep + name); return false; // invalid read } if (curx != prevx || !curnuo.equals(prevnuo) || cury != prevy || cursdfreq != prevsdfreq || cursdint != prevsdint) { // first occurence j++; // single line pointer x[j] = curx; y[j] = cury; sdobs[j] = expt[i].getSdobs(); alejsynpt[j] = new ArrayList<>(); exasg[j] = expt[i].getExasg(); } // add a ExpJsynPoint to the alejsynpt of the current single line ([j]) // a line contains in its alejsynpt all its related ExpJsynPointS. alejsynpt[j].add(new ExpJsynPoint(expt[i].getFaso(), expt[i].getSaso(), expt[i].getJsyn(), -1, expt[i].getComm())); prevx = curx; prevnuo = curnuo; prevy = cury; prevsdobs = cursdobs; prevsdfreq = cursdfreq; prevsdint = cursdint; } // sort each alejsynpt (increasing order) // the sort methode needs an array, not an ArrayList ExpJsynPoint[] aejpt; for (int i = 0; i < nbxy; i++) { // for each single line int nbass = alejsynpt[i].size(); // nb of assignments if (nbass > 1) { // has to be sorted // copy alejsynpt in an array aejpt = new ExpJsynPoint[nbass]; // new array for (int k = 0; k < nbass; k++) { // fill it aejpt[k] = alejsynpt[i].get(k); } Arrays.sort(aejpt); // sort it // copy the array in alejsynpt alejsynpt[i] = new ArrayList<>(); for (int k = 0; k < nbass; k++) { alejsynpt[i].add(aejpt[k]); } } } // happy end return true; } /* * Invalid line, show why */ private void invalid(String cmess) { JOptionPane.showMessageDialog(null, str + lnsep + cmess + lnsep + "invalid in file" + lnsep + name); } /** * Get number of data points. */ public int getNbxy() { return nbxy; } /** * Get X array. */ public double[] getX() { return x; } /** * Get Y array. */ public double[] getY() { return y; } /** * Get X value. */ public double getX(int id) { return x[id]; } /** * Get Y value. */ public double getY(int id) { return y[id]; } /** * Get Sdobs array. */ public String[] getSdobs() { return sdobs; } /** * Get Ejsynpt array. */ public ArrayList[] getEjsynpt() { return alejsynpt; } /** * Get Exasg array. */ public String[] getExasg() { return exasg; } } resources/PanAff/jmihidebar.html000644 001750 001750 00000000251 14000365526 017514 0ustar00cyrilcyril000000 000000

Vertical bar

Reset a vertical bar which helps to visualize frequency coincidences.

resources/PanAff/jmifaso.html000644 001750 001750 00000000165 14000365526 017052 0ustar00cyrilcyril000000 000000

Assignment

Set frequency mark.

resources/PanAff/jmiunassall.html000644 001750 001750 00000000242 14000365526 017740 0ustar00cyrilcyril000000 000000

Assignment

Deassociate all predictions from the selected experimental peak.

resources/JobPlay/jmilpHITRAN.html000644 001750 001750 00000000277 14000365526 017654 0ustar00cyrilcyril000000 000000

Load prediction file

Load the prediction file in HITRAN format to plot it as ticks in the pred/exp area.

resources/PanAff/jmizoomx.html000644 001750 001750 00000000232 14000365526 017271 0ustar00cyrilcyril000000 000000

Scale

X-axis zoom. The zoom area is defined through click and drag.

resources/PanAff/jmirestorex.html000644 001750 001750 00000000171 14000365526 017772 0ustar00cyrilcyril000000 000000

Scale

Restore the initial X scale.

resources/JobPlay/jmishow.html000644 001750 001750 00000000164 14000365526 017306 0ustar00cyrilcyril000000 000000

Show

Show a hidden data file.

resources/JobPlay/jmixunshift.html000644 001750 001750 00000000247 14000365526 020200 0ustar00cyrilcyril000000 000000

X-unshift

X-unshift of a data file and the associated pred or exp ticks, if any.

resources/PanAff/jmiaddexasg.html000644 001750 001750 00000000175 14000365526 017703 0ustar00cyrilcyril000000 000000

EXASG

Add an EXASG string to the list.

resources/JobPlay/jmiexp2.html000644 001750 001750 00000000271 14000365526 017203 0ustar00cyrilcyril000000 000000

Associate experiment to data

Associate the experimental peak list to one of the loaded data files.

sources/org/spview/filehandler/HITRANFile.java000644 001750 001750 00000003467 14046454633 022103 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.awt.geom.Point2D; public class HITRANFile { public static Point2D extractXY(String str) { if (str.length() < 100) { // line too short return null; // skip it } // HITRAN pred-as-stick (100 for HITRAN, 160 for HITRAN 2004) double cx; double cy; try { // read data // extraction and validity test on 67 char (ie not all) Integer.parseInt(str.substring(0, 3).trim()); // validity test cx = Double.parseDouble(str.substring(3, 15)); // Double for X cy = Double.parseDouble(str.substring(15, 25)); // Double for Y Double.valueOf(str.substring(25, 35)); // validity test Double.valueOf(str.substring(35, 40)); // validity test Double.valueOf(str.substring(40, 45)); // validity test Double.valueOf(str.substring(45, 55)); // validity test Double.valueOf(str.substring(55, 59)); // validity test Double.valueOf(str.substring(59, 67)); // validity test } catch (NumberFormatException e) { // format error return null; } return (new Point2D.Double(cx, cy)); } public static String extractAssignment(String str) { String cjsyn = ""; if (str.length() == 100) { // HITRAN 2000 cjsyn = str.substring(82, 85) + " " + str.substring(85, 87) + " " + str.substring(87, 90) + " " + str.substring(73, 76) + " " + str.substring(76, 78) + " " + str.substring(78, 81); } else if (str.length() == 160) { // HITRAN 2004 cjsyn = str.substring(112, 122) + " " + str.substring(97, 107); } return cjsyn; } /* * get current extended assignment * */ public static String extractExtendedAssignment(String str) { String cxjsyn = ""; if (str.length() == 100) { // HITRAN 2000 cxjsyn = str.substring(67, 91); } else if (str.length() == 160) { // HITRAN 2004 cxjsyn = str.substring(67, 127); } return cxjsyn; } }resources/PanAff/jmizoomy.html000644 001750 001750 00000000232 14000365526 017272 0ustar00cyrilcyril000000 000000

Scale

Y-axis zoom. The zoom area is defined through click and drag.

sources/org/000755 001750 001750 00000000000 14205762147 013646 5ustar00cyrilcyril000000 000000 sources/org/spview/preferences/000755 001750 001750 00000000000 14325453166 017465 5ustar00cyrilcyril000000 000000 resources/pixmaps/spview.png000644 001750 001750 00000544217 14000365526 017120 0ustar00cyrilcyril000000 000000 PNG  IHDR+{r pHYs.#.#x?v IDATxqYѳ# )Cpg2@@Dpni~IF_kUu!ϸ왽xG9Eb^s}硃/>Я/oPDLSJoWޝwǔ条 9~=JqoDEo]eQi/a<E} k?~-pCNC"M~s>0Txwr4d-|)4?qr!F#"f~ہ65k~s0x"77 #p< o|9@Nf;4/7/ sk@\I Ew~h^ߕ'5z=pMx"ߗ]0n͈Ͱ>DD3n:.L z)T}or0!" .q '9o,O=F^_~woy< 0:."Ncw;|i<'9K-j\{?< Ծ2ϻ:Eļ\vq:n~v #/x{34yB(?Ywf|'3Hx0k\}l˕u ׮BD}^_=?2)a<F^}20`ʋ,a<h~sޘ}""ޛm\̾75xZҼq'ЪI4_k+-xU gs,+ܔ0f>-W~fR_o|I?o|=1I^my>E\` FN@a<DB@O2A0@Ѽ(3`^J0_=,`X=;:9>>DD4m[B?|yg|'蘓+kW:<@ Z} <tƶuM`=HD{?=ʾʗX~g'}шXGX~c%W"ꙙ)ދXrgy#~Cgڞk80"xFgF/Vol;a<0*'|}~-:䲼Xa<0h "x`@^%Y.0Tx`0"#r,s/0x"b~r &c߈P~mD@ ^E|lS^J,O0s#uxs"y %96ח t0hUDL|OlUF,_$n5x^rUh0UyfDļ/\cW9">-"']i[btgI`r|$ pa<𪈘L ;֑|s^0a<??3ZrrU` 0F3.AF(8 `$"?;=? w '0Bya< @DLBM#xC'!|#xrQ^(=$ABx<0:""QBU#YO-i`IByh0n$"Ou(*p%~&f =(/+D$-K#_[?\0>)"Qo >G8VP~gxpa<#"WῙpAP`:a<4DĴ\c7R"Uyc^D,1t±50fxF'"yב|  ?u^7 r,"3 %_wG'l7 xUD17SJK+C5xWϝpc:/@Us6%ύk;/a<DĴW;@lSJo, `\#Pb:fܾ\_A0P1o @U s+K&a<D}JiRzJ)-#s,gs>xAs%*g <סH߄=‹<ύK;3a<@ODc#|6,E= 01<\H#ֈ:Jв6Bo*9o]xWD-܈A$a<`D7"01<H D&B'1<*_H~gH#3mK$|0 bxD xCD,RJObxH~s>6 "|$/(x`"b^B*vl+HFDLK/9-c!fb!u ݆"_U(sY804x`0">Fjۈ^`@3%_ gxw"b^B*A:V|W0腈zK$9oa<i'pQ*/wF t0蜈O)=~fC7\.W.WAWt0hMDLK _=ls%_7E\D,e*@/l@D+nIDDK _=3Sȯa<p51-WL`p)*sX/p-x"M`4U _9睵$.""2|L`ԞK  WAIpX%pS'&)2|00#Y"b^bꙙ_\!:0).X9 x0O)H)=&& l@>9 hi _=w@K+|ym @X.0}"tEM#uwx.ʲ`|0"Twz",`0p:r~b K 䗖 &*c"y&a< HDܗ0R*O)r/ 0z."%;TWWH߄S\@_<0zux8WGQM"= qZ<t0:""RJ`'Ъ\YO-y:<tK眗@ \qEZ$) ~f[ 6peH)H)}3k}jzp1-\_[7\0.("K \`+?SJ+WrpQ_3O t*9 FWQ79"bQof| SJr  1- ~ff.W 'wD<3RparA~i6a<""ʅnX]/ iJG gk  /A}JgJis>X c&`"\vL)-E0FxF%":|lȯ-10 q_6 >39,0hT?40@|JisY0C%`p"bZbB̆]04x#"))ǔfVWsK/C!"bQM?>[T.l]w@ 蕈ʅ|K 26D@/D} )U`t0N? )eygt0ΉiJ\)M"Ƹa<S?V@~et0֕ 6:c_}'缴&5(A-tֱ _ 6x*Azw"6nIMD4<SrA~m܂0y(m V 5 XRJM`U RZ WL`t%%\DD< ~f~W?9 i1M)H)= xs 6 >KEļU?1ARaQxV)'9圗UD,uoT\?*WL +9Vq@x*Atr;CI?"bRze Gy&]R.ȯm`܄#? *_朗0Nx)Aϔwg傼@`d#B ۗ zs>& ."=5sL)< JN00RR[F W_ sY>pՅ@-a<@ ]yPDLSJ?RJOx8sJWym\#F_={)="A<\@G&0"b^ᛔң nbRs^7@:hž\t09yH'a<@ ;y nH'h008y\ O px+HpAx=<.@\0 _.@ x_ A<EyOA\@W&80x4 ^!7~gb&@^J a<0rx%a<0Vxg QU4* 2 D`g`O`6fxIV_g?DO;)7=NLr*'1R<[k%F)ٴ$`M w*'Y$y% `'1S``߳6u1ƭKmϦ~mwQ`= ]B_>Q`oJlSA!켶'S!Եr?cYmW_FO֕eZrdvb<;Ye#Ļ$1Ɲk)m,rY`OK`(3V_=OrNr1Ƹ l'IV_9c.b<[i=JDl+xR$$G.C'1Q`= \rRtlxFYyW%I.b`(ڞL+hV1\l:x6V$dJl*x6R۳$$G.,%1 Q`L+$]}/E&Q`cJV3 XIIN]`c'1N)Vm端[Xz.Eۓi% ls`xVm:_+9)lڞM+GRjwsPM+$o 3&sPI=V$ >Ny<xĴ*Ŀ0λN Sx!U[Y[xqC۫c5W+R[c'cQQL+pbqI%mO~cP৵]=X ˴#(~b<f%d=XY!mϦ#%1 ?Q %XG1e%gb=R/[xz<X`XO(1+l(++l{L1`X`YS{J<;z<QVAb<vxx(xb%"xa%z<QrVo7"n/`{=Irts۹xx-5=ֽu?!_\XN[fZ_&yv`1,lx-b%x-O+>|'Spmg$nzZ-RPVXpT's͢ANWkq?/96D۳$V`#O2^zV.|R:4xYX'J;z7Ƙ9XX$`i{3 a8LRu~c̝ y)<$GֺNr1ƸuB &Lr*o Ifcs<=x'N;I< l*)|G$G9Oc1Ɲ\jvR<^%i{&hoS?ڞ$JF639J\1X&mgI%`$9c:)2ɩ< pd6X:b<6lSAa}}vR<^'i{@>SꑨM7~;Jܡ}1{Ex` ]'9c9O02+W}1ƥ#KIx`$vPv^yI\!oL!;c vR$IN]aIfc#J1ImϦR {Tsp`׼pQ`״]$Inڞ8<kM7 쩣$]1[yx1N$SZ$$]/1Dll'IAOm"x`+\\Owb<U&Y&yrvbq):`p-`[=Ir|h!Mvs#eo޴=% :p(`c=L$\1. l2j{F)$v!b`),$G.޴i{,j`u0h{di%`-\1.lh{F)`m|hp`(,$G.vo޴=v `tڴ=L\1.X'ڴ=Ir|hp"`h;K9ɑ l7mo;cx6m,l$cKxٴ=Ir|hpB9)Ϣ,$Gzo޴=vJ9t!hɴ=L2k)$c+xɴ=Ir|j;wb)YDۋi)@{zZsn)Tr48ɕR<:HOxLG%6n&a<LLm ͦ>$5ra<LYE10{_.gdk0 x$fE0?VCk+Kw[\쾪a0I_궻.xك(&mSU?'ɚr\ HrSUr`Wl_p^.%y[U`vvUuH\3J?n<\3HM ETՇ$ !Kr]UCe(wIY)^ۘD2,LnoPb IDAT[U~4\ISU,GU\[5pdIUvf ^wคpDc?|̾2WX7ݽ~8#uU}% ܎7 T.!0BI~^v_q8`eF0nxoRU`HN_Ho{oXt?/,$kb<亪>K"`,ZM Krdk,YۂX$K Ǫϖ "%y'V䪪\[:Kb<d[UCY`T>X>K"`1(0x fo{R`,AkQ<%8X E}k$̝ZCQ<%yH5Ll%y[Uw6MWUuHʘt0;I 6h_ꦻ q1YIMr/xx907.0C?|+[x7ݽ7Bxf!uU=.;`.\`(~-zݷF Թ%.!ֈ21!XU7٨"${Q<\Uէ$F 1 Ym `LFmQ< q$V!^+;q<S"\W'Q< qJa<5FåMLN$UA0iCdkM\0;:a<gd/!87a<g5F;Sx9^Y 8$$xInsprC? ZUW wxEI%Q<" q;݆ IQp)~c]d\$.}p .ptI>L`u>VMwzxJjWUup9cp4IxOI>GQQ4x9^Qx$$Q<_-5^*m<+7ݽ7 xEܹKx$UI q<0'Rx8hI^x!O5H+mX|W۪3)cUtg{\DUU\1|SwxNd\0ߒ6 T}ULRU7`SxhSU+x 6A<_Kw cbU]o\Wx&x~O_WՃ( ,A{2Få%0Axs1`D.ޢMRInDįq|SV&mU;3n+#`Ʈr<VDVHIމX!HrmjK=0_ꦻ,`\X8Q< 'X0Q<+ Xa<%&yLnX!>UՕBo{ob<\Xa<B #X$עx!H!m3(~c_w3b<vI0oxfN0Cxx2q< f&ɭ(eI0/n+1/xUuݟ`\ Q<UU0#xщfD0qx8q<L&L''a<D%ً,8Sk&a<Qnl6xq< &F#(a<$Q<L8`b L0D0Yx\Hm{Q<LoqkktǪ`6t޺x3l%>(fOpx3bLpxXq< NL%8a< `g 8Q<8Ą' 0DZx(VOpx##q< @|EpDxJr]UDW8~o(/'x1.o;q< I<8 Hx'$x&a<<0DHxO Q<p"xG|(81q<#(8q<w(83q<7(![XFIce&n~x($xQ<0=x'&J0&&N^ 53!VO(q<jx`uDL"fN0X Q<x`u*"O,8X a<hx`*mX$C  >X2T.d/V>ɵeK%iw Ħx`%E8X$a<x_ q<(x`DJ,0$oEiIFYYJr[Uw]ꦻ?0G.$x:̕0Q<"xq<0Kx`6DG!f'mk%6p489p11?Qa<0i76ptx`dBL0$Q<YId[UxFL01.f_Ina<0lbIL8tA09\%ً&ijK5F;[MU ̂88a<pxY%.ɭ(`v8~dku ;Sr88+a<p6xEg6u䒼M`1>vub<prI{X$W8 a<pRcI,N08Q<*'d[UxUV 081.tV_In8a<pTL`u) 8$x_q18${Q<UOrmiPU:c/(ǿ2ॄ$"bl x a<lcg|x9^<['Dp1x$Çw&\%xI(PUvx9%y%8!O!%ɶE?$Xx(~eZɝ8xtaߔd4%N}0X[\?t$1+ Š J Z V?>G]_r-i_ƀ0d|MIF.J_uI#ZT8x_%7DK0$]2ЛxxR䶪K3+b<$Oqgto00WB"ٝq<gn%YV1``Wwo3wŻdx$Uu9_{"/`ƒlm` `;0| %7`d$K$YWz`.̤u3亪>Jp1f"ɪv`l a3䢪U7qdLx$åxx$kFO/ Dox>xQ<3IR4La<LTmUXT80&(ɽQ<34%Y*0&&ɺ~+3p98&&ݭS$OfCw=`">nladYUZ.ɽ(` `Kot {H`jKt)FUoQ<0^.%w퓬e#侪ߵ]` `w{so#HUU}<ˇ0"Q^gl7Iq1H]U-t/r^00Qӥ7W{(`ݪKt)FOvX`\K5'Y0,ɺtGm`Jr[Uu']r1(ӇV7p2I0&EUj8$$YV($kUR>u^ 0 .@$`쒬`侪t8*33Kp~pFIVU ֥^WB0hwITcg`[<ݢKGi'dkORa<PuUFkQU$K餻 '亪>&Sw_Nx8$&*VppdIQB0)wIU Ǘ3QǪ1L/ݽS/pDIF0y$+5$y;-jdj8x8$M0pDʒj#WK8xxEWWKwx]Jߒ0^Ϧ. < 0^A%p}@ 79c$-Kx$,?j IDAT>t\gJrjIIr/Rx/ɾ4p{/Zx-ɃQ<Q,E `%8/ݽ3Cb<䢪8$Q0T NeUj5Z a<CإfNnd%v0A9*vXT6R I[! 58}xNp^ B}U]i`Pu^%]xA%Yss1Jr]U0XZ=MW40hWI6*\lK`r>tZ868ë_;)LM{pl.pTIF7RbxڗQ<, Q$YUgƇU7b<Nr^$,,xM.<.r!\$O}=x=.*x @U]%yx^E4wݽ/b</vx(%YJr1Ir]UW|k.l׺v<p1gK0 ?]ws^2G,sOKr]U%OBgO9ʵpAp,)IO Ru^(a׸x]Q.C곴x%V[;In b<ߕi#)mw? oq1o:e, W%YUg pdV|+[;p7I ׸J4'꺻߹?$Ypb&Y 3/j#Ҷcimk9%YK?3j9m\h/- V{I0_{ x꿣׳`@.l@@w )`\$F 6RAb<%O}$r1`d\%yP|0SIF7`DvxJro,0?.LUU};#o0/.5$k̋a<tx:r/3p^e0]z_f!JӔV-$z`ftq<01I.jWf2Fb<$WՕ^wݽ0.LHx]xHݷ&zN$xiT]_<$Y`j$U^tq<ȹ0bI.jC$#d_UW:z{1#(~6R\b<%YUgOݷ"F*Von0>PUzg$U0IꣾE>uxHŮ<`<\$O}yݏ>F (^6Rg0pI.j'xuU VKw `JGp4{/^r1`<, 0\. TUU}ć500@ץI70 CU]NjBÓV $:ע$ɲ:Jr/~aq1`@F'pvoQ b<@$5ت`8  ?`P.<`ݪ8$ λޫ\8$F0X$Ka<%`8t$޵xwݽWyp&I`4vI8 ɪ>FCwߪ \8ޝVfY H lR!`A! lYfdr{Z4aoh9%1x K[(,TpYn\HwUY0jx r1BQ0zwIjxUR0 $+U\F[gdSU&ި\8$&gdV38]U- KR-yE p&I6U|`~x3I['٪|\8$Q|`6wug(fgr08$ X$;^['k0kC8%N(ɽQ<~a<$YxUud'Iw|t-E ob< $7f/0x$ Tnvn1A?𾻿\x$FO6dA WHѵxw(x^0,<@WM˥I>"@s1o l }߷| %Y ݢ$U.dQU{N:?Ӯr`d%xJw(ɻ,>ux$bpdWU`d0{IU ʧv9r1{Qs02ϵmU݊$ sgZUU0XK(tJZr1$[x`$I0p}`$UU0WncUiEiܸN{x`0$ ?`n̍a<07ߍ&Y)tYH_ LoݽQ$0.s60$0$Zj$ sV40iIUg- LBdUU; .ݭd`|;3񾻿("IJro^Tdm)2p,0C$+SV*0I6UF7~L͓F['{dWU}Hw+$I?|GqSb<0{MCH)0F/֚GII0F-ɢM$;cV"0ZIVA0V.dc,{1cf< +x`j=g{J0FndUU_bx`t\h5WyH06$^ItdZ<&ىx`L6JX ^MU)7x`,4pR$"0$۪=%Y:x`Вj%XpcV0XI>V՝}w10T.dcpOb0$k"I0j N[+$YUW\܇~;04.C U<$وx`PWZ+W$z`h Hp-n a<0$ҍFnd`(.ɻ x`(40(wI6*0$UU a;ʲ*!-x࢒AKw:\.*(`.0':ɽ K2.&cUJ`Q.0$ X&٩xRiL6J% UwUY9S IDAT %k̎Y'd0[IN08Ãe)UL[I$YUWiއ~{ғ4$ag.$!;f!ɦ$9 af0f*!FIQ0_0_mG< %VR.Bi0?03NhVq0?0?Qm`^*8~ݏJp1ILCw0H;}0Œ|t Ge0Hrq5f0)MY>x$۪Z%Y(0&! ;*0!1u5-ݭb$[fCw?*xnY` a;|u <)0ɇ9K02Ro*;-]1Is-)B00V_f76{0.0x00ɹ# \}U= N]0l.%YD g pp9kWa a\p*ݭ$_V/pqx$Fp5{tW<0\\xJ>Wa` a \Aq 0 ɢtqoað]lq+3+;~za0s-xx"`\0r-x2x$+j<\Y[pIU {_wuWpo\a<\x.papa9 W`j<\a<\k0 …efZ<LcpAp9>p0$ٸW)\a<\k0=q] ̎`ׅ 0Ar5.0ȵxٻD% -<<@4AAC_ou#2Y˄䯗.LL!D\8 t|Ä0KD0 .菫0a<̵x0a< j(/Da~j0X\+p.$cs5.$?p-pa< 0Wxk8  Ss5@_Hp4d06T|GO`65a<|εx\/Oj<|As-xWx_RthoxIk9 ÿ}d1`Lg<p ~"x0\Q`8`nNWlP$r50x9p$ ;]&z&[IU/Xǂ0}>`0. h0+?4-a6 Ӭ~a 4a<-ljڲi0&qtf i^.БxZ%U9:-Ӝ$(~c@>, i|")Iz5л$oi(@}-ӒmedkBO6j<ȏ?lKxϏ?XX:Y$$o#xZpzW6 |MO qѰtx-)">ƳtbmNJa,V7."gф,kٝ" Y$l{s5ƳT\p>^ #gqO\m02Hypns<1EƳDx۬Nqٱ4x%pGYan 'gOMn0R@xPq<9YƳG[ĪF 㙵$(~cK0ZN 60MW5a<䭪^l`r3kxh;K5jJ,΃<̇Q3Wxj:$Y9s$gvfcfIje31$g>l)6I̎0YIvz0mi̎0lvI^93IUn#O2"gN>l`0 xf!ɺ0G`.a\l V\ #,y$8,"<$yMS%Vfh5<0gs-`Iv3 yBϱdJ3jeX2O%>L`6I<0HvzM`1<0gL`QvI^gpIUnx2O!d01Ǜ s$hc|a0%^$&=,ɶM?p5㹇k\bdmRJ=\buM[ Ió1=.4s+KWxd[U&\&xnZ<'Ys a<XxZxlL  ǵ\˵x0%YWջp'ǜ0kx`< IDATn`Z`Dń\$[UmL 㹘0KyX.D0?JI0@0Kt\D%<(Lado0o%yS`"$0%Yx0CIުjXǣ;%xhdksw&ɺvHߟ?!XGm`_Ia|_\/h*8#N$yMsG0h{Aa<vB߁$Z>#0@^l}%YWա9Ьj'okLOa|mYqۄ K]syM7N߶} 6>Jj|ÄJZU@7\o0]~'YxFc(a|a7 V&a|=zI$WZ XU@o0=~m h0!IUޱF@wa|[UI"o0IULa|;co"o'$F䵪6g~b?GAU"ݨz-9>i9ћ8xdc9Odׂ9;GG79Ŗ}4⬜vlyA9&J A-qH-$}~.=礁T0@wK)K aV0RJsѩ />xc)i*7a|N[rK)?0L6R6Ɨ0z-1a|IV=:iP%//?xлٔҜy0RJqsuPtKJ_N~`:)9s+a|ɤ#XtG_>uKH_>~`:)9+a|#XtE_.~`̰\"뤔%"/tD}E%"/?Xu%"/ulVJI_H)MGl[L_r[O[r8Y@RJ\Ji:"f>a| 􏞷w>j'/tD}g ?]o dJ) L_P)鈘`@&/.?808ɺR0RJ1[9 J_L~``t%/u A+$/ a|#bs!0x} H_<'>VJI_0I)MGlC&/a|0XN}P8@Rٺ B_ š-a|q@Z)9R H)Gı )a|1,}P@:(NJiι 0~RJqC:lO?|@q @?|~:)93\;V@Y=d!J)0~@ͦK "aoV-_3ac64C?]q d_jl#a|#b/>o\}q 7.G'4C?Gb4}"R-U0?3Rw[O U|)} b)T^ @T eYV#bFFG.+_PN=˲_93Z)Ҹ(8m#q`ugƘS+ƲKzhV=[)z"5[[-R:V .Est$wН_H\;yEs0`,6#B)ֆcy!7wߋl}d;{cݯ\ԝzʭ۱w75,G'Fpb@A{TfSJY}p7Ou"O½w?~߽e_p}+qtW> L 7ORJ)P[FV /nj\Yk|z3-STߞX5 ៙h9q0~k㷆mx7?κzH> +CzJr:lщ]-[ot[D5|C@M5ƺ_ojwb7vy1щVSwRJG,F9MJ)Gӥ~ jO|N/V(o=8>81@o%Moޑл|Wn_cqe4inhũ\<y 7o/qg7i3W9G'dͦ,d'x(cgcla<PHy"qb/>1%RZȲWj}l_xD̖G痮UE"o}鉩Hrw?|Q<ǵ~-(<{3t@o)@7`tO<|n`-pDxҵxRG6:qznI)MgYv9sPjw4N\x@l;L(Q<TfyRVi6༭1qzc-@ϯxc~_#r}r#y<ǩ3 :PG>x*%b7ŋ3gRJu(4xX؄[ M䟽>5M30wŕ/4]y omC>%jI-A`3m>S8s=5m-wtW˖xٔҴ0wn\WV4kڿonͷ6œa0)A`Clq`wو3-z[z%[v\x@0/X{wTcsqbc5Gw9lt;N LFjmq˺i/|<^zbu [,q~aRJt hx<4";ލ]؂M67)P{+uCoCmϨ7D@@60+3yhx`'?Gg|>5R7ƯM3d'S31(:Q<0@Z5RoR(C|z;S3qA04>&iK]nP;Ast$ޘ'w`'Nm .(pǕտd7NA?wONJiԿM] E{ )CGJ)ha@s-'&qznf0oܘkSWx483zGg\o#5\YPL}xz".C7ĸ`m6S6w4<ҁfCS0knJXuh(q|=χ7 P:;_ j4Rǿ)>{cχe0RwRGxjnW((ڷ!sCl Ci;Q[b@>a|o̔((й<ð-ơv?Q,:Ly鉩8>vd^!0?9$/M)MgYv-wCP<0=Q5w.v gOcznx}5W@c}\u%=;Q'>Ws5S.Z<-0Փo\HP1WxT>'`\;eyPJŅ sG|_E@՝ҹ׺ʪRβR]O0eYVR~:;ݍyxt|AIARuZF710^_TL?3r^~u)-]ŕպ\;yU6zy<0@==eku}چ (CɻA݈>u=1=l8:N},ND+k_o-,NuuClB_ ِ_^]?lx%vtW+~㎰n_],\ƿ\Gm$~Ѝ׍l0#+ ݧf\SB+nw?_t->]Yq,.qJuQ:M,Tc] (Bd/݈_^KwC<гg,{eR (!]n*#Ez%r`'OYI ҍ6Y-qru 㟏3x` Nl+u@᝘lǙUyǟŧ7> ،,8񿊈cx4ƺ]q=#&Vnݎ^HP`xs~?O_xXv[,~_i5""Zxdjщn,o|}x}5~r#!@5[b)(o^Ȳl[0>4Q˷Ԟ~h+ҍxNl;@:˲aqTHɟLt77GGmxc(8s@ &L&-ݨ(ߖ,۔*"Q|3-G\!/|p9^T1 ݞXԌh dz2˲iu 㿈6*wm#@TggL5 >ŕպ`{j&h8?XvGBeg4Z)x8>8>v%vefEX޻ \va ϯ,[?g (@M[Ou&h6(K77z?`ؖ,)-UD+ĉvwTc0JF0X29d@sK7>8{'ۆ=D.Bz!˲u9ڄ)x&|ܺ]n}}utW+~<$WVoƳbK<ӯ,[)>"QJ _nO/|T1MsH\x@4GG y޹e@q-gY6^<àkewNlO>ס;q#[.dzDJ)匄u7?+7SA޻utW+hեZ7XmZ)ƃ< rv1~vjw+s6hFc35g+εѹjP6Wm@rZ7?;̂j8L GX%<P26W0*8R|_k>ybSjoS5no:;Mn('xa|EZxѹ齻cS6(>XNJi){ǯ Kst$6b/.u J|S]0Cv]K7joS|wS;ͬ(.W%7Wx|O/|?xՐ{鉩hP*UW=ʥksM<̃Ov=hN6fSJU~Ya<@ ,Ə]w-;˃ϣDk9{c.}$کt[]0΍xBXv@- Kʶx5gO>[g@#o{ŻbOc,NM4>g~b< Z Bb ir@:5YH ovl\MbZ0SIzaLk8 ?qXY@!_㿯P v)I2(~v? ik|}lTRc"\saW{1>2r_=ذ P +Zby9=xYȔ" wZpV^Fؘa惍>8*=љOd "@`Rd ObzRA>x{^ik<@ş_z̭G6x]+ޔ1^[<@dt95L*Q~o Ht'v zN@j֣zS{w [1ypW< |YB3փ gPjVBzXG}tfѻm@$Y(k O:֣-9ܘyrS[VA $B`tq Dbf!JZoȍ  p,@hϘxH#[yJёpS8‹ C``"1>c=?{aE@s30PNO[<$I)խGξM8Djfq6wZLJDYF09=AU z0N}xX[<%,$Irh3۝VXkYS9(Լ@1O۹3>8*@%2]xxzHX4eO)m]Jy7[`|!m5 n̼Pxcz*GG)zx jI.-@4za3NI30L+ 6;cm@)I|fO:VˊB|sQyhkd͎ҭGj^Ƌ- OoŢ ʼJ8(C&PrZOP)@ߔKSբmVт OiV`pߔ7NC[<P4I*1h?K62isJu P$IV @EivgjdJx4x I[?ޱƴezs JcT۶/],̬@n4)h l>I¼x&@V[{agTbV N˗B}i8|Wa2E ƿ dxcz8ѹ9M9QeaxEYB$PQξ򥋅O}l$pS>8*ߡ1~ swgVOi5 B񧒶.ϥ1~4T;DԘR,L[StG[<PSI,>^<ղQ}ZX9xɚ9[>+̬'(D(@n?޶}Z4U9xܘynBzX˓~a8` $Ir=4ڳ}f]3п3 u w,̬}(D8aDM@҃4fk㖫O!-jEcpV+lv-FA$Y({;6'kaN4Ї)ԧp>]`f oۊ>iN|i*:DoB@ ƟG$c=쇵֞kyip8 PZcodwwKއE xswGV (+$wڇ‰'f@nP|;AT$ 1h3"w3 sa݉zF y =[lLМDCbgUvQXk'fz۱F0`|VSyHV rT9~9y^XlL_<q`~{|vZPri0z$D*mOOC2C[?K_ٟo;EJ% /O_ o\ iZE/_qH~/OM%u;k@pZ<|颵A,Z s0lm6ޘ: ip? ? 8$m঒$Y1ކ`<;~ 1YjLT} oik~7p$ įOC}t˜>+3헿\^pgIg?$xGfjc+#O IDAT{ۚO>LHݜy:Vs@0Kk yCߖˍF5{·9my$IG0pWZgEl}Y9om;hO .><+'hN^z> iFj/0˗¿/R@_Ka+ q НSoXh&CmU9 fT0_2^ z-zJd!%O(n4,CڇG]sJ[wק,7.O_bz bCF_dwZV (}6ޫ-iS:@$f ~l ǟd1Pow,Ȥx0ܘiؙ,6&ךXE1>۽_١\k*[tn'; ikwJ<~'? 7$Gc|" ȭl 07?wZEqЛWo~4k倘L%I] xJIg҇sq+ԘJ=s@,P||~9bx:q.c0fƟlIc4TRzJz 1br=݈*$xLɚ  ex "O i?Yslmܲg,5&( ǯ,L?BdP05ؘx:`|h`RW^ ؅!+3ǭ9؂#5P˗.>{U3@c#^{_h{@$*[0>J}ekNpeSE}y{S@Ped&=_$w4$IBS@;}xd {h/D;0XK M }`{'^V"YH.0k= CPI݇{O~F\m(4fkߋu4`@~騋 >9v*xzYkڒy6_ ?r6b(I\` J$}p)]$}+ခX'x^?KW^nE|owBh˗.Z5P49`M> ZkbpWFL/fVK] 'k!i@'~|ݵik<@Ɂ PJvlldwvNxvZOx;)Z22$B K0>!#=;g%}o#It޿1Bn*'B5'k1½y [FxB[6y9"C0^c<CK.eؘ'XkE=iT.WOCwrl_?4[_7m1,t @I ?vW XHc=* i(>} k}^P3o9jI$Lx (('};ÕIC]x鋙/L&Rl[6;^sy:rτO$+9 nЬ_v6şnǡ)E7;ᝏ7Ko>ã̮>u ^s_m>:l-5& ٌiz y?Q2"(9<r>ٽ_>""wZ}#w2ޢCԁl|}*`oeryf?OxfB+sgމw>ZWJd!ϥ;P ;݅-eE-IɋK3 2T`k9_ -\m<ှ-6&- @w]gnolgv-Bϻ*Xn p$ݸdNYow7 2Rk9^ Գj/sp/dPDãN2T}gU~gk:@!{<Ї%=݉y<U{U6;RerT]n<[n@~:{t+nfpS$In< 9^98]x0 ŗe{CQ\m"]Sc<^s*Nid]`|=+dZI\Tfv P| ؃Q _Wr'z@iϫ1>|ISpoZ㡸|Oڍ}DQ%7 Vn9qx*ݎaJk<֕Ih2`|nT{k/<ЃooPU}v%P6Ii5^XlLXkjji}t[_gj@|PP:d V|\mܪ@A9,^ղCtŁ@/W5>Gv'Ԭ{y$ݸJY`%/y0>p-k@8ԢQXowbƄmʦy(i jX(V^>=eOb1 J@o* (3Tdo @ڍy<c#aeLnnN@*kbI_C/~9+>:;GB̺1^[<Zĕɚa^rԘ7f]IgVY+wc`<k1iw@ U9^@Jϼm}c=Ry_ ?]~YJb@p5zK@/M_οuriv\0Ji wԘs0๚u^V[{RW3}'}Qy(g0*ϣA(舽& [O,w@~r`ۂ@PZ.~WvK9ZZHno=h6>=ےc6i>cO;G K +źO!K'?Pۏm+W0>IY\8%a[^}>6?= !rY5ghnj,@o~x@Z7qX1^0x(U nV'hg+P|H[<f*Y>p>ͺpIl{N }mx@21|0/` z؍ .cY/~P}pڇGDsA\˓~1"Tr / s`چ9TH~v:Ojk/i23`<mYxȝ*zWT~>Y<^swPmBId(Ya/s`ZmՐ/Z{d@( ZCɚ+uH sxdQ{hN^v6Y@u D0x0>IA& i,5&~PMBJ|]%ڍy<` 0T0inQc5f=Q'PCɛ#q C=F"Jfw{b TP<@L ca3`6;=\΅<ƭ{wXU!PB}0~].9^g$Pϝ:q98=,5& H0MmPJB25t0?2ڵ=F ha]=($I=9y!Tڋy<ǠW:E%ەz-T{K_@A(lOD[<pn#q%Ԭ_=v6B7  Z[lL:gnZ}ϵRXK*AÍT;n\=I Mɐ ˟r7pcUl7bsO(XE\Rv^n *aQój/Ҝz[k1`kWy1׬1@_CL^'zg@YHX19%i-'2U̓J`C/ŮyW&ۗewɶ J(~zs\d|MyP;BYHx(j|&} _Z֙P@0*m,r};ٶ@],VTKǍI Rtuq)b<@MS1N`eK4@ϴZ?:&&&:?y5==}uu I0X}UՇϟK9g8iO2::}wlmdd$΍6?ImmsY[ {eW. n]E!q'  ܻG>x.~nꗗ? 'qOL}8@|@_+ @FOw`|՚0[ݪ{T~655{ۏyϟwPD5bp+C_ hOZ1^0 B|Ȉ.n`~ffFe| };~__SSSQXy^h_8m9<r]f}-ǦNǝ4w0.M_2>%6#jk&5 @y57G =zdH̜dnZڞ$HIfgg?U qt><\Qhéb޴߽{;sanp>6QHJ7> 1v9$[w=d XIm4[ALMM5ᄈ $j^#v 9Qvq@Սv^tQmE8a7S!P(C0}W,\uCͻZͮ]o:t16A*|?}j 2@/;[(EVkPUc %;wd]JXZsEr};b|7$, % "D!Ar@N_?Ix3'*%;/伜FFF&N@>>wBqXQ,xbТw( fa.09|)ٶ\WH]0T4.e\N6(͉&9' ƏCRqdN" ?779-,w^'FKH\1'^*>}Tl,26Eۻ)7T,! sݛ! üJwPIRU T|#LaW^ W3s!6Fݠ|P? q?ȷq/jC@_0x䚀4kR~(_:vI0E7pDHѣG/ aAVAFPM>v9զ^[r) 47C|ٳ&cOtNKD7.BXO<666lbGuh kQM< g!ձwVOHL9FFF sk/ .(0Wb@N6S,fD$*&>}#ܹϝEy= N5"[foH_PA`C7Sn|3J`}+-a:kkkd@} \:|?"$;(W,bXV%@O9XE~vv$" /w Ew^o<LP08qm`|؛b[1.[z"Q>C'O>0 Qhqk0`[ A$Pl{) (S/g7`#`<I(nDuL |j*)@ZGeKg6;;k`VT9ΣB6n-N Cʔ#8C0<> = ,Hqn8\ڧOv*PYġqҒ|ɓ'g;J-` @̔C@'xQ\#0E0jZz`*Ȉ΂C0ҶζMLL1!M_JyP+C_TL9|BejjJٳgM5 eǚ7 ' xnsN?gNG:]k<(ō:(U#0S؋{Ԁr񽃣8@!r8 AxNL (" P sss٫W:l\i{W/h?dLW.T+C~< 9. |kkkF~UHYO+K< <@5LOOgO>3R8u!"RM.<{3w4xN 8Y(y8[*Q-&pBTL9u<9X[c>T{#V.@Egx" A+ lsisƿ'U%8SA0(h65596kPr>`b<VxU# +ݠR611_% 'aOl_Jmoî"ovߧ8( 8Sػ]rRP__@u'/ RE?8觙,`$_\\tx{ O]k+[ɶ J | 'IfAX[[666@ꎵIU0F85Bz(˝;wݻ asss6#U8`0`|Tܛk0d! PsFPjSadd${AŒxis{#K0&7:rB)IŞ?g?߳r@'oPS> *Aʞ>}---e Q݃" `#qE0l ynh!er@ٳgM5Z֑G 7@fiPR333Y.vcr̓PBowMup4rzDyC؅Õ?^@R| t`<52??ߩ777gXu&/ilt}#Elkz&/c /\;_?X[[666@m'?m/* ŏ=MbjjStaa g{9؈F,ve諔}@b/wRn3=-.D%؟ O9 9GNThl^^UvΝνlvv8$F4`|k}!{&5j(Kb*%g1)`<TOa0jٹ Pa0jQOx'OdKKKBVld#MR\4=DgϞAnqVփܣa>92,tTx(*^TΉ-6R=gs|;b<0b+ۻ)7MrĚ=f5#sGUlddDxP=&y%YDP&qS1֌g TM޳~Y9ü1<P-G=TݻwU[=" @W:[~g|  $džw-KCr(Ng {UxHґٳgك H@ Y9} mN7>4ȅsƻd'%PSk [Bk zϼ#+ qiC4^/655ՙݽ{HQ;/w jAadrxx j%y@oN+{0j:*?a\'Ot*%YzJ q,lii uv!(LL]yVT^T,5!afz%?C)=y<jf*(N @!+j,4::y#KPh_8ϟ39'lmm͸zĄ%= T<Hҙ$f~~(&dP իWg^m ׆QD0:Rm*\Co.k)ߧr@uPQxqq1{Q622b{.H\<Ƴo<C |1v9AOL//׷m`M9G\S;;0bx*sssFq.;$Lxlvv555e(hffsO m 'sQUw]+C_%6!}Y@0:Rqj~YEa{RUmSw`|R `߿=y$1=:GHW< z*7JTև=VZ[`S1>Tg^/-8;:uxxޖ{>U<Ge F'0L\qR-:Sucp63{t9 y0R(*~>H;;wt3oJ?};b}y)@bguu Eż-N6o ??rL } 88:SyBpz*zo18(0>>K`+(B# 0@q0IXyp<,v}l8 l&68lmmM?@Nw~sQǼ.lqq1saH@xu5 ~:t1@>Y18k3{t9ZVn]0`@FGG;Usss 1KHW>}P#V\w74M9/t}5>>MLiߔ|*Tx$BQnjjJ$*.H_j$O@MRmk-F ɩDLY\kp:70_+ Ə_ދ8eL( }`q 'U/wMsY¹$gqUBowMu*P;89K"'T\ 2rTG ?6P O*^#%.8ɶP|4J=DFGGq=Vtx>x '*$SY9ﰇ"owMqcښn-% eK" ^r<@ͪ@(!$b*lS'džgCF5rXMLLD@8wP9/}8%xl 61H/׷Cgۻɶ A.h8ߕr@58/)C‚hxul͏{ ԉ`|{C%KL9>Tx l@LMM $.(ʖ EcgBhuuո lńi}G= 㺩*@ZC7|7pBϜAi[ 4`@E0~ssЗ@ \2hPLreMUn,8&xA+s#D6?$'%_}*e9 P2b#0/z88IsqqQ("hn ]sssysϟ%68%U`e]m~r~9!%b1z Ogb@~H۽{yD) \V5S1ݔ}#S2'̠GC1ߑr8 *ЇZN)Bpݻw@,2S7utI<pa. @~0h###d!GܧuO ^N!B333B!4v(A~HTg#F]qubrm6Z|x. ?$E8K9ҎB||gjRn { iEҸ|?Hϝ;w:ME$p uE0l_2^o'6( N9#. %),_2)G{8QTD>^Y|κa/(%Q$...f Q1_ÌlRTL.6TՇ*pB4w}T&5qMz,'@wB ?ԭ<04 '^Ogeff&[XX޽k  pƆ!~7:5G㩪!cW@0,  '5%˱fTۡÂwi)c&ݟ^70Ay)ӝ;w:k/߿0#znN-09|)l^4(U?bOKRD9;Â|677;N7uXX>^0u?"鷨w/3g8+Ћ SeW\ ~ \&o[a.ԓ`<@x|/XK###q}mll[|-6fK?mfCSU7. 90w!'Ajڴg~cR{8,x8FGGU?޹UI|!iр^"ly<ǩA/m~P99ub*ʡVߥ<(BP~c}Tŵi6/y||\O<jӧO_v' GTYlnnf?cv믿[XXp]QQ!2 Q7o>|ةg133ݽ{W$*6ЙK/wgsɶ \Jx>ٶ@:n'T_xڄߘ#yp~ TūǏgn\a<'*ǽbbb"9x 3owՙ %6sc첾)ll j;}= ]SSSt(7axO@c(IDen>*/--&BU7HH3M/<8ա:&OB_bi6>>n a`Rx?m Oo"(q-//goξNexف͛7Ǐg#b@rATz ϟKm`|{ C0f41s߃8 jÂL E7t^HDuccCqx2?? ! ϟ?Q\3޽{=GA<7.015_/t~M%O\4]Y(7ԗ`<@u*~ [nuQ |uuUGqlqxB/sP8\Ay!-V銹=֮>qUm*5[)7J{)*41s MgR(V5jSllXVKKK38\!;QE~yyY򅑑NHXl҈9=ʶ`|+BT%CVdje9h*U 8drw@!~b7A@D9t,OE9n޼=~X@߿TaOl%68Ћ[3=_xJؒpd)Z`0Q"xDxyv֭N > 7| onnyx 8\/^oWUB\ު)=41UZS@*֧O>V+0s " 7[ڟ_R'Ў?5GZ- %½[|H͛7=Kr*ӓ:/?''68/w%ɶ_35]4^wz,4Q쉝2$/ M/bK_+ӧO_u0?e4Al^h.xR' O:*8r(i'L6>>n(ऄiOfv?6>leCr8{rxTt9\p6б{iaK}&-Oeos@,鋪:*I 4P|CE >ŋxR3Ri898\,ǜ>Lccvo߾ݙG30| N]}\킃F\p`,CtpDro@Lj/N\XX0 #OU 7[n9;o&uVB\cq}I}r\dcw4kܫ!'4N#"hH3u/T]7 /?~l<dzz:oz7fKKK |Э,߭*iR1ա6uZ)M#dLոfi"r @>}lZ,g܁(O>5 U#D,@]˱#4M=mbbs@qOf截8;;9>>n9os_e}&OXIqW6>vYwb^-v35]4Mz-4!w` R~^~[O>}`xn⩿v⩳x[nekkkƺT"(???y/H7pRovKɶ lE8 SPyrI\=BP?V߃xڊ*;ᕻwz1"~iYADq0M,LF5p!yLOOq":t1k+ۂpq!FSX\4 @#Dh4So߾yaa?b1/T-ݐ| Ӱ_$>CI ㋽}r 8C0\4yI9̳FȈǏw>X\\lzW@'/oݺz|MMMMeMX@9$]?#ƛq+[**:&udžQwɶ R!S4µNUwޡ ܫٳ&xfZ@DHtffLTgyf't/,--u^ iS2::n$ļkvv6o~Dωl "OPI0q-]-T bgAg@m}}?T Nv^: ŁR=~b!+X]]c!۝hXV5 l@0ؖI`[ VxlF"U%&˵e=Ysssp/q!(b<}QQop\"MU2x"``y/fgg=B---uGuuǏ#IIq) 's=V4ZTa_r.CYډpj/o޼-,,4+4"(^:T;Aqr5#N'Em_ RXZ|Rx#-X+vN{:3T/ᢞܧE¤n~sh7 wG*e H{ JdҾT$._dBSc=4@ c3w+-moo_~sUzy꾬R`|97Z|XA 5@E_ 0W N@~o3&qZ9^+^n:/{At_~Y@\\X8)۶@[Cؒ&!_8M#hhxvTZ_#v;~nȶm4k*/F0j4kԑp |+n h7xVR/U%Ϟ=`.R8~6鐌ܿ_ Z(ݛhfrgT*b'97jG'`@{lnȎ{4zsX,E /vSç\A 5D88Eio|[[[U7.|$`Zowޫ^ Eeż(TEpƩV:*xa 2W@~bX'w8\1̻@G0]z@vƩBO<~~`Relg+^g4 ĩE*x$cTr `#wŸʜN&z}] w^S@G0ȝ^RpӧmeZ/wSakk]?n~mh0r#HA6ȕGK2g/A?&5f?f4gO6|T+ぬ W+Uu!㝔[-tGT: y}| \.۶,m4 F\1j]*9s}P-8%d+: PER`UEg,ЍtroH>|}/q#vc,cjob<-zIAX(C88ӧ6lTrN .2A6]k:|gacX'W/M/f!W'c`"23O0ȒEj?UIr |.mi:^scE)ݍ; `| kd0t1)Yf I3gq_ ofGD'KT*9^_:^sjᓾX2%sbXB1|ڷ9QjΎ;BN쿌1L: N/db5<0V?6)P6s<| __ɶmMޜinɅk1`|}yJN\1TΘ?4EX6~l #pϟ D 2y{^(p{m5۶Qow7nTh@1 eGQ~a!'3~`<zBx;e,1 fEs+9x +"Z5(fYgoo5ӧOR0/E=(ݍy6sm{y.@##۬S5{xb4g]!h :L6|Tぬx@B_sj)p8lgT_y6^_1 =#v@/R5ae~\sv|Wȁ0jYOx UsÜ |I ƻ1p+S<"^2VɫcP,kۓsn4`| kTI7`|sP^ `3ś c N/^@*xvFzZE6׾ϷqԒk؛?rn4eTh* 3L-C!~_p0`< -1/ucIϒ@'UfIn~+ }m-bD-l6#gΦ щwZAS.|^JÂ-PƍxHޜ lmۨ5R6Z"Ta}HzvdU웵W7%暋a PD0Ȃ ,ubΊ(]A̽<|# Ͳ>>ɹyB1ވz!\CDsN @x x,ubΊQKp_9I/et<9!F$[*u9{6U[D0hER0^8tږ 4V,b|aR0*Y!ԝS |TS1XY0@bHk!U5|~7J~ [z)?[@z=sD1<n s T/"y!Dz:#`| 1~ y8/z P5ZЁj q#摡 KWٶ "Dzb8G:$`<˖}< `<@Kx4`<@ZG@+ٶyer=p_ \`<@KXLWVͲ]iJgڊYAPD1X*b2a~.B0BZ^B>~2 7wRPD1aYuc<7l6ˠ%ƲzNן.B0ژN B/P-abs^moD1a>&#ѡ&Kw,y;d2?x\leS _"ZL0Z 9#۶D)HDTFQm0yepppQ8 wQ(pgc-۶7HlxGxGU WƨC\0>yep.J08z/몀s>VS z,u"`C0Q91@gY-i69 E aʆáN =0,eMޘcVT،!U ƨKR|[[[M,9YZ`:fĆx^b|ݍz#N%px0W!\c4զ*쑈1Ld 6`0ecE֯0rKB3nxܙf%%ډQe**\?1 .^.R0ɓ'ƫd9眆P1X]Mb7W31P?)PeJޕr•14*쑈!\`<@ x4ΎX"G9Q I'щIK{] %nahd.yppPpkxO0ETs.8_ޚ_Tڌ] :Pߕl3Nkfau]s7~.C0(4 sM/POE1 u 6|Lc2P @!/}F}d.;]jqCxinZǝlfXrQu< D0> ẉp'Zj.K_ȏ΋b<98ۓsnP@(Ä(b5dA+w{+pY-wRu("_LEn~o [TaJ6Ljn]n M,@&~ 5QxqCxy.۶T܌!\ETJ\D׳G"WZ¢y|l<*Խxk^Z,7=[ xwP->d2L6|TB:|щ>*bsw~g04c$~c*N;|t(cPy1hā ޞssq:vdM1ݜk% xHz= Wc4cs ;/~m~}%Q͵u}m@C`P->`<;yi=s \`<@X y} ^S5t{5QaPF11"!Md2ɠ%;yk#yEW!Tb|:jձVyWn~mۨkb@#pl63HWS5|p~8<>=U sW ׃UE^7W˶mT5y#a9x ~J6Yc=5~c_J0E,pi֖Hq~*pwFm#P|m@cW14 0w5W*g1'N۵ W=zH'UZ Z{}%vQ[*@sXc!8G4c4-?2hInRu> ~*xr ng蹊FV~n.OuT6׾g6bblmmcrI·1VqX)ЛD~2xrlbYpH w]}Tx \v-z}|qH̪Kxش`| Yĵ!N2pk8^K=z_*d:8VHJ#ԭouG?u>|ʲmXk!ϗ.b4q!"Seh!!$UWZ^sQ*sÚM#\C/y.'f|GYz {"e0 D|VoggGoUfE.J*$BE#4G1M}f.V?"bA0҃p<g_wPB$k+?mp9G1+\ VCRNm:69D;Mk"l66|Td@,K8Z|pY*Xg6rR((͹ IDAT gɇx_wϹ&b8 Xxw^g0~Tτ\wnk+명ݭo^d:ಬ9,C&cA9w s,@} )v c,ۓuikٶrlE^9HZC0>`0hG m-Xl^E2ٱH!PO)x=^Y6՟msX+k+uh42ktBo{7JcH!@xi8i*;>_KDnkv!`>(`"7[esm]PH0E0>F 6|TYm8\V*f1np E0ȒJi-.,zWrmPh{WB1*N/,@Bl638J_~ezB.Duo[ƷwBGm$\CЮ=u Jwv10ezZRӧmJ d2R9C\>۶B&n1w%E9p8U=`yTm<=sQ?:aޞ 7wBoNȴe@ͷ.(l63g}F+ pAgg$dm:v?njzYzR-H6׿Ϲy\5⋼>R1L@)rf|c7i6sxtI*Px {[d/t>}:?eKQ)P? ל`<^To6;8~s HtTn.B>;l7{Nl@R0~2{G+%T67ܿ_ϝNt*79#Fn"ms Uߦig?f5ٌo yejAfH'o Cҋp@xpptn637¦1K0>Pw^>p M0O~i@gY)*RT"7ms]0~_ R)*+h4jG4cÛ)xS>@ZH-t5Vgr0=+*}7m~}~mE.,T"m3ߋcLl(`f!Py~*99e @xTg~/$J"=z4駟:[[[T'7*Dnߵ 9#U\!P 19<\͐t%E0-UC򻻻ːJyoO .i˸7[e\K3' 鰎 `udfO@^)gYɝ@l97sqCWxy.۶ղ)F UwU ԏ@e s奾;-Dyד1nsMM/ƓpNV?NkONOoe{:«w;_tk [E:p8M%K﫼SW^y%`|}9$&4FZ{ٿxCNʟ%,sZ49g߇CYf>?7z#|E$_W〓 WחaO<M0hJdiQX~<GX~:@Z4~49$AZF]>:kHƅƍwA2n_Ƣdy$C0jR Ɨ+oڻp1$`<M0hOZ8 ˟}4LaXy64!N O>5{U@0n_+k@K!_?i̺]Ζ-j3Çb^?c1" ^xѹwRH(| ^ ʧ l:|[ ҡ|P'G>w6Tծߵ 9(Shb` ׋@e sY 4҃bu$pC>0`={fs #]{}%w7nyi_JAkC2ե'1@FJ<۝x*fy@4,w|vvvo lf&z T/*C6ˑxRz=EjD0>yB ƻ )-*n>4R۝O?W7V?,/4u<сz#`|{#S3t}U2 b5+U9m9qC:<|\?|)UqKv:Ux񢳷:w6n/X]8B{pc/}]l tkzhM"D |]Qh>-`ailx_(= _"[z{{ƭr"#Jlʟ@e s( 27ԇhb8\ `Rv%?1a"ׂtK4Juޞ^tgnvH}ag/h=H%bt+\ 0+\2W =$jMJ0fkzU7Q#:['22/-Խ r|. {x1 -Tw//'op0NP8 x6BukjM;13:' W!gccCS 2u9?S {t*'>U,=}`|FQno`^Z R]HЊIpq8~Qa@!{*UP%}ݾzŘCn,:`ׂ) 6!>8g/ {-.y<}(0 $?̲Pya3> ?P~޽M''yz 9TN6.Usn"kqZl:7aqs=̎9"}qأ xAeO$F=}xCE}`tM}C /{#aP?8PbPp  D0xSDž8<s2~:믿IIhk!2 'n䩤{qBm[+J^3#\&K0e1 t{'x/#;%$,o-UTwbgn.-m.qK0>{9CA!抰,//G"̓`< aaǏ Q%`|>GaO#v0O@mll_vݻ8pdX8,A{it:Q|Ȯ Kк\n,?C0R"V5q!"L\IQ+-+2`pz0#]X\|0+yC6Biŋ诀|ڬ;]2 5CB->`XY. c/MK)^zC8i2.qgyTGU9 I8>!}}k8 ͫ0!* u+K/dK?^Fk_R?0@5ٯH:V*3񝝝:\j0s_jM8in;ɇţClmllhK> }繹0><,^6B9zɶ H_X3{*^zTCh;N͞Df˜hԽqFop|S8#|d|qŋ?QVS|*ͥwAW HjG5sLx R٬eq Cp<TC2-aϕ* ỦC0>j8<+)8pR<}.ս rwǂ C5 {v.9пfbQFIxO&9B8ɓ'V5믿~SU>| P%9GZӶx]7zpP9#q(V^.<<8:\*6L>x@o*?~8{EV&(@wzxooD ɶ,\nPP9 !>8<#8֭[ |{aKP NiPO}u1ٶe.)FG7p~`0ГTΟ1CH;0JH`<)Ph8UbPG777-*G Wz#^ƢӸYR4naN^ժ_b)08~?>\?^xeF\ Ϟ=˶ ƧkeI(>O} ̐S|l6(`0%>՝PXh~FQ.!I0~ψ^6<޽1BHaB(~}}]Ïf t9 )7(a8K58%oi!'㏎pF!yΝkZÇ_P<|˷K/dK?^Hmux]7zpQ4K5 !Pb fϗó!Ha4/,څgћS|^M>,E,H0kBp2or\T5>E7Uj e'Lp8K\2_?s x ‹'Ot)wӮC5}|*m[༄^|[9VT&OKuFD[ʠq(,1fSxc./^0ϑ\D0`677[n9-Z8$ŋ!#;;;ښTtTηtBͫ*OCo>{* ϟ1CaX^^L@<)(@x ۺ_}{BeParIʒP|.Sao|a9TX740:\*P `vBscc#w%3֖EL#x8#p)զՖ1CJJe8v.39BqƧ8Ϝ2H_%f.ZÇ|X` z{%li&u\[U(3 _񭮮풣 E) _PȞ$ ?@Ν;>S8%_#S}8|+K )7vn\nPfBqx etan8@j" K|{{[ݻwO B P}ĭ[a~{s\W5>)cѧl0%'`_\«qc\%_4Tx 5x37>|8">"PM!6 1$Hger*OG5w援س\Oo߽ Ǻ|v 8bς@B xd{{PUwww}(ϟGl[v*t|><`00GR}EKu!q;T`NǃPwf/^~5M P 5x(㡮+IV/׽ rFnP*!L e1&qcw3ρ@aAΝ;Y543jOMy |NXBmK u\nPQq!y#Mwrh4psg>R$p*cX``f,,>~x/³P79n. d¥wTOaB8q7d\n\Gػ`c|3 k #߿U sS!tɶNn8`HxxQq0% ߠZ^.X*}elO0>sM< Q!^y &e oK/$۶XY!LY,26qm|9HJad!677aMyqݻmll}ѧC>/W$ٮ:p][TqSK2Atȡ?28<(I@lf<!(<@aEhaNz#ʞu\[Tk~VC2m⭮V*l45x@Jl($ 5>oz樷/?5P D"`|q¾VUKF_pGqc8d?::PR;wNc "85nllJHa+ɶTn0Hhu̹RC:}(sNq$*$,V[neϞ=33h4Ǐ ?>>͆ HooF/$۶:b4j@}t J Pw}3<mmmս+T+6Tʁ@dql>{©q[GK<X4s HPn0 UU<B2Tx”?/{fOa>R'EpJ~nʞ={9=~x @loovww;̛qcQizD2 .(Vsj~(5 ُH`<_/YW !yΦng@u:AkCI>`r,]NmU$/̇ epBc>4;ᐁp3: !$O?e<Ȟ?s@xB>0 ;#Cy>Y{@̎PjBbf8¼p@}ҩb<dkkkBgLJ@*@D^777Nj?Sv޽l{{[P1ŋ ^oo).^H]U|RnP*!vBqQ1Üt>H7)_㏎\Cwww/|18@-<}T( (\xǺs˳01t/}J S{t} ̟ eg'e5؟[<مCC!gNM e|7ÿHk*BYyY}} | Z4!C*OAp*Aʳ!պwC[p8( xJ',~<&!vmPJ&`0W\A9㋷$/A0>Aʳ Յ[V^*e5=>{7.t (֎L‹xx?~Ν;ٓ'Oϟ_*Ûͦ1Nd<o U}C:e!8@¾yPQ1J|~^U>h|B8>TN&Iuxz>Bmu\[Mày~7ENG2!w>5cn:Y(xj!,}/,Z-? Ia~ sX,@ C >87kvkeq!VU%}Aj'c/@ aS9]0XfBh2aAB+|n0+kV wooѧl0udB o8*xt}}rt{N29.ҁqa +!$?0gyB>Cex@zW1.^HMUsCUz#N.Z|_Ęr2`|~@ʎyYQ1N !|IH>b s?"L+˷PTHʢ0|rBqQWO!+ڪ%ΔN0e#0m!@`($> 4  1Fw m[|RtT4@0(0߯{7u:;3P6j-7a8!X}bggLJG`no][g+u\RgT<{÷j/^簓 {CQ" aְxܢ⤲<0$o!!Tm,RwW1/\JMUo}:FSmPc}q~2qY]!Bt+U;;;Uę2r@ @Bcw|CRLJu?HLOB_8 u!n,j^};]oZ<&axT>8-ݻw 2W~Üqx+&PyC&CNjxN40GN|؜$ѫAiBel\I,  F(`.e j~LgNócV J.o'UUjyxߤ0 rW_p(\0sz>GfYMjlIZ-?Q"ܻaqxo!>M0<@F;G ɶn.-Խ rwOCU|q8<8߽{P(sV>8<%p pIHhIhjN8!!o?臃d&wx9][G(=k֦}:FmPULa>=2qCgnj99=s:P.L $P4md`_Xf'9u2 OK'G=Sy-?Պ3waSF gT4/`B[vxk"|Wqݻpal(~÷T3PV@t0qIeIϫx>P{M2~|aw˷Wc,]\Az@w}T|Ke8|c`v`<PVӂ]8-|OOBV`a<'NlPkOu\\2>@olld[[[ ۺX7lU\}x<]Oa`|(%Y*P^ӂ{xxA.|h y ¤jsss|rM{B0VT>re"8ž-{z {%>|Xn(\k IDATc7.7$܏ ?8"סIP~χRh4֌^h\l?z# PBVq}hr\!2Cd^ý ԇAţ@^__Cw=z=< z{`<]U}u1/WԽ r?Hut:z%u$@e}AnP8s}A @ D6 ˇPr8~ݻ=yA C@ Z!DQyzs0BBA;%IX즾ϫ7֭[bccc|*4ZYZHe+u\}x|ht8@,Z{u'9߶X<U Pa!JP"fU|NLtB$V*s(PFVqu/ DP4`0}?M(ܼy`)D}sNu]CƩݿKFNƟյFkq@0GCqPDaNw-%2%/g!,UW(^**ZQ}d~QZZ h4hIyNhj=/@ qyRM޽{ٳgτ>:u8pn>UΡӍ>f6 UuQCbt:P˼y}a1@㽽Tn>ߺu+:F熐.S O9(/8"T!2ᷠ0O)y(n^SSPNaQkcc#[^^UsNBMKs@y ]h4{8 =ρ'} Hn=/@JAx8>)n,.$ٮ]W->{(Nc #kR2J>Pv=YG*"*xjbwww駟dnTon%Ӻr58}8#\sO}Ԍ`|u3x;-}*+KKn TVu *3mQ.x*Ru/ܼpbXq)@p]t:S0:6}J${r}]v3U\\HyIYWBTpm*3kxUĿ؟g2lr |uΝ P5P:唛%i޸ hd˕f9.@BUaJ}Q@xg2jEpK(5?9.^Hm)Y jVw'{FK%upP2]L^0><H3TRTwea6t+$ەy]8A8ggooϡ'a {KDy (ڿs !X}Νl0R5T¥4u\opNO+:(U=I8@xufe8f~_?::L𰐹CKݻY٬{7Y>&"F6PuVWW+)x$N*W=8.g1¡b<фǗ@&]+K pVQoߥ83jUA*'Խ 8!s{Uq_*:I0~hIϟג{ ݮʕw'Bqb$ܹ C8U|*绹Z|oߥ8sȊʕwWWWhE :s~/kZyGؗp@͜$)BEvuta |+K*ystpqT5d(<}[`|Uon3PRʹ p*eXt:$QU]dNGn_jӒ|-'f~8ܧV7 [:0ettt4`<D7^|OPP5^Tϳt7g+ y] 8ըb^8'9 yTIj}}]8>Q>6oS>sS{%}3̊:UC0jefR״<.Cܣ@ dœ> ǧFo n~/\ʖ.?Mo ͆̈`VUGxxn7Gp8~.$s!v=>o!ηKW#xZP8ܟU-,@}7tt/F8>=>:OiVl؜ݾXpN jfYyj?ý ԉbT yY5P0 TJe|><#*QCx)^v b<Pe'  ci ,L}i K0>&:Њ*aq|c}̙ Q={ano4 >@v]k .>>rfFx2xN},t:X0xNTIHN31gy,^Ny {5R1hZY,u؛Y;*j%á'q';Q=R1G\ZHyݾX+>25\ݮo چá̄nUسГ8@Y(.@en,PWzl؜A@jQ q33+~Kq}n\^^F->[@I O=mP$,Pjy}JM |C&Ԋff:,XvxY߯åsE 9ED`+'bf' sdk*SvC~{.іKh+,۾vuLGJ{J{~lssDfM|\r $ޞBqNR~o~{q8(gP5~8ЈTz׽ r-x!ﺪ:܊lIH~_+\5ZR};U4xOH$-($W7|+Sn^~P 8ʶC0>eUVU6J:\*PMZ(:M0'Pon.-ܼԙde #(\n;.>E1QDpP''yC y!N$v~{]0K,E<@ oQªޣhb`0+Ӝi~á@ɝaFx*GxxF7j V8PPZp2TUQ8IbpCTk;NN@r+۫Aݻ W]rJHj\%hIwVv5۹5ZR}럦 Ɵ8̃uxzvz'% K!8jhI xQ]8á(B{~noo/t:)2< gP}u1ٶ?{q3_y NQPjq8Xo-u)VN8@*~`%Jx,^@|]ㅄ[W_^̈́B\(TV2̝8o@jx(Oy#;B-+KUs}:`+"H=(8$1ٿWۨN7nT,l6rBӭ,3M0~ іgG)þV@Km8߈A q|rh4hI뜶 P~A9 NtB +/WTf7A8RۡZ|B7Gs}q}a,@6i  -(~]\ N!}TCs`fi0@]qP2I{qx8OoZϢ/58=<O0.Tѩ_TW+KhE'ꊣn'צZQ}1I0xF#[^^NM>Yތ/u :Ϫ7r|moo/ %Bqt="Gjsq8Y.,xoTwb͛Ņ\I1^}W878R * !̼ +95TU)'ΔW?K0|7:9RI}eI0~7J+ !O R0p8}.@s`ё''!qZ&P1~['kZJ)}TD` j(Nc"R^}WΆD GN!4h4hI loo@G*tF9nYwF2Y 0p}W xk9%N`UFX Xu5힦TFFF>nWVdddf8~' _:wM".m:_*w.۬Z^ToY~/Y oV,ChT}c|alݭ8TæB@&m|у4Lg2 |C߳pca>*Ɨ{}>]NZQXoxR{ 0BFG8WV{>7zAJ/ G |U5EJ꭯KKK=p8P |)qmdmn-_mJ8-j+++C I"GUyJO>k3_nϦ_%G(\Cmz"5aPjL7a ~ yVcNxy.㚅|D^Fh2:BqM"z*ƷO;W0~<{# yKKKN@dk)[7dR_Vocc^]]-k6d/*4Nx `|\+P1t&kMM(rRgp8$I8ڀz8UH0Y2R⥹ܮ#cotԆ pa¼q^T->cRZyd6 A_ %6OerR>snAiy(pZ^qqHYZc|6?2rEHϘÎzck2v+u,=`ChիkݤqkQ1MT@ %^ O=],Y|;=UmIYcs$ Xx6 |]1pȱ•"]{g"4Gu* v5i0c|NEoW׻7T1HًKsE\v^6#S1LTB#vQ8i8TO0>O\(~`mxيC0t666Tv oڤč_&Hmo31j sqv\hx,|BupJ+++ +4Q$_ -H~M0~o=2tu f_.ⰱM [ŨTd@O rI-<1 IDAT:<%챝:lp>_qZ_p^O+G7Cv\:EJCP164_g}'\n3Z.ߣ Ǯs ká拠굈/..&m5 7 "Gк5qi\3eR-0Qc?&i\?.%gP5sy\KHҴ7:j{\XUCȘ k{V'qx.2r`<0sviDŽ17x,X[챝֍ \oypDիz V->PHx IxL&|_vU3R-\:W|@"!xHzTXg" 3W*@S0 -=UCFrQ:bqq1/!i*8\on-{ 3IEO/L&Zr ?k)n-_Mbma9[W6 _WU_x~ `NcժUfA@f{>qppA"2q )"@:^u6Jt.%{lC*&`| doCqŴl<,'xeSZ$"ʼ9MI(ҵ7R-`KC0\'4uب3Tc8$c<_8b<0s*% au->P{Sbs%cr/ߦ|x!0Gưgee%/td6vJ0̜~g<}(P2KsWZ^Hv7XGՄ&pt V*~&yU*1 4lDf5.{kFc4Ii_1d$[$9Џb|sLn.* #PݻwDDiT"]/߶ fF ,f(&q666fab_63$S1>0AWX8 W4㧹`w ӼQ-`Gcb}}=/ 9Џ8Z|P# 7iT'' Vۛ :{4SV^o1d3ٲ>!YUk 1˱Y0>*ۢ B0> Fa.tNv*qUx1 ̒ qllls&Pez %<x<Y>%,EUR-i>zJ-\Iئi1Ǵw=_ xfhõANEc2z=v/@$_`к `|x1p :β< z,Ϣz@ff63`~~tFn>u677M+p<<^St.%y\ewYN!Y?T->P~!,yk@nf;`x<6|Dt=Tfgg9<p*hOwkj6յo=)!B۝YI-g C3{U`8ßd[=ݻW-&iWVVgJR4 L:@5ظa9ү718PZg lmm-Z|}xs*Ѥ`|޷KD`B<p r_W/"h6| CIVg888(zSYbiiuh,v>pZ:WR>O4Xr}_ n#H1 H7!dhf/@#L8p<@T_@gb6+Ѥ*k ElPH0"aKNr([ CI61VUぜ_/hcXMѤZG0`<@uWC0>}3zdh;`20ßdKY}TrZ|8ёv);Υ$M/߶ *e=b)V!;Lpi\xRa"z8j䚨Hz_4w(?͵o4}qjY?+; kwV&[q7b|y gfxbc^ݳ;b}} :p^/]dĚF>:<!.kwhzGc8~ _h>:j<S]Ғ,y]nm!jI(MP98@?y  YT= /ga2곱1 d;;;sXp?7 `|ZQi}8n{3Tκ8~\]]-VVVB0>nhb8 Gx/gL:5 UCe& lp.[m8E\uBr{#b #aC-~1 uV1>c=? Q/LTjMS-E|YPѾN/lM%),;ppTO24/2Pmά@ӄM=ם zv-\I(跶7@4cq%n]b&{666N6@c8z@vf>3`x<>^z3Puyii4DL}ӕ``wD*Ɨz}>ȋ`| ^!(Lqv3g]1>V3 HJ7a3ҠZ<0 ڱZ'ݪ)Sbmۛ 8NT->n}%z62֧Erƫ|pc:ݻWlnnM:!,K9|݂ӼQ- *8կ~d’~_=T#*{}i fa> Oh"]{GmoTώ4AHa8%i#k֪w`|Xo6Kzz Gx9UL ͦ#LP8@Oq>Yz-?Z'`|ʕSj$X olle' L֓mJQ/dgPKz7phZܿYR]{Υ$ Eߵ kOSizulr8<$o^E00UօHǏ?Xlmm9#@fvNKo b|WS 0`|Yu+fOT%83Əcv6um`Fk>MrǴ֙O(ҵ7:j{D'b:%6@*ɛWQ1>-0]zu8/KKKmo &a4lAzܷYS]ܵo:幢si.#Imoڨ"GY rcc#/(aIL0z+++S7A XgN@*y*j<0U C 0&¸ kx`H: cZ0^8AUV5XGXi2ըfRBviZ__WJ(>m@UTٞ7IZJG.x1-,9g(ݮV`|<Ǖ}f" r&*vоb| @ބC01e25k AecۚT] ŧ+ܧMUy5:Ҷ%RZԼ:ԗ"(bg Kơp a-gb|z S+ ƏcoWBG[[[mo"6 ]PwDJa[W84>moZz=' #y5%A0>nx6E1a2Uك|Ua?Ȁ`| P?a8g\YYK'@xǟ*ar5]spvۛ8#fq_byV[Oѹ4q\&H> ^LDpTA8nۆ cdMQ`<J_j T5ˣG3T]yqۛdXS|cnAiި 8N*ǡ:6|uQ`>p8 C+ ƏcL_~Ec5H eB(>LB߽{W;5ȳgLiuWkO!ã7@28 K0|uQgX:UV| Be<_,L u01,.umZ?`~^_T! s]6@ J4J+6VWn8R4VÄ p B4 LQ>k7:~ZJwmodXbՆZn/p8: ­ >@7*t3 ~/hg [>E 7@RC0>AIf( cuq\JܫT<}tR ! AK1ɓ'NCo8 O]+֧not&H`Y\31~BuVU2gj|=z-_,޻wIo0x. w.Ks|v&H`<9\³ tつ`^{d͛"dΝ;96[ژ;m_bpէΆ.#^888p>a0[kNldx80'|!$^ַ⢓pi@]Z^h6~ 2r/׹NȁM}UPb|Ѝ@FB@N~²=*vww7dcuuu2'5u9%Ӫmmo$Jtur|'~2j|^޽[Ν;mo hD Q}{7:|nS:@ȁJu.5 2U`<$U?|r^U nɓLTRZ0~-O3x\>yp-'O AI8x3 xE0[pfȫͱ9eݻw̨[bΥsy.goj2}ݮ֢@Ƣ<Kj|VVVT8믿! Lw#r8%R%dFpzf>@Ƣ+ƏcOl*;;;/Sz|BQiV%>s)s>J-\yM9C4$`cᾪZ<W*p6mϻ|55mo TP3xWd*`6TFkr]BPz$oaU-HñQ"vP=vyM4bJήSd.J5qg݈YMO،w\74M|E+sLgϞ9-Q<|rWWW03!p}(hn@˭u:W|NS^u03pvD{ crQpap8Ԑ-׿O gwwwzX4sT/w.M ^gwpp`3%d!*Z C'?lllLakkkK KKK:DO&y"wJ•J~ӽ>z&h@H.Nx |d,N`|2@KΎ3gVVV'O>N׏?~@@9*qcaҟ;BU&k{41M! : <Pb|> v2_Xz)NBOf)O3+>hh:rkJWY>U9@c >.$ -CUd@?_Q݂*_RE*9;Y@`ݻwCBYKG@u|=x K;;;IUo%'I3I^ExRmN-egޮgdoMh"]N۵~ՒeXBP;T5(_>vTOI5 |Oc ) F@^󿽇OӹUQ>'Th4LH$T'\_ijZ^p_^_UR54I nt ד!$'z? 'ok ŋ3Y!dM0~7Gg u`RRa} TEjI s'?S#MBa}?ϟ'}}}]k8>k4Gk+3 Ƈ=ykI?P) A2WKЭ`ݚ>hǏOBsiv0> ɇm*x"Ϟ=+vvvO k }sinf,N_CXW5XPnBam(Lp)P x:N+z" YCuf]))0 k\Bxsss^\\ dޟ~g*Hj/B 6'pM訸|չgƗy}>݃Tk%%BPpZy2W[N`|(Z,TV{l{\a0;K hP`0+++&Hu@j{_5kme /hp2Qñs^ 6 XS- g%F z=! QNpR c ƛaj$_~ܷs^sy?j^O8+xR8zn=he0G@m'aCHUOmmm9?@+ %[[rqmjY$!zn x\[F`Vnt.^jFYQIB<7\ίbj~;|㱖"kշmol Q#OlVCv[/jB_rt{jloYPPyZHp<U8Ci[}s%5>7Ib $>?b@ق%5x{}^k8OG<@ڋ2Pn,܍cxx ůP<isgw|[sy PbI0\/'p !ߛ\C(T.pέsTooX#& 'O,ah؄ cIǞF"wL}#?N0܍3mã7@+HsPH"B1|UxYNB&; Su.͝響 _F `tC*\`xI%o hp`0pJ(`6F響6mģkW#ZbtA?Z xs/7o,z3j`]3aۛTM0z$kF A _1`|?c8I%nCŇCOpq{b:5as: @[ .H¦;a%D0~<{$/8L7oчcY©\(7x$U 5t5}KdLIb|-hÇN @ϓb-xtawTh5fH*_@ $3R0>2米Sܿ_Pz9ziX[8]}mJ G6`hx& q]R%@ $O))hOߋpd@øZ0~V|k7G Z[Gp!=TIr7Uo@K͛7^d,a7 P#JXWtpp}!1aS`0saп2M髩Pb{Ν_~Ѽzd7 V sy&_jo$6*zS.@T@R<q[@6n[[[O?dÇ]"%V |Go$FUo!]O_- _BM ;?.b8: 08_kyLNTE@ )H*Z0[0r͢9 0r_;s2K6ZZ 8l$) dowwwR}KnvVd~HīёSQbZW94Ph3a6fM:e'O  ~|y2>qts6JL Ưu$riz}Mjp8ԧuW>h/P0m An-/|9fpp$j04YZ@0<&WBugϞ95n 0Z<ſ/r0݃ 6fE_p2 as@ 'bss@ Io߿?8 @ި?մߩ_ա N!fE_p2 6XZ ؓ!jnMP0Άӧ!^ O𷗯&s|) ->ZOYp2 g Z/ܹSOXW0/:[St.sZJBG=gFCqrQ!Hr'Tx3tR%>4en,|Z5~m*ǫ`<.d j04떋Z@3t7pNaoΙ,q}OFG0ޡ qQ<[."K`<ӧj3 ¸OUա e~b|=p$eshc?aZ $"_ MP"a f?hчcg;}<.^2Lq^$hpyXhs0ExyfCMa| 㤉GNxyX\IҢZ<&EX<;- JEwvv*ݮ(x0>څ O|O^`?srr^@ ƟDSw)~b0h20E%@˭u/w.݃ p^\_c8Z n8}nZ<|p a ͛7' h.xinߩ_jotP_9P@AC9}g~I6?mp;;;/q.wa%P1ܭՔ1 %y6 :~Ib|`-sVvJ׾8;Er):WR>ڽK9Hh>?g-bxZ}ΝIt0hJ8?L5Ph7}O5 /'zti;r\Ϝ@\ϜVV@To&Y:Tw(!i5O #4@ ?"bFcǙi4Ju] W. q]s@8p`<s@`0@Lq姟~BF*svT+Olw֎ YYv'TѵC݋t #sd={^ ,}OiiŊ161HW 2/-r)C  (4y:45P>>5T]XX8ȯ;Lr >?Os֭[ɽ22 83q_s m(\t,?7#&A0Q S 1 ZvNGj-K0޻G^v-]|9}ᇎ8Q~NE~n &mdc˴xN'i g'q}5`byP f3]~=KwMV{ ?!?'xW]F P9!!P]oN"T\+":eآ`<ct:wI.]Jo 70M=ӿ&0$7N"8 fߓj4">](tڵtnȠ}|aa0Swf K0>ij)^ fyfphz<y>UwV(& T>s (M2(<=VLVƖi OALss )._t)[?tPo޻oMsa(+-`$&sY>9*nL`K/nܸK&B/7|s"q. egb<8P}YPz\`|D, t:iaa!]r%]|9B0GVk>c/S(Tǻ(02H߱*|N3yOԀ`iPAf3ݺu $} LWoK.݇~2e8'3-qqiPwR.[0޻Iˡܛ7otsX7vu]9(/9J{[p,ԇü'."Jao(r8!Bf9oׯ_]]a>j6L-%`,իAY;-Pqe۞B$֭tʕttƍtm޻_}PE;WN mvCJI@ŕÏRMNh.`9Ip9Oί .k׮M?/^Gj:...@3ޣ]`,3=';^ȟ*` r7+˓W^Ur*-w :[mo?}ۮEϴx;9,Rw 5P1& Çu?I>tm>hݟ |cc/ϱV[ ؄''y/T_7"eeY0/\p ZSAN+Hix[&p6իXcnW0jl}ŋ<+囝FD`i4[4#(O?H;үßO=MϿWo~P;WN X`֋LT_r (/*2SEpDxߧW_K= r`~oS<_5cM0Iˡ*YiA3Yior _Jn*s01iuҥt^-xSn{|o6&} \}p_-}#zRʉl4ݔҋXpA`>Z`ZAp)ec${{?&+AK91>}35~1,F?]~Kh~~{w%n6WUFD2_`;)_`)0Q?{輪}'Ig_]#"Q֚9-wX ܵktWP~?p}?Tkϥg:iҫSѮBӖ#ZYkR`|&_ϧ#,Oϯ=~Q~= ?tԥnӟ_׽ pw#❲V,SJw0}GCCU~εƾ^Kk8 b-Ũ9 $?GFVۊQs8V+"e.@eZS98Ns٥GD>n韍`;e/L''SÖ6;QS+11Jߝ"[}|g,W$W;0Y5cZ< T{VjRq;iׯ{` H0xʒ񕷢 ҍ 2LÎJVҦ3*L0>"LrnL*lk DpV۩heA x)=Tf%"B0*6ztS U@,^=w鞆0Hrו GD TbVu/D0*' 6zttJNTu^fW+"79c"Y`xuhr'd޺r' | xJ0'S~֪X*NO1irsAsSQJNs8Q7"L/x;ieуսp+j>zVŒI= YWub|\5cxy-mv^8%TC{>.u/("KH08sww^8rS`|gzwUD>zЮ{ 4WWyb|zW`| Z)p*[1}TdzLST:[]`|Im,TѩrXwn|'] >z ^S\S W@f? E0>hm*NA}MLӬGDUjh7}lj|!}ԴP~;*F0VD4P)xJ{;>h%a&G]`.,(YAk5WL<-hea W`<p*SǴxI+"u)`<!yj']%`$O&AK 翺f̴x`|^'}P3dZ<L03};hZѬS)L{_~mZ<vZt=h`)@ vfG* S\iWws0=5  m>*uiׯܾ`ZѬ[N[xW&(2e^v'ZX PGyar=s] ׈Hv']E\\O`,5"gA^"Y  h7>:cmN']|k J{;?(rr אXfWFPҬ J0VW!}Uu@"YCCn˯R 's3u>' w?p ~˯Fd}:Sݻ"gC{VZioqiPF&ט`<pf9_WCr(¬Jn=":u>Dx !|B0 E:+zTn᧿u8^(B0k_l >-"&,FկEis(>7XNK+/LAn/&}\j{{ @nLEI0>' br8筽Uޗ7)>EMX|5wRJs ޹ҭ{1.}w+-"X0~OѸR,^N?ϕryJMS`6#"gkO0FLs鿽W^.MW{Sw w#-h,~P5s/Y|Ŀi]@k]0@ѸRYP#E u#c`FJJB,z.WY?J=Kp6fq|ݎ77i4ҋYP[?w._LM aVZzu@q7i4)fAO&N?sύUv꧍J{dx(7"b|C0Fq=7Y rH>޹wgݽ|V?*'C+".9|QRw^ӝ8L Ɵ Q?НdAeN0~2nWaQҳ SZ*g'?4G!O`DD3^!?9.J@D O`(}Ubr'$"RJJl8;*89ej<d'K0~\zD4k_ ;U 0 褔nWfC8Tm'OnD9ŪmOXD4SJ0,);Up*Y)*n 8( OADpD0~z1X2S"?=.Zh:;U )褔nWrsQ *2=ӵX{Z#?]w9`\ OQD4SJ -t O2S&?}.b;v,"RJJoˠ 3T, φg9":ug "(ԋ3"?;.j5TzD4lΝlj`褔nbP}3$?[.n(VD9-x(?褔ka;ugon F3&?{.r(/y9hki9;PD ρ`||n;XK)jq(7D0~~\P.2s"?? u88XK)jy(H0~yP"s$?_B@GD9͏`EDwk[(4_gj<L ϟk="g,"r0["@q-8/Sd @0ԽP@h:/]"x B0"R]:gNS˖,FzR&=d& 8,X5Aޫf7s!~}'6kЎ%> o0ڡm0I֪>h"!oF-Ia|[|ds'Z@[x%"o0G a|cU}$[o0M"$9{{mz}`7Jߠ$>xI._Y̻Mv q 7*ZU5v pK9K6a<ܟq%Yj?ζ6a||]gIr0߶ 'v@߸$kUg`'L-I>a|p{:N;dsh %< 0nGa|'U}p#[0/:߷$9c?}v;#Hik}'$03I._8>G'Wkɏ >dnw(%?>I/Ws4ˏ>d^}w*%?>AK߷T}퓬/a|ǒj} wN??BLK IDAT9Mn>}Հ&\CUgCƏdg.ƏI;a8oƏc3O$AG O;a U}p5qP$98cه\0~ IVW@?ax/`,$\0~L~Jk; aX>j(ɡtvV>&a\`&ga0]$TSUgp-~`mfSX&/3N?$kUgZ؄#`d$0~pIvUu} ˡ 簛} KK;a6!퓜v| $9T2kc;Y?a<dd9'TU0\0~.$ tO$ZUmp.=;&9\Ir ϳn?!ann>9 >jsO(ɡ8 =)a9$0c'&Tn'&?=8&9ԼKsyFONGib''#@IV0~rIUu}4ˡhc@Iv60(,SI֪ڛ;a<(ВcPxdWUЈEa<3 }忄fЂ$MaBGO(!ChZ܈k|0p5[XLlb|k|0K^xa<"6&7쓜 )IUu45ȵx>MWxl%,a<_!+D|0OKVիI+||> a<_䭪vW $[⫄|Х-x,ZU&/l ]ϼ'9![Ubk|0[x1EŒdg0|0[@a<7PUGʵxnF-Œdg܊0[lG ;>Dy a<dWxa<>xa<&jִHO/<֬i0C&95ӬnCwZŨ0mlC%ag0Qxrصx gWbd5F gGygZ<3kQ0 a<ø>ί6 1c3{w-Jcm\g8xFU`R!b0$Ub fHxFbd$[KgDxt}϶ Lĵx%gdo`î3,a<#۸LbdlF%gXۗMll GݫkNВ.-{\F'gs`TC04a<û>6 hc@,\F`@ aa!g&q)F]U-6 Aa"g6/6tnIDf"g*Gxg 3a<3:ЩkHό.9 ̔L':tH2+a<ڸtfs= 3룿}#3-a<3xu- V埀/̄??k> pvG@d@d "0D` P y\2T 3giw`0LbH1>cZ<b<|x+ (qx/Y#Q#0/b< x8"x8g )qykpdpDVCp|p|+.kppdV` (ix`zÉ( XI2 '''b5&.bI6 ==V< B)CyDC)Cu荌dvU5w^7xeǷLoscNA1j<xxQ] fk-% DkmSUWNr0 aXmaXa@Gv+3C000<3($Y]XRaۭ[``aGQv&saR< b< TdL0\0lGx'Cn(x^^ç÷Z&yv6x1qhID #ZTՕ,OIb$OR KP,,eg)u^vĵ$SgڦNr'bB8}'K1x$qAbg(xࣖUuE^H8G$|HkmVU"_&Q|XZ$_9p ]<&Y9p Q$y[vYcRi}0m$8xhP..ɪ%;s}h' ޸M$^H4za?-0h={\/IDxoR<7@$YUսT.f@(}_Inlxw:ɝCa1>d b<0(IVUu/5w3` b<0DInl DhHMsCd1$wUfN b<0d>sdVP)dSU7]U͝D+):ɝCf1^V@1$$?&FA1yU $Ơ%$0 /*M&FZKfNb<06>OqQF%ɦn LԮƦ%*0:^AX/SVJ1$.0 D(6U/ #IBJ1Uc_,CU= pS`^U;I#tI)dSU I#P)hI LBkmUU?J Lx`JfF^)x`2lFj.D`JZZ{ /ILx`0Tkx`I[Ѐ)RjQU[H0E-Ij_[PTY&+ɪ00eͫj7#vd#"`Z~ߦ~>%A4Y&/CU=N!n3]Ep\IA0  f#68 ABзt朽g2瓴nUYrL\-8O0!m 0om$$ `"|VXc&y+`޵UI2(??| '6N5ڊOהرw?w.`Ǿa%󌶢x1Yo~{3</6ɥ L2ܧ$r~SxV/48KML /b<Mr)K`md. 0؜OI Fm`3F[Ql,7yIC$6mKZ&`\؂1"+yFb6 xhQp4`7F[Q6[y{Vbb<5[pic1IxcJr-1^`d.@0:_%5BxJj IDAT' }p1c$MMۅا0Q<d޴}H2Kd]0ZvI` ػW40)$j`F[%0 cWmoT/_FLLc5LLL% 2LLL6[ԛ 05.0U3*x&z],f0Z0v!` WǴ$`F[0ycWبmoD Թ_ `3.8.p0gIi ž=#xF$5"If"c$MsBt8D$wxF"xNۇ8~=Gn{%.h8c]bPj{RLr&"1mwmoE!s1c0KrIh10}Xm?c`QhHrMvgk11hJrQ->zV1mZNԻ1%,p>p 8:mvr8F$Nĝ-0*ɥ#L2khh\6[-G][p\̒k8B8qRnYxNBEsmGVSbh{R[&}P$bU6'eq8%.pfI5F"xNNۇ8~}\0NhxNc5}ܵ}(N7I.|[&9SxNc5s&Mۅz8e.@2Or'`>a<CY4 l{HF[1gImb<MA%)a<WIe2ɼ?c, csgIeGx9xIfIvgAhHr.`l ^%%$gm 0AOIlQ< x$sJr)7IfmO3ڊ aq7BXt.͓xF|Lm̒,ee+cN v&x.|#'wIB3 h{R2ɬ@F[11n&O8yo.N=`Id 'Q<la<lPۇ$$KIl{z,xذ?ɹn{v0$2e;`wNp.T e[~,ɽ|l{R>x؁8~d)o8 m?vc5c5"o8hwm_vxء7I>]3nJr-w88$mE{0X]Mp0޴] vx؟y;A`c{!,R0imTc{G3x2=kH2Lϝi0 h{.`2}P a<LDI{L23m2ƸMV'ٻ+vA "*TTAA\R/Tq:;*N^`C}]%<5e[b<ϫb%$Wբzd0>x!q<\&ɏԒF>pV$7N x$je#8]U-M#d]U;jd0nx$Kq</lOvx+D$_CrUu,jgkff%>I8`of}1x8$ѠЏБMULԧ$}b<t&ɶVv`vU4GJ01CH7G:? 8T+Q<KK6_jIk}~FoIy1,jJ0$WB Q @ ώq:8 W(0M_/NjM|%q+partAmU\ U;%Y!_$;8pa<M8 pFQ<=xO%6{ߕd)>G[%q$^ZkÏ /\ xCx!Us1h%B[8'#!/r%y\y1$UKd J|/ *a<0ix?%fAL8Nx`6,"fĔ8DY9 t HNjcY3x`,%Y|FH̖8(=a<0kx`D@]3#"!fBtGtEL(08(Q<Э@Zk&@t͋@φw X n%_L#JH@τ@ QH@@Hx`|D_0 xDw8Q<7 .L' .Dx?!Lpxg"'a<=<0HxD x q<HxG<8x Q<Iܓ(O Cpx' 8a< 0D(Ą'$@pbx(Lg Ψ%q_3j ?nqcVyNjgdYUw\00;x \8fCpax @pax,Q<@#"a<`2DW&"q<(`W&0`q0(`#d-Q_ߛ`#"0`dp5x8.N0bxňFN0b_;Y,L#Z۪zf+8Ms& v dD"q<(`b"'L0`bhxL1^މ%1D֞WmU!|*yˋd_U /7f@0qx&Q<Lf@_9%1)ֆN Q"|x1`f,jcW:$)a< (`Ƅ3%#x1_U_E&$kq<3;70 : `Dt8`s&gQ<@_ZtvSUUL&p}b<@glcz9 tJ!q<$0S8vwx$kUu[U/zJ6 @߼й$Zx9?x?#pעx>kIߴֆ߸W2D$[JeUm\+p'a<p߻ --ppswhx)ɺV..a<ǿKpbDGKP|Wknkp$K>$_r<' A1^߹NC$֞WmUp9`d`<x$ZTϮ=D<xW(~d@<x$˪ڸ"wpxǿsIQ<В8$'Z н}4$YWժ ЭE8MUV3&8$2/0ig&i2/0iIU5;K;Q<sxfr *)a<Z~}cUcZ$#`.~$sdYU tj'`,ֆ@u|F`n,%YW˪:XFy1YkTǪ)[0gxfnꅵY%Y]}U-jcl`&UR@/@WZkZ_U0֖Ujdo<zMuU وVk>V_#SFWxZ{^UU[J6=,ɾU̡^ڿ.U* ֖UU+U";gn\&k?|-vk^wb<|CkyU}pFzdp7a<|Gkm;g0D$[om%YV;gőn[^ 8WaQ phꞺsfGz'9PSg$q)Asbg$WI> x}"<x>xxKr//#/x~JHr):I>)(+ >Nr&?'QNuTP?O+G?&9Fy+PUInq_#Cu}d?ɽ8$+(tSL`k-| ?XxX?z&W:Nw8=VuH` j(ȟ'ٕ/madǩ`` Ur' kVU{In|5}^Fjy,,,9`vd o|7l$Jv uyO+ގb< > l>'7U-rxU*ɡ`ҖIN™}(;$_&1Qw9x:N2w&>ɢޗb<LDU$QIciqK?. ߕ`Z,UE/nojd1b1&h|eOrӤI>s9.?i`ªj?U+m&b<Lb|XeOJ`tSw'9s/ehMw߈6CuS$Iv ^:0Z#:`tJZ-|g}b<l?!%&L`C ׺!$ xl`h\uOq)I$, .Wm䬻`,T޸]Rq%b<ʭ,`~,LUѸlq)ɱ`~,L.o`κH)b<\U%9Oŭe0o%$K {X4 2B)x2U*G`}b-[.|q6r\s0>;n۩l$J`u?I}OK1zAmIY&O$_;{Lr4Xi|U]xIJHr/YwKOUu@RU{IΓ|kL2o x/UII 5Kb<SUudxwWZew  ?wI X&])x.xYI$/O !U-,E(AbWruaEhK(RUI| Ǝ$$/,|R~xUqŮ4II( 謻J*XV %RZ$}'b<rUuo:IUR֢ ~0V'N ÎTu$C9VLR<NtSw/I`,yu0vU5䯒J`. #* b<fjo,J`c-Bxx+ë=,ǟ `#'Y(ob<.j1F⟜ xk  Vw#z w&>B)xoI$Ç]$sM1Or䣫o}T(SUǔ.u)q `j$ɧ$Kx3I)LVU%JrJk3tLVw?u"ə+}R<0uPUq=~Vr\'0uƨj VT^%a9^>B)4$<Kw"6Mu vA?LrW"6b<$\'9L1:H2fEϏ>0;Aw% leOJXf'u]`&9'b<0;}du-r x`,VUrWWfq\sd`,'I~Kti`(sg1 U*ɡ30tcb<I\pIJ6l:?>a~tb<u.Pv}`C,l+V$Iv= `nub<պ""gLYw/mg1`TUrWy89x$,$Jd1?T^$ea㸻,~$߬ov\WBUYw )/O}xnǕxxJUu0!g}*$QEU5|.G<&9;ގ~@)YXKrL`-wն*+VUGcA~WuRΎ,Vk| ɭh`k +ߺH)`,QU&.c$C!b<uP0?g}^kwII~f1ɧq<51 `c #oC1UX,{($}loK1TXuo%}F$ua%[w)PU'IN܎+&$Yw:4(LLU Ӿ q%Nӡ0A]XEkR<tYZ Vn8'Lb<[+1w8O1`TѸnjc)J<P0zPv"˱%6͢X`z<xqD5^rOI6xq=Mriwo{s03Uu2|Kla%`>fz<[J<)̘xxS9̘x-%03Vb< XB[z<J<RR Vb<Y@1LxA1DX(oCxcVS)+b<?e=5(,X!+b</RUGcAz<q=?IRŪjo,ϴ Wx5uB1ɩI]xSmtx&CXe>5SUqsks=?R`jo,q-y W"`UHr䃫INIlx6NU&r/v主D&Q`#U?E&R`UqK$' 6b<rDZ%6b<QU$I>*~$9myP`v4w}s|D1Yq=Ѕ-9R`֪8wץNr \)0{U7k3主oS`kTb\;m֩qA~ s;?8D1TUzg`,tcjUH2|0Y#iw?9J1^?M]'9;G`)Ch9.ğ;b<:J2|X ]&9'?)#sX67x Uu0 X }*\s Uu<w PkLUdx_主oϣ/TUzXsݧBQW +C'tT^rcb<@U,%Sa w IDATS +WZINOVC1VIXqvX-xXOrP0k$}fUudA0;J(#xY 7 or oG1AU- ?6Ə$dU|]wɺWއb<Kr[<+gQU$ÂZܟwSSWDӡTU{G$X7Lb<LXU'HrN$}.^.xU  VX)Lb<l:>'u7x$ B aj/y/n/䤻E16TUC7w`3) -\+Oͥ3QUë'Ivr;DO1f/ z W~00CUu0-w(UX0Ojx $ɮ{޽5- l$< JmA0(d8=[uOpCX+b3}J?[* 0F"39=E)ee0x̜G2"5? _J8a<Tf^@~3@Se`2s/O> :[DܖR%8~|><]T/įleRAD|5dS{xdu LE}J)[CڄOeM̔8?#~+3oip@O( a<&9m~?"z!~e[wyxM?RqW&o1R a<)y/L^QU)eE0"Rʭ(,iD71YQzW08Eߦ 0XcTC+lK)?# *E]f^ W 0o^ 08/#bfOxT0iTJ)R<"CDYJb<p6y1N{j[Jy&YeyiRnsFnSU] :fNNt0̼lvGK)[Ft@@/^E g6paK)kN@@iD7A7yAʸ%xA<{xj &wX!+lK)?#BkJ)sQ<0.zA#wC#>l] -*a<0xy]/ !i&/lm2a<0y` =x`he3oR0=<ax`ByC-xW ~@5G-"n?&̜6_>O S2Y@80A<;A |  I xO!8a<W$8 < G 00xx@GpBx@ @pyA< H 'a<@@~Q=DR x&Y< ka<@G &8< =!0g2&"n#]vqKA<@z*3y<>/l =' 0` j v mj 0``2s^/ &+,m`X%`D'/"6"& %3k eRG 87W俚(mjpx*3MDLL`T^upHx"3q[@`؞O.cptWg 0(5_[+$d2^2uE}D,0h5}شpJx&3|Olj Eel5K)+܄tJf.j$a3'Lj+Iy]/6ptX [ktZfΛ$q8/. 915OV1xz'35=7EIJkcOVf^@-Ԧ(5&H@e<"nk$?Q=~i0iDD]DlB|)0Hy]+F`5Z8C#`2s^Ķy1b2a<ӈXDmDl蹇XRV ̼=k ~mq0yDEDO|ziR҂+a<ӈX+茇z~e%0Z2W_d<"" 'fKėR MӈX+3 ?'7136A N9o]FOM_JY0>!39?Lj+ >F5j@DlK)[e4"Wgf Rpp$yS#/f \_ ~mpxx8̜O)"K)V %EziW߻#3Wok$<t_s~YJY0(3qS# Ni?V#߸"gM_JZ0:y8 Äa+_ bw@ EgvR(3y]#yW}5wzD=U+?G߄0yY&)K:<0yFn7H^Z1ڸV 64!|ėR3a<05_waZ1 a<0H9oE3[[-"a<0xyY<0 uRb!׭H~b@ϼ4ux1<0x`2"y1|s~mS i+2yiBzJ30'2s^ED\pV 2`H8} XJy4XH1<'>F,WER 9xzEF$u~;a0D0:bx#HK p"x@@p&"y 1< :_GąY"b%a<@d^_dvEa"y8*1<@OzB$!!a<@eFbK/K)F?xD00y ' 4!|ėR0xugv }`؄#0M Xc 0`2s^&vѲG0r5شb02sڊ71N%"*<<{x~*3ۑ̤8o2|ï &yY/"] 1 a<%/&olj,<%2s7g& @DUxzK@o&[0x5yW&a<T_.l} *<C%`5:cU0Q̛V(Stn7A|)emY0y+og*|)ee0^V$e> lk 5ta<Ff޴._p×Rֆ ?'wi$gS+64x;a<|Bf_URʣ 2_-f׫[e|_5 ʮ [W !iO(k]oBgBwf9߄p~x̜(/{j+n@G $x <zH=%7` /!< 0_ֿv j,aHdEy<}mA#UCV(@lZ!K|H~OLzG"Bp@OB?",3B v"o%>,3HyW @!1|)`L`^]Z;p(x2sފ/]>W 08̼~˻*mOB} /f9IDAT$UyNq a<Ik@ ^i+l|@c nߺ5dX~fHH xq BJw:H: fd>ְ$Ďϙl|Rny@Gpfx ./k~OKX]Cݏ"x#c6P69\f("SJe=7] 51$"PO>o+|tt XR]4`7*"P[ ٕ𽮡N?`#Xh'ˏ;wa!wM$/ZL.<"K|`OpxΦ z)ܴ] RJ'<$WDDI,K3m | 4a<Wtn7e:V7e~ MqDǜ0Gx'"XyCwF,0"VF*X <PwVOUDu|o,/uz'|O$a<0+NoyP#TCs΃D,ZҷDM7襙Nt/ڸTw`фDD;}湼.j?6{k'.O]D,~{ >IK"о^?qGpq}1|t5'X>MLO}2ɞOfzҞ#w52؏>,x*S[SN9pU񯸾w{uݒG_?.WJ?Kȏ}IENDB`resources/JobPlay/jmisavexp.html000644 001750 001750 00000000303 14000365526 017627 0ustar00cyrilcyril000000 000000

Save assignment file

Save in a file the present state of experimental peaks with assignments and attributes.

resources/pixmaps/end.png000644 001750 001750 00000002762 14000365526 016343 0ustar00cyrilcyril000000 000000 PNG  IHDR szzIDATXWMEޫ]@DD{0Š ==`<=O"0D;;Dbpb6,ffwN5=3ZIuWw^WEGB !@DD4JDDtZ39i0md!f&$<%JE/Zf#xBfFEDǏϗBw,#&O$H$3cmׯ_B#CRʍRJd0L3l{=7ǟaƪw"DrR qeu$$IeB@qFkROp "A)5gB$IclZ')2OI)RJ#PyRsuY)u* 3mbbJRj6 wmZ"R QAJ yU!ĊшmB Z-ZJ 107f 0Pvq ٬ l<Bt1 <ϽLu_(JԻA C(e"z[]n`__:&ǪU:֯_]w[PJeWfa5.^Ggv7_l~G^VC!bDDljG}{p!j|߇<*3T*`̬q5a4.ܼyArkT̎hjjZ>sDc̼5yrr`_\add( ?J"zܹ_k "BV! H0<-466fܘu8`}͕N5$+:\B@)k-&''!iഔmh'PV͢E#"!XK7iz=Z FRЌ?R1<<<ՎwǣҥK+Iծ++ \!PJ~EJuddlw=._/xZZm)y!^f>\*t8mi$5Z<|\.;9]IENDB`sources/org/spview/filehandler/OpusDialog.java000644 001750 001750 00000003343 14205763641 022354 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class OpusDialog extends JFrame implements ActionListener { Object selected; public OpusDialog(String[] string, String initialSelectionValue) { JPanel panel = new JPanel(); JButton button = new JButton("Information"); button.addActionListener(this); setLayout(new BorderLayout()); panel.add(button); selected = JOptionPane.showInputDialog( null, panel, "OPUS File selector", JOptionPane.PLAIN_MESSAGE, null, string, initialSelectionValue ); } public Object getSelected() { return selected; } @Override public void actionPerformed(ActionEvent e) { String Title = "

File Information

"; String InsParam = OpusFile.InstrumentParameters(); String AcqParam = OpusFile.AcquisitionParameters(); String SampleParam = OpusFile.SampleOriginParameters(); JEditorPane editorPane = new JEditorPane( "text/html", Title + InsParam + AcqParam + SampleParam ); editorPane.setEditable(false); editorPane.setCaretPosition(0); // Put the editor pane in a scroll pane. JScrollPane scrollPane = new JScrollPane(editorPane); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scrollPane.setPreferredSize(new Dimension(800, 600)); JOptionPane.showMessageDialog(this, scrollPane, "test", JOptionPane.PLAIN_MESSAGE, null); } }sources/org/spview/gui/About.java000644 001750 001750 00000003077 14200316740 017660 0ustar00cyrilcyril000000 000000 package org.spview.gui; import java.awt.Dimension; import java.awt.Image; import java.io.IOException; import java.net.URL; import java.util.Objects; import javax.swing.ImageIcon; import javax.swing.JEditorPane; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import org.spview.Spview; public class About { static String versionnedName() { return ("About " + Spview.PACKAGE_NAME + " " + Spview.VERSION); } public static void show() { JEditorPane editorPane = new JEditorPane(); editorPane.setContentType("text/html"); editorPane.setEditable(false); URL helpURL = About.class.getResource("/about.html"); if (helpURL != null) { try { editorPane.setPage(helpURL); } catch (IOException e) { System.err.println("Attempted to read a bad URL: " + helpURL); } } else { System.err.println("Couldn't find file: About.html"); return; } final ImageIcon icon = new ImageIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/spview.png"))).getImage() .getScaledInstance(64, 64, Image.SCALE_SMOOTH)); // Put the editor pane in a scroll pane. JScrollPane scrollPane = new JScrollPane(editorPane); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scrollPane.setPreferredSize(new Dimension(800, 600)); JOptionPane.showMessageDialog(null, scrollPane, versionnedName(), JOptionPane.PLAIN_MESSAGE, icon); } } sources/META-INF/000755 001750 001750 00000000000 14325453166 014220 5ustar00cyrilcyril000000 000000 resources/JobPlay/jmiexp2null.html000644 001750 001750 00000000235 14000365526 020076 0ustar00cyrilcyril000000 000000

Deassociate experiment to data

Deassociate the experimental peak list.

sources/org/spview/filehandler/SPVProject.java000644 001750 001750 00000033127 14205456426 022310 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.io.File; import java.io.IOException; import java.util.ArrayList; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; import org.spview.gui.JobPlay; import org.spview.Spview; public class SPVProject { private String projectFileName; private final JobPlay jobplay; private boolean unsave = false; private ArrayList paths = new ArrayList<>(); // List of paths of the project to load private ArrayList typeAttribute = new ArrayList<>(); private ArrayList AttributeOfFileToLoad = new ArrayList<>(); // Contains all attribute-types in order to private ArrayList yReverseElementToLoad = new ArrayList<>(); // Contains information about "y-reverse" state private ArrayList hideElementToLoad = new ArrayList<>(); private ArrayList xShiftedElementToLoad = new ArrayList<>(); private ArrayList yShiftedElementToLoad = new ArrayList<>(); private int indexColorPredStr; // Contains the color got from the project file private int indexColorExpStr; // Contains the color got from the project file private double MinX, MaxX; // X Zoom values private double MinY, MaxY; // Y Zoom values public SPVProject(JobPlay jobplay) { this.jobplay = jobplay; } public SPVProject(JobPlay jobplay, String projectFileName) throws ParserConfigurationException, SAXException, IOException { this.projectFileName = projectFileName; this.jobplay = jobplay; FileParser cFileParser = new FileParser(this.projectFileName); paths = cFileParser.getPathlist(); typeAttribute = cFileParser.getAttrlist(); AttributeOfFileToLoad = cFileParser.getAttributeOfFilelist(); yReverseElementToLoad = cFileParser.getyReverseElementslist(); hideElementToLoad = cFileParser.getHideElementslist(); xShiftedElementToLoad = cFileParser.getxShiftElementslist(); yShiftedElementToLoad = cFileParser.getyShiftElementslist(); indexColorPredStr = cFileParser.getIndexColorPred(); indexColorExpStr = cFileParser.getIndexColorExp(); MinX = cFileParser.getMinX(); MaxX = cFileParser.getMaxX(); MinY = cFileParser.getMinY(); MaxY = cFileParser.getMaxY(); } public void open() { int j = 0; try { ArrayList xshift = new ArrayList<>(); ArrayList yshift = new ArrayList<>(); for (Element pl : paths) { jobplay.loadFileByFilename(pl.getTextContent(), typeAttribute.get(j), j); if (typeAttribute.get(j).equals("spectrum") || typeAttribute.get(j).equals("tdsas") || typeAttribute.get(j).equals("hitras")) { if (!yReverseElementToLoad.isEmpty() && yReverseElementToLoad.get(j).equals("true")) { jobplay.getPanAff().yReverse(j); } if (!hideElementToLoad.isEmpty() && hideElementToLoad.get(j).equals("true")) { jobplay.getPanAff().setShow(j, false); jobplay.enableHideMenu(j, false); jobplay.enableShowMenu(j, true); } if (MinX != 0.0 && MaxX != 0.0) jobplay.getPanAff().setXZoom(MinX, MaxX); if (MinY != 0.0 && MaxY != 0.0) jobplay.getPanAff().setYZoom(MinY, MaxY); xshift.add(Double.parseDouble(xShiftedElementToLoad.get(j))); yshift.add(Double.parseDouble(yShiftedElementToLoad.get(j))); } j++; } /* we need to load everything before shifting the curves */ for (int i = 0; i < xshift.size(); i++) { if (xshift.get(i) != 0.0) { jobplay.getPanAff().setXShift(xshift.get(i), i); jobplay.enableXUnshiftMenu(i, true); } if (yshift.get(i) != 0.0) { jobplay.getPanAff().setYShift(yshift.get(i), i); jobplay.enableYUnshiftMenu(i, true); } } /* if success we add the project into the openRecentList */ jobplay.addInOpenRecentList(projectFileName); } catch (Exception e) { // IO error jobplay.removeInOpenRecentList(projectFileName); JOptionPane.showMessageDialog(null, "Cannot Open File " + projectFileName, "Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(new java.io.PrintStream(System.out)); } } private void saveVersion(Document doc, Element Project) { Element Version = doc.createElement("Version"); Project.appendChild(Version); Project.appendChild(doc.createTextNode(Spview.VERSION)); } private void saveZoomValues(Document doc, Element Project) { // Zoom Element Zoom = doc.createElement("Zoom"); Project.appendChild(Zoom); // create xZoomMin node double xmin = jobplay.getPanAff().getbxmi(); Element xZoomMin = doc.createElement("xmin"); xZoomMin.appendChild(doc.createTextNode(Double.toString(xmin))); Zoom.appendChild(xZoomMin); // create xZoomMax node double xmax = jobplay.getPanAff().getbxma(); Element xZoomMax = doc.createElement("xmax"); xZoomMax.appendChild(doc.createTextNode(Double.toString(xmax))); Zoom.appendChild(xZoomMax); // create yZoomMin node double yZoom0 = jobplay.getPanAff().getbymi(); Element yZoomMin = doc.createElement("ymin"); yZoomMin.appendChild(doc.createTextNode(Double.toString(yZoom0))); Zoom.appendChild(yZoomMin); // create yZoomMax node double yZoom1 = jobplay.getPanAff().getbyma(); Element yZoomMax = doc.createElement("ymax"); yZoomMax.appendChild(doc.createTextNode(Double.toString(yZoom1))); Zoom.appendChild(yZoomMax); } private void saveDataFiles(Document doc, Element Project) { for (int i = 0; i < jobplay.getPathsList().size(); i++) { // file element Element File = doc.createElement("File"); Project.appendChild(File); Element path = doc.createElement("path"); path.appendChild(doc.createTextNode(jobplay.getPathsList().get(i))); File.appendChild(path); // create and append attribute to File Attr AttributeOfFile = doc.createAttribute("AttributeOfFile"); AttributeOfFile.setValue(jobplay.getAttributeOfFileList().get(i)); File.setAttributeNode(AttributeOfFile); // create and append attribute to path Attr Type = doc.createAttribute("Type"); Type.setValue(jobplay.getTypeOfFileList().get(i)); path.setAttributeNode(Type); // create yReverse node Element yReverse = doc.createElement("yReverse"); yReverse.appendChild(doc.createTextNode(jobplay.getReverseYElementList().get(i))); File.appendChild(yReverse); // create hide node Element hide = doc.createElement("hide"); hide.appendChild(doc.createTextNode(jobplay.getHideElementList().get(i))); File.appendChild(hide); // create xShift node double xState = jobplay.getPanAff().getXShift(i); Element xShiftState = doc.createElement("xShiftState"); xShiftState.appendChild(doc.createTextNode(Double.toString(xState))); File.appendChild(xShiftState); // create yShift node double yState = jobplay.getPanAff().getYShift(i); Element yShiftState = doc.createElement("yShiftState"); yShiftState.appendChild(doc.createTextNode(Double.toString(yState))); File.appendChild(yShiftState); } } private void savePredictionFile(Document doc, Element Project) { // file element Element File = doc.createElement("File"); Project.appendChild(File); Element path = doc.createElement("path"); path.appendChild(doc.createTextNode(jobplay.getPathOfPredFile())); File.appendChild(path); // create and append attribute to File Attr AttributeOfFile = doc.createAttribute("AttributeOfFile"); AttributeOfFile.setValue(jobplay.getAttributeOfPredFile()); File.setAttributeNode(AttributeOfFile); // create and append attribute to path Attr Type = doc.createAttribute("Type"); Type.setValue(jobplay.getTypeOfPredFile()); path.setAttributeNode(Type); // create color node String indexColorToSavePred = Integer.toString(jobplay.getPanAff().getPfass2()); Element indexColorPred = doc.createElement("indexColorPred"); indexColorPred.appendChild(doc.createTextNode(indexColorToSavePred)); File.appendChild(indexColorPred); } private void saveExperimentFile(Document doc, Element Project) { // file element Element File = doc.createElement("File"); Project.appendChild(File); Element path = doc.createElement("path"); path.appendChild(doc.createTextNode(jobplay.getPathOfExpfile())); File.appendChild(path); // create and append attribute to File Attr AttributeOfFile = doc.createAttribute("AttributeOfFile"); AttributeOfFile.setValue(jobplay.getAttributeOfExpFile()); File.setAttributeNode(AttributeOfFile); // create and append attribute to path Attr Type = doc.createAttribute("Type"); Type.setValue(jobplay.getTypeOfExpFile()); path.setAttributeNode(Type); // create color node String indexColorToSaveExp = Integer.toString(jobplay.getPanAff().getEfass2()); Element indexColorExp = doc.createElement("indexColorExp"); indexColorExp.appendChild(doc.createTextNode(indexColorToSaveExp)); File.appendChild(indexColorExp); } /** * Save the current project to an XML file with .spv extension * * @since SPVIEW2 */ private File pickAFileName(String nameSuggestion) { // This method creates an XML File to save the file(s) added in the project. File selectedFile; // choose the project file JFileChooser jfcfile = new JFileChooser(nameSuggestion); // default choice directory FileNameExtensionFilter filter = new FileNameExtensionFilter("Project file (*.spv)", "spv"); // Only files .spv jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only jfcfile.setDialogTitle("Save Project"); jfcfile.addChoosableFileFilter(filter); // add the filter created above jfcfile.setAcceptAllFileFilterUsed(false); // All types of file are NOT accepted jfcfile.setSelectedFile(new File(nameSuggestion)); if (jfcfile.showSaveDialog(jobplay.getParent()) == JFileChooser.APPROVE_OPTION) { selectedFile = jfcfile.getSelectedFile(); if (!selectedFile.getAbsolutePath().endsWith(".spv")) { selectedFile = new File(jfcfile.getSelectedFile() + ".spv"); } if (!selectedFile.exists() || (selectedFile.exists() && JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(jobplay.getParent(), "File " + selectedFile.getAbsolutePath() + " already exists! Overwrite?", "Save", JOptionPane.YES_NO_OPTION))) { return selectedFile; } } return null; } public boolean saveSPVFile(String nameSuggestion, boolean fileExist) { boolean ret = false; if (jobplay.getNbOfDataFile() == 0) return false; File pickedFile; if (fileExist) pickedFile = new File(nameSuggestion); else pickedFile = pickAFileName(nameSuggestion); if (pickedFile != null) { // Sort the attributes of each type of file, first Predfile, then Expfile for (int i = 0; i < AttributeOfFileToLoad.size(); i++) { // Sort the attributes "TDS" or "HITRAN" if (AttributeOfFileToLoad.get(i).equals("predread")) { jobplay.setAttributefPredFile(AttributeOfFileToLoad.get(i)); jobplay.setTypeOfPredFile(typeAttribute.get(i)); jobplay.setPathOfPredFile(jobplay.getPredFileName()); } // Sort the attributes "peakExp" if (AttributeOfFileToLoad.get(i).equals("expread")) { jobplay.setAttributefExpFile(AttributeOfFileToLoad.get(i)); jobplay.setTypeOfExpFile(typeAttribute.get(i)); jobplay.setPathOfExpFile(jobplay.getExpFileName()); } } if (jobplay.getNbOfDataFile() > 0 && jobplay.getTypeOfFileList().size() + jobplay.getPredfileToSave() + jobplay.getExpfileToSave() > 0) { try { // --- instantiate a project file --- // DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbFactory.newDocumentBuilder(); Document doc = docBuilder.newDocument(); // Project root element Element Project = doc.createElement("Project"); doc.appendChild(Project); saveVersion(doc, Project); saveZoomValues(doc, Project); saveDataFiles(doc, Project); // The prediction and experimentation files have to be added to the project // AFTER the data files, otherwise there would be a shift in the colors. if (jobplay.getPredfileToSave() == 1) { savePredictionFile(doc, Project); } if (jobplay.getExpfileToSave() == 1) { saveExperimentFile(doc, Project); } // Write the content into xml file TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(pickedFile); transformer.transform(source, result); } catch (TransformerException | ParserConfigurationException tfe) { tfe.printStackTrace(); } // handle exception creating TransformerFactory catch (TransformerFactoryConfigurationError factoryError) { System.err.println("Error creating " + "TransformerFactory"); factoryError.printStackTrace(); } this.setUnsave(false); System.out.println("Your project has been saved succesfully"); ret = true; } } return ret; } public int getIndexColorPred() { return indexColorPredStr; } public int getIndexColorExp() { return indexColorExpStr; } public ArrayList getAttributeOfFileList() { return AttributeOfFileToLoad; } public boolean isUnsave() { return unsave; } public void setUnsave(boolean unsave) { this.unsave = unsave; } } sources/org/spview/residuals/ObsCalc.java000644 001750 001750 00000006272 14325453236 021335 0ustar00cyrilcyril000000 000000 package org.spview.residuals; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.JOptionPane; import org.spview.point.ResidualPoint; import org.spview.filehandler.ExpFile; import org.spview.filehandler.PredFile; import org.spview.point.ExpJsynPoint; import org.spview.gui.JFCreatePeakFile; import org.spview.gui.JobPlay; import org.spview.gui.PlotData; public class ObsCalc { private final ExpFile expFile; private final PredFile predFile; private PlotData mainPanel; public ObsCalc(ExpFile expFile, PredFile predFile) throws Exception { if (expFile == null || expFile.getNbxy() == 0 || predFile == null || predFile.getNbxy() == 0) { throw new Exception("Invalid data"); } this.expFile = expFile; this.predFile = predFile; } public ArrayList getFreqResiduals() { int n = this.expFile.getNbxy(); if (n < 1) return null; ArrayList XResiduals = new ArrayList<>(); ArrayList[] expPoint = expFile.getEjsynpt(); for (int i = 0; i < n; i++) { for (ExpJsynPoint uniqPoint : expPoint[i]) { int index = uniqPoint.getIpred(); if (index != -1 && uniqPoint.getFaso().equals("+")) { double diff = (this.expFile.getX(i) - this.predFile.getX(index)) * 1E3; XResiduals.add(new ResidualPoint(this.expFile.getX(i), diff, uniqPoint.getJsyn())); } } } return XResiduals.isEmpty() ? null : XResiduals; } public ArrayList getIntResiduals() { int n = this.expFile.getNbxy(); if (n < 1) return null; ArrayList YResiduals = new ArrayList<>(); ArrayList[] expPoint = expFile.getEjsynpt(); for (int i = 0; i < n; i++) { for (ExpJsynPoint uniqPoint : expPoint[i]) { int index = uniqPoint.getIpred(); if (index != -1 && uniqPoint.getSaso().equals("+")) { // 100*(obs-calc)/obs double diff = 100 * (this.expFile.getY(i) - this.predFile.getY(index)) / this.expFile.getY(i); YResiduals.add(new ResidualPoint(this.expFile.getX(i), diff, uniqPoint.getJsyn())); } } } return YResiduals.isEmpty() ? null : YResiduals; } public void show(JobPlay jobplay, String type) { List data = type.equals("X") ? getFreqResiduals() : getIntResiduals(); if (data == null) { JOptionPane.showMessageDialog(null, "There is no data to display!", "No data", JOptionPane.WARNING_MESSAGE); return; } mainPanel = new PlotData(jobplay, data); mainPanel.setPreferredSize(new Dimension(800, 600)); JFrame frame; if (type.equals("X")) { frame = new JFrame("Frequency Obs-Cal Display"); } else { frame = new JFrame("Intensity Obs-Cal Display"); } frame.setIconImage(Toolkit.getDefaultToolkit().getImage(JFCreatePeakFile.class.getResource("/pixmaps/spview.png"))); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.addWindowListener(new WindowAdapter() { // clean end public void windowClosing(WindowEvent e) { if (mainPanel.getJFT() != null) { mainPanel.getJFT().dispose(); } } }); } } resources/JFSelPred/jmilsshow.html000644 001750 001750 00000000234 14000365526 020061 0ustar00cyrilcyril000000 000000

Local simulation

Show local simulation based on selected predictions.

resources/JobPlay/jmiloadd2hsas.html000644 001750 001750 00000000121 14000365526 020343 0ustar00cyrilcyril000000 000000 Load a prediction file in D2hStark format to plot it as sticks in the data area. sources/org/spview/gui/JFSimul.java000644 001750 001750 00000066754 14205637134 020142 0ustar00cyrilcyril000000 000000 package org.spview.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; //import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Locale; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import org.spview.filehandler.FortranFormat; /* * Class to calculate local simulation spectrum through a simul job; called by JFSelPred */ /** * This panel creates and runs a job to calculate a local simulation spectrum * from (area) selected predictions. */ public class JFSimul extends JFrame implements ActionListener { /** * */ private static final long serialVersionUID = -3391496760301771994L; private final String playd; // SPVIEW installation directory private final JFSelPred calling; private double[] lsx; // loc-sim spectrum X private double[] lsy; // loc-sim spectrum Y // simulation parameters private final JFormattedTextField jftfumin; // for vumin private float vumin; // lower frequency cm-1 private final JFormattedTextField jftfumax; // for vumax private float vumax; // upper frequency cm-1 private final JFormattedTextField jftftemp; // for vtemp private float vtemp; // temperature K private final JFormattedTextField jftfres; // for vres private float vres; // resolution cm-1 private final JFormattedTextField jftfpaf; // for vpaf private float vpaf; // Drawing Steps Size cm-1 private final JFormattedTextField jftfthres; // for vthres private float vthres; // Intensity Threshold cm-2/atm private final JComboBox jcbsptype; // for tsptype private String nsptype; // Spectrum type private final String[] tappf; // apparatus function choice array private final JComboBox jcbappf; // for tappf private String nappf; // Apparatus function private final JFormattedTextField jftfcdae; // for cdae private float cdae; // Press Broadening Coeff cm-1.atm-1 private final JFormattedTextField jftfmass; // for mass private float mass; // Molar Mass private final JFormattedTextField jftfptt; // for ptt private float ptt; // Total Pressure Torr private final JButton jbset; // set button // variables private final String tempod; // SPVIEW tempo directory private final String njobf; // job file private boolean jobexist; // job file exist status private PrintWriter out1; // to write job private final String lfil; // line file private final String nerrf; // error file private final String noutf; // output file private Process monproc; // various processes private final String nxyf; // simulated spectrum file private BufferedReader br; private final boolean verbose = true; private final String lnsep; // line separator private final String fisep; // file separator ///////////////////////////////////////////////////////////////////// /** * Construct a new JFSimul. * * @param ccalling calling JFSelPred * @param cix0 window X location * @param ciy0 window Y location */ public JFSimul( JFSelPred ccalling, int cix0, int ciy0 ) { playd = System.getProperty("spview.home"); // SPVIEW installation directory calling = ccalling; // calling JFSelPred lnsep = System.getProperty("line.separator"); fisep = System.getProperty("file.separator"); // default cdae double dcdae = .09; // default cdae // format definition // nff = NumberFormat.getNumberInstance(Locale.US); // ask a plain format // dff = (DecimalFormat)nff; // reduce to decimal format // dff.applyPattern("00000.000000"); // define pattern // nfi = NumberFormat.getNumberInstance(Locale.US); // ask a plain format // dfi = (DecimalFormat)nfi; // reduce to decimal format // dfi.applyPattern("0.00E00"); // define pattern tempod =playd+fisep+"tempo"; // SPVIEW tempo directory njobf = tempod+fisep+"locsim_job"; // job file jobexist = false; // job file MUST be created lfil = tempod+fisep+"locsim_lines"; // line file nerrf = njobf+"_err"; // error file noutf = njobf+"_out"; // output file nxyf = tempod+fisep+"simul.xy"; // simulated spectrum file setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); //setLayout(new BorderLayout()); getContentPane().setLayout(new BorderLayout()); setLocation(cix0, ciy0); // panel to show and get local simulation parameters // panels JPanel pcentre = new JPanel(new GridLayout(11, 2, 5, 5)); // SIMULATION PARAMETERS // formats // Number Format NumberFormat nffloat = NumberFormat.getInstance(Locale.US); // float nffloat.setGroupingUsed(false); // NO grouping nffloat.setMaximumFractionDigits( 7); // fraction digits // UMIN pcentre.add(new JLabel(" Lower Frequency Limit (cm-1) ",null,JLabel.LEFT)); jftfumin = new JFormattedTextField(nffloat); jftfumin.setValue(0.f); pcentre.add(jftfumin); // UMAX pcentre.add(new JLabel(" Upper Frequency Limit (cm-1) ",null,JLabel.LEFT)); jftfumax = new JFormattedTextField(nffloat); jftfumax.setValue(0.f); pcentre.add(jftfumax); // TEMP pcentre.add(new JLabel(" Temperature (K) ",null,JLabel.LEFT)); jftftemp = new JFormattedTextField(nffloat); jftftemp.setValue(0.f); pcentre.add(jftftemp); // RES pcentre.add(new JLabel(" Resolution (cm-1) ",null,JLabel.LEFT)); jftfres = new JFormattedTextField(nffloat); jftfres.setValue(0.f); pcentre.add(jftfres); // PAF pcentre.add(new JLabel(" Drawing Steps Size (cm-1) ",null,JLabel.LEFT)); jftfpaf = new JFormattedTextField(nffloat); jftfpaf.setValue(0.f); pcentre.add(jftfpaf); // THRES pcentre.add(new JLabel(" Intensity Threshold (cm-2/atm) ",null,JLabel.LEFT)); jftfthres = new JFormattedTextField(nffloat); jftfthres.setValue(0.f); pcentre.add(jftfthres); // TYPE // spectrum type choice array String[] tsptype = new String[3]; tsptype[0] = "trans"; tsptype[1] = "raman"; tsptype[2] = "absor"; pcentre.add(new JLabel(" Spectrum Type ",null,JLabel.LEFT)); jcbsptype = new JComboBox<>(tsptype); jcbsptype.addItem(""); jcbsptype.setSelectedItem(""); // default to space jcbsptype.setBackground(Color.WHITE); nsptype = ""; pcentre.add(jcbsptype); // APPF tappf = new String[4]; tappf[0] = "dirac"; tappf[1] = "sinc"; tappf[2] = "sinc2"; tappf[3] = "gauss"; pcentre.add(new JLabel(" Apparatus Function ",null,JLabel.LEFT)); jcbappf = new JComboBox<>(tappf); jcbappf.addItem(""); jcbappf.setSelectedItem(""); // default to space jcbappf.setBackground(Color.WHITE); nappf = ""; pcentre.add(jcbappf); // CDAE pcentre.add(new JLabel(" Press Broad Coeff (cm-1.atm-1) ")); jftfcdae = new JFormattedTextField(nffloat); jftfcdae.setValue((float) dcdae); pcentre.add(jftfcdae); // PTT pcentre.add(new JLabel(" Total Pressure (Torr) ")); jftfptt = new JFormattedTextField(nffloat); jftfptt.setValue(0.f); pcentre.add(jftfptt); // MASS pcentre.add(new JLabel(" Molar Mass ")); jftfmass = new JFormattedTextField(nffloat); jftfmass.setValue(0.f); pcentre.add(jftfmass); pcentre.setBorder(BorderFactory.createTitledBorder("SIMULATION PARAMETERS")); // south panel jbset = new JButton("Set"); jbset.setToolTipText("Set local simulation parameters"); jbset.setBackground(Color.WHITE); jbset.addActionListener(this); // box for jbset Box boxsud = Box.createHorizontalBox(); // add button to box boxsud.add(jbset); JPanel psud = new JPanel(); psud.setBorder(BorderFactory.createLineBorder(Color.BLACK)); psud.add(boxsud); // add box // add panels //add(pcentre,"Center"); getContentPane().add(pcentre,"Center"); //add(psud,"South"); getContentPane().add(psud,"South"); setVisible(false); pack(); } ///////////////////////////////////////////////////////////////////// /** * Process events. */ public void actionPerformed(ActionEvent evt) { // set button if( evt.getSource() == jbset ) { if( testCJP( verbose ) ) { // good new parameters jobexist = false; // job file MUST be created setVisible(false); calling.simParmSet(); // warn JFSelPred } } } ///////////////////////////////////////////////////////////////////// // test parameters private boolean testCJP( boolean verb ) { // SIMULATION PARAMETERS // UMIN vumin = ((Number)jftfumin.getValue()).floatValue(); if( vumin < 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Lower Frequency >= 0 requested"); } return false; } // UMAX vumax = ((Number)jftfumax.getValue()).floatValue(); if( vumax <= vumin ) { if( verb ) { JOptionPane.showMessageDialog(null,"Upper Frequency > Lower Frequency requested"); } return false; } // TEMP vtemp = ((Number)jftftemp.getValue()).floatValue(); if( vtemp < 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Temperature >= 0 requested"); } return false; } // RES vres = ((Number)jftfres.getValue()).floatValue(); if( vres < 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Resolution >= 0 requested"); } return false; } // PAF vpaf = ((Number)jftfpaf.getValue()).floatValue(); if( vpaf <= 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Drawing Step Size > 0 requested"); } return false; } if( vpaf >= vres ) { if( verb ) { JOptionPane.showMessageDialog(null,"Drawing Step Size < Resolution requested"); } return false; } // THRES vthres = ((Number)jftfthres.getValue()).floatValue(); if( vthres < 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Intensity Thershold >= 0 requested"); } return false; } // SPECTRUM TYPE nsptype = (String)jcbsptype.getSelectedItem(); assert nsptype != null; if(nsptype.equals("")) { if( verb ) { JOptionPane.showMessageDialog(null,"You have to first define the Spectrum Type"); } return false; } // APPF nappf = (String)jcbappf.getSelectedItem(); assert nappf != null; if(nappf.equals("")) { if( verb ) { JOptionPane.showMessageDialog(null,"You have to first define the Apparatus Function"); } return false; } // line file parameters test // CDAE cdae = ((Number)jftfcdae.getValue()).floatValue(); if( cdae < 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Press Broad Coeff >= 0 requested"); } return false; } // MASS mass = ((Number)jftfmass.getValue()).floatValue(); if( mass <= 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Molar Mass > 0 requested"); } return false; } // PTT ptt = ((Number)jftfptt.getValue()).floatValue(); if( ptt < 0 ) { if( verb ) { JOptionPane.showMessageDialog(null,"Total Pressure >= 0 requested"); } return false; } // simul.f limits double refl = 0.; // FWHM reference double pdlmh; // FWHM nb of steps if( nappf.equals(tappf[0]) ) { // DIRAC vres = (float) 0.; // resolution = 0 } double tef; // effective temperature double sl; // LORENTZ double sd; // DOPPLER if( nappf.equals(tappf[3]) ) { // GAUSS sd = (3.581097E-7)*Math.sqrt(vtemp/mass)*(vumin+vumax)/2.; tef = vtemp*(1.+Math.pow(vres/sd,2.)); } else { tef = vtemp; } sl = cdae*ptt/760.; // 760 <- atmospheres sd = (3.581097E-7)*Math.sqrt(tef/mass)*(vumin+vumax)/2.; if(nsptype.equals("raman")) { sd = Math.sqrt(sd*sd+vres*vres); } refl = Math.max(refl,sl); refl = Math.max(refl,sd); // sampling test refl = 2.*refl; if( vres > refl && (! nappf.equals(tappf[0])) ) { // NOT DIRAC refl = vres; } pdlmh = refl/vpaf; if( pdlmh < 3. ) { if( verb ) { JOptionPane.showMessageDialog(null,"Sampling too sparse" +lnsep+ pdlmh+" drawing steps in a full width at half maximum"+lnsep+ "Has to be >= 3"); } return false; } if( pdlmh > 20. ) { if( verb ) { JOptionPane.showMessageDialog(null,"Sampling too dense" +lnsep+ pdlmh+" drawing steps in a full width at half maximum"+lnsep+ "Should be <= 20"); } //return false; // only a warning in simul.f } // everything is OK return true; } /** * Calculate local simulation spectrum. * * @param cpredx predictions X * @param cpredy predictions Y */ public boolean calLocSim( double[] cpredx, double[] cpredy ) { if( ! testCJP( ! verbose ) ) { // simulation parameters NOT OK JOptionPane.showMessageDialog(null,"You have to define local simulation parameters"); setVisible(true); return false; } if( ! createLines( cpredx, cpredy ) ) { // line file NOT OK return false; } if( ! jobexist ) { if( ! createJob() ) { // job NOT OK return false; } } if( ! runJob() ){ // run NOT OK return false; } return readLsxy(); } // Create line file following FORMAT 2000 of simul.f private boolean createLines(double[] cpredx, double[] cpredy) { try { out1 = new PrintWriter(new BufferedWriter(new FileWriter(lfil))); for (int i = 0; i < cpredx.length; i++) { // for each selected pred point out1.println(FortranFormat.formFreq(cpredx[i]) + FortranFormat.formInt(cpredy[i])); } } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null, "IO error while writing file" + lnsep + lfil + lnsep + ioe); return false; } finally { // close the file if (out1 != null) { out1.close(); if (out1.checkError()) { JOptionPane.showMessageDialog(null, "PrintWriter error while creating line file" + lnsep + lfil); return false; } } } return true; } // Create job private boolean createJob() { try { out1 = new PrintWriter( new BufferedWriter( new FileWriter( njobf ) ) ); out1.println("#! /bin/sh"); out1.println("##"); out1.println("## Local simulation job created by SPVIEW"); out1.println("##"); out1.println("BASD="+playd+fisep+"packages"+fisep+"PKF"); out1.println("##"); out1.println(" SCRD=$BASD"+fisep+"prog"+fisep+"exe"); out1.println("##"); out1.println("## SIMULATION PARAMETERS"); out1.println("##"); out1.println(" UMIN="+vumin); out1.println(" UMAX="+vumax); out1.println(" TEMP="+vtemp); out1.println(" RES="+vres); out1.println(" PAF="+vpaf); out1.println(" THRES="+vthres); out1.println("##"); out1.println(" LFIL="+lfil); out1.println(" CDAE="+cdae); out1.println(" MASS="+mass); out1.println(" UK="+1.); out1.println(" PTT="+ptt); if(!nsptype.equals("raman")) { out1.println(" PPT=$PTT"); out1.println(" CL="+1.); } out1.println("##"); out1.println(" APPF="+nappf); out1.println("##"); out1.println("## SIMULATION"); out1.println("##"); out1.println(" $SCRD"+fisep+"passx simul "+nsptype+" $UMIN $UMAX $TEMP $RES $PAF $THRES \\"); out1.print (" "); // 1st out1.print (" $LFIL $CDAE $MASS $UK $PTT"); if(!nsptype.equals("raman")) { out1.println(" $PPT $CL \\"); } else { out1.println(" \\"); } out1.println(" end $APPF"); out1.println("##"); } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null,"IO error while writing file"+lnsep+ njobf +lnsep+ ioe); return false; } finally { // close the file if (out1 != null) { out1.close(); if( out1.checkError() ) { JOptionPane.showMessageDialog(null,"PrintWriter error while creating job file"+lnsep+ njobf); return false; } } } try { monproc = Runtime.getRuntime().exec("chmod u+x "+njobf); // allow execution } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null,"IO error while changing rights of file"+lnsep+ njobf +lnsep+ ioe); return false; } return true; } // run job and test error file private boolean runJob() { // command String ncmd = "cd " + tempod + " ; " + njobf + " >" + noutf + " 2>" + nerrf; // change to tempo directory // run command in a shell try { String[] cmd = {"/bin/sh", "-c", ncmd}; monproc = Runtime.getRuntime().exec(cmd); } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null,"IO error while running command"+lnsep+ ncmd +lnsep+ ioe); return false; } try { monproc.waitFor(); // wait for end of job } catch (InterruptedException ie) { // error JOptionPane.showMessageDialog(null,"InterruptedException while running command"+lnsep+ ncmd); return false; } // error file has to be empty File errf = new File( nerrf ); if( errf.length() != 0 ) { JOptionPane.showMessageDialog(null,"Error while running local simulation job"+lnsep+ "See : "+nerrf); return false; } return true; } // Read lsx and lsy from simul.xy following FORMAT 2001 of simul.f private boolean readLsxy() { String str; // to read file double cx; // to keep X double cy; // to keep Y // to keep points, unknown nb of points ArrayList alx = new ArrayList<>(); // frequency ArrayList aly = new ArrayList<>(); // intensity try{ File f = new File( nxyf ); // the file br = new BufferedReader( new FileReader( f ) ); // open while ((str = br.readLine()) != null) { // line read try { // read data cx = Double.parseDouble(str.substring(0, 12)); // Double for X cy = Double.parseDouble(str.substring(12,25)); // Double for Y } catch (NumberFormatException e) { // format error continue; // skip the line } alx.add(cx); // keep data aly.add(cy); } } catch(IOException ioe) { // IO error JOptionPane.showMessageDialog(null,"IO error while reading file"+lnsep+ nxyf +lnsep+ ioe); return false; } finally { // close if (br !=null) { try { br.close(); } catch (IOException ignored) { } } } // set lsx, lsy int nbxy = alx.size(); // nb of points if( nbxy == 0 ) { // no point JOptionPane.showMessageDialog(null,"No valid data found in file"+lnsep+ nxyf); return false; // invalid read } lsx = new double[nbxy]; // create arrays lsy = new double[nbxy]; for(int i=0; i { timer.stop(); callback.run(); }); } public void update() { if (!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater(() -> timer.restart()); } else { timer.restart(); } } }resources/JobPlay/jmipred2.html000644 001750 001750 00000000262 14000365526 017341 0ustar00cyrilcyril000000 000000

Associate prediction to data

Associate the prediction file to one of the loaded data files.

sources/org/spview/filehandler/DataFile.java000644 001750 001750 00000012337 14205450376 021760 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; /* * Class for data files (spectrum or stick/peakas/tdsas/hitras). */ import java.awt.geom.Point2D; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.StringTokenizer; import javax.swing.JOptionPane; import org.spview.point.StickPoint; //////////////////////////////////////////////////////////////////// /** * This class defines spectrum or stick data. */ public class DataFile { private final String name; // file name private String ntfile; // type private double[] x; // X data private double[] y; // Y data private int nbxy; // nb of points private BufferedReader br; private final String lnsep; // line separator //////////////////////////////////////////////////////////////////// /** * Construct a new DataFile. * * @param cname file name */ public DataFile(String cname) { name = cname; // file name nbxy = 0; // nb of points lnsep = System.getProperty("line.separator"); } /** * Get name selection string. */ public String getname() { return name; } //////////////////////////////////////////////////////////////////// /** * Read file.
* validity is tested following :
* FORMAT 1000 of spech.f (HITRAN format prediction)
* FORMAT 1022 of spect.f (TDS format prediction)
* FORMAT 1024 of spects.f (D2hStark format prediction) */ public boolean read() { ArrayList alx = new ArrayList<>(); // frequency ArrayList aly = new ArrayList<>(); // intensity // binary files OpusFile opusFile = new OpusFile(name); try { if (opusFile.read()) { if (opusFile.getCancelled()) { /* User cancelled the operation */ return false; } alx = opusFile.get_XArray(); aly = opusFile.get_YArray(); } else { // flat files try { double cx; // to keep X double cy; // to keep Y Point2D data; // to keep points, unknown nb of points br = new BufferedReader(new FileReader(name)); // open // to read file String str; while ((str = br.readLine()) != null) { // line read switch (ntfile) { case "peakas": // peak or assignment file as stick if (str.length() < 31) { // line too short continue; // skip it } try { // read data cx = Double.parseDouble(str.substring(6, 18)); // Double for X cy = Double.parseDouble(str.substring(20, 31)); // Double for Y data = new Point2D.Double(cx, cy); } catch (NumberFormatException e) { // format error continue; // skip the line } break; case "hitras": data = HITRANFile.extractXY(str); break; case "tdsas": data = TDSFile.extractXY(str); break; case "pickettas": data = PickettFile.extractXY(str); break; default: // spectrum or stick file str = str.replace(',', ' '); // comma as separator (-> space) StringTokenizer st = new StringTokenizer(str); // separate X and Y if (st.countTokens() < 2) { // not enough tokens continue; // skip the line } try { cx = Double.parseDouble(st.nextToken()); // Double for X cy = Double.parseDouble(st.nextToken()); // Double for Y data = new Point2D.Double(cx, cy); } catch (NumberFormatException e) { // format error continue; // skip the line } break; } if (data != null) { alx.add(data.getX()); // keep data aly.add(data.getY()); } } } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null, "IO error while reading file" + lnsep + name + lnsep + ioe); return false; } finally { // close if (br != null) { try { br.close(); } catch (IOException ignored) { } } } } } catch (IOException e) { e.printStackTrace(); } // save data int cnbxy = alx.size(); // current nb of points if (cnbxy == 0) { // no point JOptionPane.showMessageDialog(null, "No valid data found in file" + lnsep + name); return false; // invalid read } nbxy = cnbxy; x = new double[nbxy]; // create arrays y = new double[nbxy]; // sort StickPoint[] stickpoint = new StickPoint[nbxy]; // compare X for (int i = 0; i < nbxy; i++) { stickpoint[i] = new StickPoint(alx.get(i), aly.get(i)); // X, Y } Arrays.sort(stickpoint); // sort for (int i = 0; i < nbxy; i++) { // save data x[i] = stickpoint[i].getX(); y[i] = stickpoint[i].getY(); } // happy end return true; } /** * Set file type. * * @param cntfile file type (spectrum/stick/peakas/tdsas/hitras) */ public void setType(String cntfile) { ntfile = cntfile; } /** * Get file type. */ public String getType() { return ntfile; } /** * Get number of data points. */ public int getNbxy() { return nbxy; } /** * Get X array. */ public double[] getX() { return x; } /** * Get X value. */ public double getX(int id) { return x[id]; } /** * Get Y array. */ public double[] getY() { return y; } /** * Get Y value. */ public double getY(int id) { return y[id]; } } resources/PanAff/jmishowbar.html000644 001750 001750 00000000247 14000365526 017570 0ustar00cyrilcyril000000 000000

Vertical bar

Set a vertical bar which helps to visualize frequency coincidences.

resources/JobPlay/jmiopenproject.html000644 001750 001750 00000000371 14000365526 020656 0ustar00cyrilcyril000000 000000

Open Project

Open a New Project. A project is a file ending with the ".spv" extension and created by SPVIEW: it only contains file paths embedded in a xml format.

sources/org/spview/preferences/WindowHandler.java000644 001750 001750 00000005247 14046454633 023105 0ustar00cyrilcyril000000 000000 package org.spview.preferences; import java.awt.Dimension; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.util.prefs.Preferences; import javax.swing.JFrame; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class WindowHandler { public static void registerFrame(JFrame frame, String frameUniqueId, int defaultX, int defaultY, int defaultW, int defaultH) { Preferences prefs = Preferences.userRoot().node(WindowHandler.class.getSimpleName() + "-" + frameUniqueId); Dimension savedDim = getFrameSize(prefs, defaultW, defaultH); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // The following test is needed on macOS. if ((savedDim.getWidth() >= screenSize.getWidth()) || (savedDim.getHeight() >= screenSize.getHeight())) { frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH); } else { frame.setSize(savedDim); frame.setLocation(getFrameLocation(prefs, defaultX, defaultY)); } CoalescedEventUpdater updater = new CoalescedEventUpdater(400, () -> updatePref(frame, prefs)); frame.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { updater.update(); } @Override public void componentMoved(ComponentEvent e) { updater.update(); } }); } public static void setLookAndFeel() { if (isMacOS()) { // take the menu bar off the jframe System.setProperty("apple.laf.useScreenMenuBar", "true"); // set the look and feel try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { e.printStackTrace(); } } } private static boolean isMacOS() { String lcOSName = System.getProperty("os.name").toLowerCase(); return lcOSName.startsWith("mac os x"); } private static void updatePref(JFrame frame, Preferences prefs) { // System.out.println("Updating preferences"); Point location = frame.getLocation(); prefs.putInt("x", location.x); prefs.putInt("y", location.y); Dimension size = frame.getSize(); prefs.putInt("w", size.width); prefs.putInt("h", size.height); } private static Dimension getFrameSize(Preferences pref, int defaultW, int defaultH) { int w = pref.getInt("w", defaultW); int h = pref.getInt("h", defaultH); return new Dimension(w, h); } private static Point getFrameLocation(Preferences pref, int defaultX, int defaultY) { int x = pref.getInt("x", defaultX); int y = pref.getInt("y", defaultY); return new Point(x, y); } }sources/org/spview/gui/JFText.java000644 001750 001750 00000004053 14176570742 017765 0ustar00cyrilcyril000000 000000 package org.spview.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; /* * Class to show a string array */ ///////////////////////////////////////////////////////////////////// /** * Window to show a string array. */ public class JFText extends JFrame { /** * */ private static final long serialVersionUID = 1L; ///////////////////////////////////////////////////////////////////// public JFText(ArrayList list, int cp_x, int cp_y) { super("List of points"); // main window setResizable(false); setAlwaysOnTop(true); addWindowListener(new WindowAdapter() { // clean end public void windowClosing(WindowEvent e) { dispose(); // free window } }); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); getContentPane().setLayout(new BorderLayout()); JPanel jp = new JPanel(new BorderLayout()); getContentPane().add(jp, "Center"); // panels // panels JPanel pcentre = new JPanel(); // center JTextArea jta = new JTextArea(); int ml = 0; // max length of strings for (String s : list) { if (s.length() > ml) { ml = s.length(); } } jta.setColumns(ml); // jta width for (String s : list) { jta.append(s); // add strings } jta.setBackground(Color.BLACK); jta.setForeground(Color.WHITE); jta.setEditable(false); // not editable jta.setFont(new Font("Monospaced", Font.PLAIN, 15)); jta.setLineWrap(false); // no line wrap jta.setRows(10); // nb of lines // to show the string array // with lifts JScrollPane jsp = new JScrollPane(jta, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); try { jta.setCaretPosition(0); // at the top of the text } catch (IllegalArgumentException ignored) { } pcentre.add(jsp); // insert panels jp.add(pcentre, "Center"); setLocation(cp_x, cp_y); pack(); } }sources/org/spview/gui/PanAff.java000644 001750 001750 00000562075 14211661562 017760 0ustar00cyrilcyril000000 000000 package org.spview.gui; /* * Class to draw spectra and assignments */ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.io.*; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; import javax.swing.Box; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import org.spview.point.ExpJsynPoint; import org.spview.point.PredJsynPoint; import org.spview.filehandler.ExpFile; import org.spview.filehandler.DataFile; import org.spview.filehandler.PredFile; import org.spview.filehandler.FortranFormat; //////////////////////////////////////////////////////////// /** * This panel is used to paint the graphs. */ public class PanAff extends JPanel implements ActionListener, MouseListener, MouseMotionListener, Printable { public final static String EXASG_DEFAULT = "SPVIEW_EXASG "; /** * */ private static final long serialVersionUID = -6454762258521638796L; private static double threshold = -1; private final JobPlay jobplay; // calling JobPlay private final String workd; // working directory // data private final String[] ntfile; // data file type private final int[] nbxy; // number of points in data file private final double[][] x; // frequency private final double[][] y; // intensity private final double[] sxmi; // X min (including shift) private final double[] sxma; // X max (including shift) private final int[] ixdeb; // window first point index private final int[] ixfin; // window last point index private final boolean[] use_red; // use reduced data private final int[][] i_red; // index of reduced data private final int[] nbxy_red; // reduced number of points private final int[] ixdeb_red; // window first reduced point index private final int[] ixfin_red; // window last reduced point index private final boolean[] show; // show the data file private final boolean[] yreverse; // Y reverse the data file private final double[] xshifted; // X shift value private final double[] yshifted; // Y shift value private final boolean[] dfready; // data file ready // first tick , interval, nb of intervals, last tick private final double[] acadreX = {0., 0., 0., 0.}; // global frequency private final double[] acadreY = {0., 0., 0., 0.}; // global intensity private final double[] acadrepasY = {0., 0., 0., 0.}; // pred-as-stick global intensity private final Color[] coul; // colors private final int nbcoul; // number of colors private final JTextArea jta; // for selected exp and pred characteristics // mouse position private final JLabel jlx; // X in data scale private final JLabel jly; // Y in data scale private final JLabel jlypredas; // Y in pred-as-stick scale // data file area popup menu private final JPopupMenu jpmdata; private final JMenuItem jmishowbar; // show-set vertical bar private final JMenuItem jmihidebar; // hide vertical bar private final JMenuItem jmizoomx; // X zoom private final JMenuItem jmizoomy; // Y zoom private final JMenuItem jmizoompasy; // pred_as_stick Y zoom private final JMenu jmrescalepasy; // pred-as-stick Y rescale private final JMenuItem jmirestorex; // X restore private final JMenuItem jmirestorey; // Y restore private final JMenuItem jmirestorepasy; // pred-as-stick Y restore private final String[] xaso = {" ", "+", "-"}; // possible values for frequency and intensity marks private final int nbxaso; // number of values // for X/Y rescale private final int[] fact = {2, 5, 10, 20, 50, 100}; // scaling factors private final int nbfact; // number of scaling factors private final int nbscalec; // number of scaling choices private final JMenuItem[] jmivalexpx; // frequency choices private final JMenuItem[] jmivalexpy; // intensity choices private final JMenuItem[] jmivalexppasy; // pred-as-stick intensity choices private final String lnsep; // line separator private final Font mono14; // Monospaced 14 font private Graphics2D g2d; // to be able to rotate text private boolean printing; // print status // all variables beginning with p_... contain pixel values (int or double) // others are in their own units : frequency, intensity // the following are related only to screen drawing private int p_xmouse; // x mouse position private int p_ymouse; // y mouse position private int p_wxma; // window width private int p_wyma; // window height private int prevp_wxma; // previous p_wxma private int p_cxmi; // x min of data area private int p_cxma; // x max of data area private int p_cwidth; // x range private int p_epymi; // exp/pred area y lower limit private int p_yatma; // exp area y upper limit private int p_yprmi; // pred area y lower limit private int p_yprma; // pred area y upper limit private int p_epyma; // exp/pred area y upper limit private int p_gap; // gap along axis private int p_cymi; // y min of data area private int p_cyma; // y max of data area private int p_cheight; // y range private int p_ovray; // oval ray private int p_ovwidth; // oval long ray private int p_xhalf; // half fuzzy width private int nbdf; // number of data files private int nbpredasf; // number of pred-as-stick files // exp private ExpFile expf; // experiment file private int efnbxy; // number of points in exp file private double[] efx; // frequency private String[] effasog; // global frequency mark private double[] efy; // intensity private String[] efsasog; // global intensity mark private String[] efsdobs; // frequency and intensity standard deviations string private ArrayList[] efejsynpt; // exp assignment point private String[] efexasg; // EXASG private int efixdeb; // window first point index private int efixfin; // window last point index private int efass2; // associated data file index private ArrayList alexasg; // EXASG choice private String[] exasg; // easy access EXASG private int nbexasg; // number of EXASG private String defexasg; // default EXASG private boolean efready; // exp file ready // pred private int pfnbxy; // number of points in pred file private double[] pfx; // frequency private double[] pfy; // intensity private int[] pfj; // J inf private String[] pfjsyn; // assignment string private int[] pfiexpa; // index of assigned exp private int pfixdeb; // window first point index private int pfixfin; // window last point index private int pfass2; // associated data file index private boolean pfready; // pred file ready private double predselx0; // starting x of pred selection area private double predselx1; // ending x of pred selection area // private double p_zx0; // starting x of zoom area private double p_zy0; // starting y of zoom area private double p_zx1; // ending x of zoom area private double p_zy1; // ending y of zoom area private double prevp_zx1; // previous p_zx1 private double prevp_zy1; // previous p_zy1 private double p_pfx0; // starting x of pred selection area private double p_pfx1; // ending x of pred selection area // private int p_paintx; // starting X of myrepaint area private int p_painty; // starting Y of myrepaint area private int p_paintwidth; // width of myrepaint area private int p_paintheight; // height of myrepaint area // private double axmi; // global frequency mini private double axma; // global frequency maxi private double axdelta; // global frequency range private double aymi; // global intensity mini private double ayma; // global intensity maxi private double aydelta; // global intensity range private double ayrev; // global intensity reverse axis private double saxmi; // global frequency mini (including shift) private double saxma; // global frequency maxi (including shift) private double bxmi; // window frequency mini private double bxma; // window frequency maxi private double bxdelta; // window frequency range private double bymi; // window intensity mini private double byma; // window intensity maxi private double bydelta; // window intensity range private double apasymi; // pred-as-stick global intensity mini private double apasyma; // pred-as-stick global intensity maxi private double apasydelta; // pred-as-stick global intensity range private double apasyrev; // pred-as-stick global intensity reverse axis private double bpasymi; // pred-as-stick window intensity mini private double bpasyma; // pred-as-stick window intensity maxi private double bpasydelta; // pred-as-stick window intensity range private double[] bcadreX = {0., 0., 0., 0.}; // window frequency private double[] bcadreY = {0., 0., 0., 0.}; // window intensity private double[] bcadrepasY = {0., 0., 0., 0.}; // pred-as-stick window intensity private boolean showbar; // allow showing vertical bar private boolean movebar; // allow moving vertical bar private double xbar; // X position of vertical bar private boolean zoomx; // X zoom has to be defined private boolean zoomy; // Y zoom has to be defined private boolean zoompasy; // pred-as-stick Y zoom has to be defined private boolean zoom; // draw zoom area private int shiftedf; // index of shifted data file private boolean shiftx; // X shift has to be defined private boolean shifty; // Y shift has to be defined private boolean shift; // X or Y shift have to be defined private boolean predsel; // pred selection running // assignment area popup menu private JPopupMenu jpmassign; private JMenuItem jmiunselexp; // unselect exp private JMenu jmunass; // unassign private JMenuItem[] jmiunass; // select one assignment to be unassigned private JMenuItem jmiunassall; // select all assignments to be unassigned private JMenu jmfaso; // set frequency mark private JMenuItem[][] jmifaso; // [assignments][frequency marks] private JMenu jmsaso; // set intensity mark private JMenuItem[][] jmisaso; // [assignments][intensity marks] private JMenu jmexasg; // set EXASG private JMenuItem[] jmiexasg; // [exp data] private JMenu jmcomm; // set comment private JMenuItem[] jmicomm; // [exp data] private JMenuItem jmiaddexasg; // add EXASG string private JMenuItem[] jmidefexasg; // [possible EXASG] // exp selection popupmenu private JPopupMenu jpmexp; private JMenuItem[] jmiexp; // selectable private int ixexp; // selected exp index private int ixdebexp; // window first point index private int ixfinexp; // window last point index // pred selection window private JFSelPred jfsp; // selected pred display private boolean lsallowed; // local simulation allowed private boolean locsim; // loc-sim shown private double[] lsx; // loc-sim spectrum X private double[] lsy; // loc-sim spectrum Y private double lsmi; // loc-sim spectrum Ymin private double lsma; // loc-sim spectrum Ymax private double lsmis; // pred associated spectrum Ymin private double lsmas; // pred associated spectrum Ymax private int[] ixpred; // selected pred index private int[] ixpreds; // selected pred status >> -1: nothing(---), 0: already in exp(ASG), 1: selected(SEL) private int nbixpred; // nb of selected xpred private int ixdebpred; // pred area first point index // private int ixfinpred; // pred area last point index private int ixpredable; // index of current selectable pred // format private String cStr; private ExpJsynPoint cejpt; // to sort private String nsavexp; // saved exp file name private PrintWriter out1; private JMenuItem jmithreshold; // prediction threshold ///////////////////////////////////////////////////////////////////// /** * Construct a new PanAff. * * @param cjobplay calling JobPlay * @param cmxnbdf max number of data files * @param ccoul colors * @param cjta to show selected exp and pred * @param cjlx to show X mouse position (data scale) * @param cjly to show Y mouse position (data scale) * @param cjlypredas to show Y mouse position (pred-as-stick scale) */ public PanAff(JobPlay cjobplay, int cmxnbdf, Color[] ccoul, JTextArea cjta, JLabel cjlx, JLabel cjly, JLabel cjlypredas) { setName("PanAff"); // for help files jobplay = cjobplay; // calling jobplay workd = System.getProperty("user.dir"); // working directory // max number of data files coul = ccoul.clone(); // color choice nbcoul = ccoul.length; // nb of colors jta = cjta; // to show selected exp and pred jlx = cjlx; // to show X mouse position (data scale) jly = cjly; // to show Y mouse position (data scale) jlypredas = cjlypredas; // to show Y mouse position (pred-as-stick scale) lnsep = System.getProperty("line.separator"); // font size int fontsz = 14; mono14 = new Font("Monospaced", Font.PLAIN, fontsz); // default font // create arrays ntfile = new String[cmxnbdf]; // data file type nbxy = new int[cmxnbdf]; // number of points in data file x = new double[cmxnbdf][]; // frequency y = new double[cmxnbdf][]; // intensity sxmi = new double[cmxnbdf]; // X min (including shift) sxma = new double[cmxnbdf]; // X max (including shift) ixdeb = new int[cmxnbdf]; // window first point index ixfin = new int[cmxnbdf]; // window last point index use_red = new boolean[cmxnbdf]; // use reduced data i_red = new int[cmxnbdf][]; // index of reduced data nbxy_red = new int[cmxnbdf]; // reduced number of points ixdeb_red = new int[cmxnbdf]; // window first reduced point index ixfin_red = new int[cmxnbdf]; // window last reduced point index show = new boolean[cmxnbdf]; // show the data file yreverse = new boolean[cmxnbdf]; // Y reverse the data file xshifted = new double[cmxnbdf]; // X shift value yshifted = new double[cmxnbdf]; // Y shift value dfready = new boolean[cmxnbdf]; // data file ready status Arrays.fill(dfready, false); // NO data file ready efass2 = -1; // exp NOT associated to a data file pfass2 = -1; // pred NOT associated to a data file nbdf = 0; // nb of data files nbpredasf = 0; // nb of pred-as-stick files efnbxy = 0; // nb of exp points ixexp = -1; // NO exp data selected efready = false; // exp file NOT ready pfnbxy = 0; // nb of pred points nbixpred = 0; // NO selected pred points pfready = false; // pred file NOT ready nbxaso = xaso.length; // nb of possible values for frequency and intensity marks // mouse survey addMouseListener(this); addMouseMotionListener(this); // printing status printing = false; // no waited action showbar = false; // no vertical bar to show movebar = false; // no move of vertical bar zoomx = false; // no x zoom area to define zoomy = false; // no y zoom area to define zoompasy = false; // no pred-as-stick y zoom area to define zoom = false; // no zoom area to draw shiftx = false; // no x shift area to define shifty = false; // no y shift area to define shift = false; // no shift area to draw predsel = false; // no running pred selection lsallowed = false; // local simulation not allowed locsim = false; // no local simulation shown // define X/Y scaling menus nbfact = fact.length; // nb of scaling factors nbscalec = 2 * nbfact; // nb of scaling choices (* and /) jmivalexpx = new JMenuItem[nbscalec]; // frequency choices jmivalexpy = new JMenuItem[nbscalec]; // intensity choices jmivalexppasy = new JMenuItem[nbscalec]; // pred-as-stick intensity choices // X rescale JMenu jmrescalex = new JMenu("Rescale X"); // Y rescale JMenu jmrescaley = new JMenu("Rescale Y"); jmrescalepasy = new JMenu("Rescale pred-as-stick Y"); for (int i = 0; i < nbfact; i++) { jmivalexpx[i] = new JMenuItem("/" + fact[nbfact - i - 1]); // divide jmivalexpx[nbscalec - i - 1] = new JMenuItem("*" + fact[nbfact - i - 1]); // multiply jmivalexpy[i] = new JMenuItem("/" + fact[nbfact - i - 1]); jmivalexpy[nbscalec - i - 1] = new JMenuItem("*" + fact[nbfact - i - 1]); jmivalexppasy[i] = new JMenuItem("/" + fact[nbfact - i - 1]); jmivalexppasy[nbscalec - i - 1] = new JMenuItem("*" + fact[nbfact - i - 1]); } // for (int i = 0; i < nbscalec; i++) { // for each choice if (i == nbfact) { jmrescalex.addSeparator(); jmrescaley.addSeparator(); jmrescalepasy.addSeparator(); } jmivalexpx[i].addActionListener(this); jmrescalex.add(jmivalexpx[i]); jmivalexpy[i].addActionListener(this); jmrescaley.add(jmivalexpy[i]); jmivalexppasy[i].addActionListener(this); jmrescalepasy.add(jmivalexppasy[i]); } // menus jpmdata = new JPopupMenu(); jmishowbar = new JMenuItem("Show/set vertical bar"); jmishowbar.addActionListener(this); jpmdata.add(jmishowbar); jmihidebar = new JMenuItem("Hide vertical bar"); jmihidebar.setEnabled(false); jmihidebar.addActionListener(this); jpmdata.add(jmihidebar); jpmdata.addSeparator(); jmizoomx = new JMenuItem("Rescale X (zoom)"); jmizoomx.addActionListener(this); jpmdata.add(jmizoomx); jpmdata.add(jmrescalex); jmirestorex = new JMenuItem("Restore X scale"); jmirestorex.addActionListener(this); jpmdata.add(jmirestorex); jpmdata.addSeparator(); jmizoomy = new JMenuItem("Rescale Y (zoom)"); jmizoomy.addActionListener(this); jpmdata.add(jmizoomy); jpmdata.add(jmrescaley); jmirestorey = new JMenuItem("Restore Y scale"); jmirestorey.addActionListener(this); jpmdata.add(jmirestorey); jpmdata.addSeparator(); jmizoompasy = new JMenuItem("Rescale pred-as-stick Y (zoom)"); jmizoompasy.setEnabled(false); jmizoompasy.addActionListener(this); jpmdata.add(jmizoompasy); jmrescalepasy.setEnabled(false); jpmdata.add(jmrescalepasy); jmirestorepasy = new JMenuItem("Restore pred-as-stick Y scale"); jmirestorepasy.setEnabled(false); jmirestorepasy.addActionListener(this); jpmdata.add(jmirestorepasy); } public static double getThreshold() { return threshold; } ///////////////////////////////////////////////////////////////////////////// private void myrepaint(int x, int y, int width, int height) { // internally called // area slightly extended (1 pixel in each direction) // in PanAff, for all x,width or y,height in pixel unit : x0 = x, x1 = x0+width-1 (ie. width = x1-x0+1) int x0 = Math.max(0, x - 1); // 1 more pixel (left side) int y0 = Math.max(0, y - 1); // 1 more pixel (upper side) int w0 = Math.min(x + width, p_wxma) - x0 + 1; // 1 more pixel (right side) int h0 = Math.min(y + height, p_wyma) - y0 + 1; // 1 more pixel (lower side) repaint(x0, y0, w0, h0); } /** * Draw this panel content. */ public void paintComponent(Graphics g) { super.paintComponent(g); // prepare the panel p_wxma = this.getWidth() - 1; // pixel window X max p_wyma = this.getHeight() - 1; // pixel window Y max g2d = (Graphics2D) g; // for extended functions Dimension ss = Toolkit.getDefaultToolkit().getScreenSize(); int p_width = (int) ss.getWidth(); // screen width int p_height = (int) ss.getHeight(); // screen height drawData(g2d, p_wxma, p_wyma, true, p_width, p_height); // draw } /** * Print this panel content. */ public int print(Graphics g, PageFormat pf, int pi) throws PrinterException { if (pi >= 1) { // number of the page to print return Printable.NO_SUCH_PAGE; // only one } printing = true; // used to disallow some button actions while printing double pfIW = pf.getImageableWidth(); double pfIH = pf.getImageableHeight(); // we have to reduce printed area to fit various OS behaviours int prp_wxma = (int) (.9 * pfIW - 1); // paper printable X max int prp_wyma = (int) (.9 * pfIH - 1); // paper printable Y max g2d = (Graphics2D) g; // for extended functions g2d.translate(pf.getImageableX() + .05 * pfIW, // translate to printable area pf.getImageableY() + .05 * pfIH); int p_width = (int) pf.getWidth(); // paper width int p_height = (int) pf.getHeight(); // paper height drawData(g2d, prp_wxma, prp_wyma, false, p_width, p_height); // draw printing = false; // NOT longer printing return Printable.PAGE_EXISTS; // page ready } ///////////////////////////////////////////////////////////////////////////// // create the page to print or draw on screen private void drawData(Graphics2D g2d, int ddp_wxma, int ddp_wyma, boolean screen, int ddp_width, int ddp_height) { // all dd_ and ddp_ variables are drawData local // usefull variables int ddp_x0; int ddp_x1; int ddp_x2; int ddp_y0; int ddp_y1; int ddp_y2; String dd_str; // paint g2d.setColor(Color.BLACK); // black // sc_ variables define scale while drawing/printing // font size % ddp_height double sc_fontsz = .015; int dd_fontsz = (int) Math.round(ddp_height * sc_fontsz); // font size Font monoFt = new Font("Monospaced", Font.PLAIN, dd_fontsz); // default font g2d.setFont(monoFt); FontMetrics fm = g2d.getFontMetrics(); // for its methods // ddp_wxma and ddp_wyma are defined before call to drawData // in paintComponent (draw) or print // gap % ddp_width double sc_gap = .0035; int ddp_gap = (int) Math.round(ddp_width * sc_gap); // gap int ddp_cxmi = 2 * ddp_gap + (int) Math.round(dd_fontsz * 2.5); // x min int ddp_cxma = ddp_wxma - ddp_cxmi; // x max int ddp_cwidth = ddp_cxma - ddp_cxmi + 1; // x range // basic y gap % ddp_height double sc_epymi = .005; int ddp_epymi = (int) Math.round(ddp_height * sc_epymi); // exp/pred area y lower limit (basic y gap) int ddp_ysaso = ddp_epymi + 3 * ddp_epymi; // y of intensity mark int ddp_yfaso = ddp_epymi + 6 * ddp_epymi; // y of frequency mark int ddp_yatmi = ddp_epymi + 7 * ddp_epymi; // exp area y lower limit int ddp_yatma = ddp_epymi + 11 * ddp_epymi; // exp area y upper limit int ddp_yprmi = ddp_epymi + 12 * ddp_epymi; // pred area y lower limit int ddp_yprma = ddp_epymi + 16 * ddp_epymi; // pred area y upper limit int ddp_epyma = ddp_epymi + 17 * ddp_epymi; // exp/pred area y upper limit int ddp_cymi = ddp_epyma + ddp_gap; // y min int ddp_cyma = ddp_wyma - ddp_cxmi; // y max int ddp_cheight = ddp_cyma - ddp_cymi + 1; // y range // oval ray % ddp_width double sc_ovray = .003; int ddp_ovray = (int) Math.round(ddp_width * sc_ovray); // oval ray int ddp_ovwidth = 2 * ddp_ovray + 1; // oval long ray int ddp_sqhw = ddp_ovray + 1; // square half side int ddp_sqwidth = 2 * ddp_sqhw + 1; // square side // half fuzzy width % ddp_width double sc_xhalf = .003; int ddp_xhalf = (int) Math.round(ddp_width * sc_xhalf); // half fuzzy width // save some for external usage (mouse location, reduced data) if (screen) { p_gap = ddp_gap; p_cxmi = ddp_cxmi; p_cxma = ddp_cxma; p_cwidth = ddp_cwidth; p_epymi = ddp_epymi; p_yatma = ddp_yatma; p_yprmi = ddp_yprmi; p_yprma = ddp_yprma; p_epyma = ddp_epyma; p_cymi = ddp_cymi; p_cyma = ddp_cyma; p_cheight = ddp_cheight; p_ovray = ddp_ovray; p_ovwidth = ddp_ovwidth; p_xhalf = ddp_xhalf; if (ddp_wxma != prevp_wxma) { // window size changed setRedSpect(); // reduce spectrum data if necessary } prevp_wxma = ddp_wxma; // keep it for next call } if (nbdf == 0) { // no data file return; } // in-clip area to (re)paint Rectangle rect = g2d.getClipBounds(); // in-clip rectangle int ddp_clipxmi = Math.max(0, rect.x - 1); // lower X pixel ??? one more needed int ddp_clipxma = rect.x + rect.width; // upper X pixel ??? one more needed double dd_clipxmi = p2X(ddp_clipxmi, ddp_cxmi, ddp_cxma); // lower frequency double dd_clipxma = p2X(ddp_clipxma, ddp_cxmi, ddp_cxma); // upper frequency int ddp_clipymi = rect.y; // lower Y pixel int ddp_clipyma = rect.y + rect.height - 1; // upper Y pixel // axis and ticks // in-clip area not taken into account // X data axis ddp_y0 = ddp_cyma + ddp_gap; ddp_y1 = ddp_y0 + ddp_gap; ddp_y2 = ddp_y1 + (int) Math.round(dd_fontsz * 1.2); // text location g2d.drawLine(ddp_cxmi, ddp_y0, ddp_cxma, ddp_y0); // draw axis for (int i = 0; i <= (int) bcadreX[2]; i++) { // for each tick mark ddp_x0 = ip_X(bcadreX[0] + i * bcadreX[1], ddp_cxmi, ddp_cxma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x0, ddp_y1); // draw tick mark } dd_str = "" + (float) bcadreX[0]; // rectangle defined by a string Rectangle2D rectS = fm.getStringBounds(dd_str, g2d); ddp_x0 = Math.max(0, (int) Math.round(ip_X(bcadreX[0], ddp_cxmi, ddp_cxma) - rectS.getWidth() / 2.)); // center value of 1st tick in limits g2d.drawString(dd_str, ddp_x0, ddp_y2); dd_str = "(" + (float) bcadreX[1] + ")"; rectS = fm.getStringBounds(dd_str, g2d); ddp_x0 = (int) Math.round(ip_X((bcadreX[0] + bcadreX[3]) / 2., ddp_cxmi, ddp_cxma) - rectS.getWidth() / 2.); // center interval value g2d.drawString(dd_str, ddp_x0, ddp_y2); dd_str = "" + (float) bcadreX[3]; rectS = fm.getStringBounds(dd_str, g2d); ddp_x0 = Math.min((int) Math.round(ip_X(bcadreX[3], ddp_cxmi, ddp_cxma) - rectS.getWidth() / 2.), (int) Math.round(ddp_wxma - rectS.getWidth())); // center value of last tick in limits g2d.drawString(dd_str, ddp_x0, ddp_y2); // Y data axis ddp_x0 = ddp_cxmi - ddp_gap; ddp_x1 = ddp_x0 - ddp_gap; g2d.drawLine(ddp_x0, ddp_cyma, ddp_x0, ddp_cymi); // draw axis for (int i = 0; i <= (int) bcadreY[2]; i++) { // for each tick mark ddp_y0 = ip_Y(bcadreY[0] + i * bcadreY[1], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y0); // draw tick } ddp_x2 = ddp_x1 - (int) Math.round(dd_fontsz * .4); // text location dd_str = "" + (float) bcadreY[0]; rectS = fm.getStringBounds(dd_str, g2d); ddp_y0 = Math.min(ddp_y1, (int) Math.round(ip_Y(bcadreY[0], ddp_cymi, ddp_cyma) + rectS.getWidth() / 2.)); // center value of 1st tick in limits drawRotString(dd_str, ddp_x2, ddp_y0); dd_str = "(" + (float) bcadreY[1] + ")"; rectS = fm.getStringBounds(dd_str, g2d); ddp_y0 = (int) Math.round(ip_Y((bcadreY[0] + bcadreY[3]) / 2., ddp_cymi, ddp_cyma) + rectS.getWidth() / 2.); // center interval value drawRotString(dd_str, ddp_x2, ddp_y0); dd_str = "" + (float) bcadreY[3]; rectS = fm.getStringBounds(dd_str, g2d); ddp_y0 = Math.max((int) Math.round(ip_Y(bcadreY[3], ddp_cymi, ddp_cyma) + rectS.getWidth() / 2.0), (int) Math.round(ddp_cymi - ddp_gap / 2.0 + rectS.getWidth())); // center value of last tick in limits drawRotString(dd_str, ddp_x2, ddp_y0); // Y pred-as-stick axis if (nbpredasf != 0) { ddp_x0 = ddp_cxma + ddp_gap; ddp_x1 = ddp_x0 + ddp_gap; g2d.drawLine(ddp_x0, ddp_cyma, ddp_x0, ddp_cymi); // draw axis for (int i = 0; i <= (int) bcadrepasY[2]; i++) { // for each tick mark ddp_y0 = ip_pasY(bcadrepasY[0] + i * bcadrepasY[1], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y0); // draw tick } ddp_x2 = ddp_x1 + (int) Math.round(dd_fontsz * 1.2); // text location dd_str = "" + (float) bcadrepasY[0]; rectS = fm.getStringBounds(dd_str, g2d); ddp_y0 = Math.min(ddp_y1, (int) Math.round(ip_pasY(bcadrepasY[0], ddp_cymi, ddp_cyma) + rectS.getWidth() / 2.)); // center value of 1st tick in limits drawRotString(dd_str, ddp_x2, ddp_y0); dd_str = "(" + (float) bcadrepasY[1] + ")"; rectS = fm.getStringBounds(dd_str, g2d); ddp_y0 = (int) Math.round(ip_pasY((bcadrepasY[0] + bcadrepasY[3]) / 2., ddp_cymi, ddp_cyma) + rectS.getWidth() / 2.); // center interval value drawRotString(dd_str, ddp_x2, ddp_y0); dd_str = "" + (float) bcadrepasY[3]; rectS = fm.getStringBounds(dd_str, g2d); ddp_y0 = Math.max((int) Math.round(ip_pasY(bcadrepasY[3], ddp_cymi, ddp_cyma) + rectS.getWidth() / 2.), (int) (ddp_cymi - ddp_gap / 2 + rectS.getWidth())); // center value of last tick in limits drawRotString(dd_str, ddp_x2, ddp_y0); } // for each data if ((ddp_clipxma >= ddp_cxmi && ddp_clipxmi <= ddp_cxma) && (ddp_clipyma >= ddp_cymi && ddp_clipymi <= ddp_cyma)) { // in in-clip area // define out-clip area g2d.setClip(ddp_cxmi, ddp_cymi, ddp_cwidth, ddp_cheight); for (int j = 0; j < nbdf; j++) { // for each data file if (dfready[j]) { if (show[j]) { // if show g2d.setColor(coul[j - nbcoul * (j / nbcoul)]); // color // spectrum switch (ntfile[j]) { case "spectrum": int ci; int cim1; int cixdeb; int cixfin; // boolean reduce; reduce = use_red[j] && screen; if (reduce) { // use reduced data cixdeb = ixdeb_red[j]; cixfin = ixfin_red[j]; } else { // use raw data (printing or no reduction) cixdeb = ixdeb[j]; cixfin = ixfin[j]; } // if (cixdeb <= cixfin) { // something to draw if (yreverse[j]) { // Y reverse for (int i = cixdeb + 1; i <= cixfin; i++) { if (reduce) { ci = i_red[j][i]; cim1 = i_red[j][i - 1]; } else { ci = i; cim1 = i - 1; } if (x[j][ci] < dd_clipxmi) { // no need to draw continue; } if (x[j][cim1] > dd_clipxma) { // end of draw break; } ddp_x0 = ip_X(x[j][cim1], ddp_cxmi, ddp_cxma); ddp_x1 = ip_X(x[j][ci], ddp_cxmi, ddp_cxma); ddp_y0 = ip_Y(ayrev - y[j][cim1], ddp_cymi, ddp_cyma); ddp_y1 = ip_Y(ayrev - y[j][ci], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } else { // no Y reverse for (int i = cixdeb + 1; i <= cixfin; i++) { if (reduce) { ci = i_red[j][i]; cim1 = i_red[j][i - 1]; } else { ci = i; cim1 = i - 1; } if (x[j][ci] < dd_clipxmi) { continue; } if (x[j][cim1] > dd_clipxma) { break; } ddp_x0 = ip_X(x[j][cim1], ddp_cxmi, ddp_cxma); ddp_x1 = ip_X(x[j][ci], ddp_cxmi, ddp_cxma); ddp_y0 = ip_Y(y[j][cim1], ddp_cymi, ddp_cyma); ddp_y1 = ip_Y(y[j][ci], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } } break; // stick case "stick": if (yreverse[j]) { // Y reverse for (int i = ixdeb[j]; i <= ixfin[j]; i++) { if (x[j][i] < dd_clipxmi) { continue; } if (x[j][i] > dd_clipxma) { break; } ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma); ddp_x1 = ddp_x0; ddp_y0 = ip_Y(ayrev - yshifted[j], ddp_cymi, ddp_cyma); ddp_y1 = ip_Y(ayrev - (y[j][i] + yshifted[j]), ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } else { // no Y reverse for (int i = ixdeb[j]; i <= ixfin[j]; i++) { if (x[j][i] < dd_clipxmi) { continue; } if (x[j][i] > dd_clipxma) { break; } ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma); ddp_x1 = ddp_x0; ddp_y0 = ip_Y(yshifted[j], ddp_cymi, ddp_cyma); ddp_y1 = ip_Y(y[j][i] + yshifted[j], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } break; // peakas case "peakas": if (yreverse[j]) { // Y reverse for (int i = ixdeb[j]; i <= ixfin[j]; i++) { if (x[j][i] < dd_clipxmi) { continue; } if (x[j][i] > dd_clipxma) { break; } ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma); ddp_x1 = ddp_x0; ddp_y0 = ip_Y(1. + yshifted[j], ddp_cymi, ddp_cyma); ddp_y1 = ip_Y(y[j][i] + yshifted[j], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } else { // no Y reverse for (int i = ixdeb[j]; i <= ixfin[j]; i++) { if (x[j][i] < dd_clipxmi) { continue; } if (x[j][i] > dd_clipxma) { break; } ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma); ddp_x1 = ddp_x0; ddp_y0 = ip_Y(+yshifted[j], ddp_cymi, ddp_cyma); ddp_y1 = ip_Y(y[j][i] + yshifted[j], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } break; // pred-as-stick default: if (yreverse[j]) { // Y reverse for (int i = ixdeb[j]; i <= ixfin[j]; i++) { if (x[j][i] < dd_clipxmi) { continue; } if (x[j][i] > dd_clipxma) { break; } ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma); ddp_x1 = ddp_x0; ddp_y0 = ip_pasY(apasyrev - yshifted[j], ddp_cymi, ddp_cyma); ddp_y1 = ip_pasY(apasyrev - (y[j][i] + yshifted[j]), ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } else { // no Y reverse for (int i = ixdeb[j]; i <= ixfin[j]; i++) { if (x[j][i] < dd_clipxmi) { continue; } if (x[j][i] > dd_clipxma) { break; } ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma); ddp_x1 = ddp_x0; ddp_y0 = ip_pasY(yshifted[j], ddp_cymi, ddp_cyma); ddp_y1 = ip_pasY(y[j][i] + yshifted[j], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } break; } } } } // local simulation if (locsim) { g2d.setColor(Color.RED); for (int i = 1; i < lsx.length; i++) { // for each point if (lsx[i] < dd_clipxmi) { // no need to draw continue; } if (lsx[i - 1] > dd_clipxma) { // end of draw break; } ddp_x0 = ip_X(lsx[i - 1], ddp_cxmi, ddp_cxma); ddp_x1 = ip_X(lsx[i], ddp_cxmi, ddp_cxma); ddp_y0 = ip_Y(lsy[i - 1], ddp_cymi, ddp_cyma); ddp_y1 = ip_Y(lsy[i], ddp_cymi, ddp_cyma); g2d.drawLine(ddp_x0, ddp_y0, ddp_x1, ddp_y1); } } } // exp/pred area if ((ddp_clipxma >= ddp_cxmi && ddp_clipxmi <= ddp_cxma)) { // define out-clip area g2d.setClip(ddp_cxmi, ddp_epymi, ddp_cxma - ddp_cxmi + 1, ddp_epyma - ddp_epymi + 1); g2d.setColor(Color.BLACK); // black int cp_clipxmi; // current in-clip X min int cp_clipxma; // current in-clip X max int ddp_ovxmi; // oval X min // exp if ((ddp_clipyma >= ddp_epymi && ddp_clipymi <= ddp_yatma)) { // in in-clip area cp_clipxmi = ddp_clipxmi; // current in-clip X min cp_clipxma = ddp_clipxma; // current in-clip X max if (efready) { // exp file loaded if (ixexp >= 0) { // exp selected double xexp = efx[ixexp]; if (xexp >= bxmi && xexp <= bxma) { // in window ddp_ovxmi = ip_X(xexp, ddp_cxmi, ddp_cxma) - ddp_ovray; if (ddp_ovxmi + ddp_ovwidth >= ddp_clipxmi && ddp_ovxmi <= ddp_clipxma) { g2d.setColor(Color.RED); g2d.fillOval(ddp_ovxmi, ddp_yatmi, ddp_ovwidth, ddp_yatma - ddp_yatmi + 1); // red oval cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi); cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi + ddp_ovwidth); } } } // associated to a data file ? if (efass2 >= 0) { // yes g2d.setColor(coul[efass2 - nbcoul * (efass2 / nbcoul)]); // color } else { // no g2d.setColor(Color.BLACK); // black } // draw exp String str; for (int i = efixdeb; i <= efixfin; i++) { ddp_x0 = ip_X(efx[i], ddp_cxmi, ddp_cxma); if (ddp_x0 < cp_clipxmi) { continue; } if (ddp_x0 > cp_clipxma) { break; } // global frequency mark if (!effasog[i].equals(" ")) { str = String.valueOf(effasog[i]); rectS = fm.getStringBounds(str, g2d); g2d.drawString(str, (int) Math.round(ddp_x0 - rectS.getWidth() / 2.), ddp_yfaso); } // global intensity mark if (!efsasog[i].equals(" ")) { str = String.valueOf(efsasog[i]); rectS = fm.getStringBounds(str, g2d); g2d.drawString(str, (int) Math.round(ddp_x0 - rectS.getWidth() / 2.), ddp_ysaso); } // size if ((i / 5) * 5 == i) { // big ddp_y2 = ddp_yatmi; } else { // plain ddp_y2 = (ddp_yatmi + ddp_yatma) / 2; } g2d.drawLine(ddp_x0, ddp_y2, ddp_x0, ddp_yatma); } } } // pred selection area if ((ddp_clipyma >= ddp_yprmi && ddp_clipymi <= ddp_yprma)) { // in in-clip area cp_clipxmi = ddp_clipxmi; // current in-clip X min cp_clipxma = ddp_clipxma; // current in-clip X max if (ip_X(predselx0, ddp_cxmi, ddp_cxma) != ip_X(predselx1, ddp_cxmi, ddp_cxma)) { // null zone ignored if (predsel) { // pred file loaded // pred selection area if (dd_clipxma >= predselx0 && dd_clipxmi <= predselx1) { g2d.setColor(Color.LIGHT_GRAY); g2d.fillRect(ip_X(Math.max(predselx0, dd_clipxmi), ddp_cxmi, ddp_cxma), ddp_yprmi, ip_X(Math.min(predselx1, dd_clipxma), ddp_cxmi, ddp_cxma) - ip_X(Math.max(predselx0, dd_clipxmi), ddp_cxmi, ddp_cxma) + 1, ddp_yprma - ddp_yprmi + 1); // pred selection area } if (nbixpred > 0) { // pred selected g2d.setColor(Color.GREEN); for (int i = 0; i < nbixpred; i++) { ddp_x0 = ip_X(pfx[ixpred[i]], ddp_cxmi, ddp_cxma); if (ddp_x0 < cp_clipxmi - ddp_ovray) { continue; } if (ddp_x0 > cp_clipxma + ddp_ovray) { break; } // in window if (ixpreds[i] == 1) { // selected ddp_ovxmi = ddp_x0 - ddp_ovray; if (ddp_ovxmi + ddp_ovwidth >= ddp_clipxmi && ddp_ovxmi <= ddp_clipxma) { g2d.fillOval(ddp_ovxmi, ddp_yprmi, ddp_ovwidth, ddp_yprma - ddp_yprmi + 1); // green oval cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi); cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi + ddp_ovwidth); } } } if (ixpredable != -1) { g2d.setColor(Color.YELLOW); ddp_x0 = ip_X(pfx[ixpredable], ddp_cxmi, ddp_cxma); if (ddp_x0 >= cp_clipxmi - ddp_sqhw && ddp_x0 <= cp_clipxma + ddp_sqhw) { ddp_ovxmi = ddp_x0 - ddp_sqhw; g2d.fillRect(ddp_ovxmi, (ddp_yprmi + ddp_yprma) / 2 - ddp_sqhw, ddp_sqwidth, ddp_sqwidth); // yellow square cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi); cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi + ddp_sqwidth); } } } } } /* * pred file ready */ if (pfready) { // show assigned pred g2d.setColor(Color.ORANGE); ddp_y2 = (ddp_yprmi + ddp_yprma) / 2 - ddp_ovray; for (int i = pfixdeb; i <= pfixfin; i++) { ddp_x0 = ip_X(pfx[i], ddp_cxmi, ddp_cxma); if (ddp_x0 < cp_clipxmi - ddp_ovray) { continue; } if (ddp_x0 > cp_clipxma + ddp_ovray) { break; } if (pfiexpa[i] >= 0) { ddp_ovxmi = ddp_x0 - ddp_ovray; g2d.fillOval(ddp_ovxmi, ddp_y2, ddp_ovwidth, ddp_ovwidth); // orange disc cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi); cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi + ddp_ovwidth); } } } /* exp selected * and * exp and pred file ready */ if (ixexp >= 0 && efready && pfready) { // show exp assigned pred g2d.setColor(Color.RED); ddp_y2 = (ddp_yprmi + ddp_yprma) / 2 - ddp_ovray; for (int k = 0; k < efejsynpt[ixexp].size(); k++) { // for each assignment of selected exp cejpt = efejsynpt[ixexp].get(k); // get its ExpJsynPoint int ipreda = cejpt.getIpred(); // get pointed pred index if (ipreda >= 0) { // if defined double xpred = pfx[ipreda]; // its frequency if (xpred >= bxmi && xpred <= bxma) { // if in the window ddp_x0 = ip_X(xpred, ddp_cxmi, ddp_cxma); if (ddp_x0 >= cp_clipxmi - ddp_ovray && ddp_x0 <= cp_clipxma + ddp_ovray) { ddp_ovxmi = ddp_x0 - ddp_ovray; g2d.fillOval(ddp_ovxmi, ddp_y2, ddp_ovwidth, ddp_ovwidth); // red disc cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi); cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi + ddp_ovwidth); } } } } } // pred if (pfready) { // pred file ready // associated to a data file ? if (pfass2 >= 0) { // yes g2d.setColor(coul[pfass2 - nbcoul * (pfass2 / nbcoul)]); // color } else { // no g2d.setColor(Color.BLACK); // black } // draw pred ddp_y2 = (ddp_yprmi + ddp_yprma) / 2; for (int i = pfixdeb; i <= pfixfin; i++) { ddp_x0 = ip_X(pfx[i], ddp_cxmi, ddp_cxma); if (ddp_x0 < cp_clipxmi) { continue; } if (ddp_x0 > cp_clipxma) { break; } if (pfy[i] < getThreshold()) continue; // size if ((pfj[i] / 2) * 2 == pfj[i]) { // down ddp_y0 = ddp_y2; ddp_y1 = ddp_yprma; } else { // up ddp_y0 = ddp_yprmi; ddp_y1 = ddp_y2; } g2d.drawLine(ddp_x0, ddp_y0, ddp_x0, ddp_y1); } } } } // draw area frame g2d.setClip(null); g2d.setColor(Color.BLACK); // black g2d.drawRect(ddp_cxmi, ddp_epymi, ddp_cxma - ddp_cxmi, ddp_epyma - ddp_epymi); // vertical bar if (showbar) { // show it // in-clip area not taken into account int ddp_xbar = ip_X(xbar, ddp_cxmi, ddp_cxma); // pixel X position if (ddp_xbar >= ddp_cxmi && ddp_xbar <= ddp_cxma) { g2d.setColor(Color.RED); // color g2d.drawLine(ddp_xbar, ddp_epymi + ddp_gap / 2, ddp_xbar, ddp_cyma + ddp_gap / 2); // bar } } // zoom or shift to draw if (zoom || shift) { // in-clip area not taken into account g2d.setColor(Color.BLACK); // black if (zoomx || shiftx) { // X zoom/shift g2d.drawRect((int) Math.min(p_zx0, p_zx1), ddp_epymi + ddp_gap / 2, (int) Math.abs(p_zx1 - p_zx0) + 1, ddp_cyma - ddp_epymi); // zoom area } if (zoomy || zoompasy || shifty) { // Y zoom/pred-as-stick Y zoom/shift g2d.drawRect(ddp_cxmi - ddp_gap / 2, (int) Math.min(p_zy0, p_zy1), ddp_cwidth + ddp_gap, (int) Math.abs(p_zy1 - p_zy0)); // zoom area } } } // convert X from double to pixel private int ip_X(double x, int cup_cxmi, int cup_cxma) { double cup_cwidth = cup_cxma - cup_cxmi; return (int) Math.round(cup_cxmi + cup_cwidth * ((x - bxmi) / bxdelta)); } // convert X from pixel to double private double p2X(int p_x, int cup_cxmi, int cup_cxma) { double cup_cwidth = cup_cxma - cup_cxmi; return bxmi + bxdelta * (p_x - cup_cxmi) / cup_cwidth; } // convert Y from double to pixel private int ip_Y(double y, int cup_cymi, int cup_cyma) { double cup_cheight = cup_cyma - cup_cymi; return (int) Math.round(cup_cyma - cup_cheight * ((y - bymi) / bydelta)); } // convert Y from pixel to double private double p2Y(int p_y, int cup_cymi, int cup_cyma) { double cup_cheight = cup_cyma - cup_cymi; return bymi + bydelta * (cup_cyma - p_y) / cup_cheight; } // convert pasY from double to pixel private int ip_pasY(double y, int cup_cymi, int cup_cyma) { double cup_cheight = cup_cyma - cup_cymi; return (int) Math.round(cup_cyma - cup_cheight * ((y - bpasymi) / bpasydelta)); } // convert pasY from pixel to double private double p2pasY(int p_y, int cup_cymi, int cup_cyma) { double cup_cheight = cup_cyma - cup_cymi; return bpasymi + bpasydelta * (cup_cyma - p_y) / cup_cheight; } // write a string along Y axis private void drawRotString(String s, int x, int y) { AffineTransform at = g2d.getTransform(); g2d.rotate(-Math.PI / 2., x, y); g2d.drawString(s, x, y); //g2d.rotate( Math.PI/2., x, y); g2d.setTransform(at); } /** * Printing status. */ public boolean getPrinting() { return printing; } ///////////////////////////////////////////////////////////////////////////// // mouse events management /** * Mouse button pressed. *
IN EXPERIMENT/PREDICTION AREA : *
left button : experiment/prediction selection *
right button : assignment popup menu *
*
IN DATA DRAWING AREA : *
left button : vertical bar capture OR zoom/shift starting point *
right button : data popup menu */ public void mousePressed(MouseEvent mevt) { if (nbdf == 0) { // no data file return; } double p_x0 = mevt.getX(); // store X double p_y0 = mevt.getY(); // store Y // zoom starting point limited to frame p_zx0 = p_x0; p_zy0 = p_y0; if (p_x0 < p_cxmi - p_gap / 2.0) { p_zx0 = p_cxmi - p_gap / 2.0; } else if (p_x0 > p_cxma + p_gap / 2.0) { p_zx0 = p_cxma + p_gap / 2.0; } if (p_y0 < p_cymi - p_gap / 2.0) { p_zy0 = p_cymi - p_gap / 2.0; } else if (p_y0 > p_cyma + p_gap / 2.0) { p_zy0 = p_cyma + p_gap / 2.0; } // will be used to define clip area to redraw prevp_zx1 = p_zx0; prevp_zy1 = p_zy0; // if (zoomx || zoomy || zoompasy || shiftx || shifty) { // zoom or shift if (!SwingUtilities.isLeftMouseButton(mevt)) { // not left button return; } // left button if (zoomx || zoomy || zoompasy) { zoom = true; } else if (shiftx || shifty) { shift = true; } } else { // neither zoom nor shift if (SwingUtilities.isRightMouseButton(mevt)) { // right button // stop pred selection if any if (predsel) { endSelPred(); } if (p_y0 > p_epyma) { // data popup jpmdata.show(mevt.getComponent(), (int) p_zx0, (int) p_zy0); } else { // assignment popup if (setJpmAssign()) { jpmassign.show(mevt.getComponent(), (int) p_zx0, (int) p_zy0); } else { JOptionPane.showMessageDialog(null, "This button is active in this area only if" + lnsep + "a peak/experiment file is loaded"); } } } else if (SwingUtilities.isLeftMouseButton(mevt)) { // left button if (p_x0 > p_cxmi && p_x0 < p_cxma) { // vertical bar if (showbar && Math.abs(p_x0 - ip_X(xbar, p_cxmi, p_cxma)) < p_xhalf && p_y0 >= p_cymi && p_y0 <= p_cyma) { // vertical bar selected movebar = true; } else { // stop pred selection if any if (predsel) { endSelPred(); } if (efready && // exp file ready p_y0 > p_epymi && p_y0 < p_yatma) { // mouse in exp area // assignment selection predsel = false; selExp(p_zx0); } else if (pfready && // pred file ready p_y0 > p_yprmi && p_y0 < p_epyma) { // mouse in pred area // pred selection starting p_pfx0 = p_x0; // pred selection area starting point (pixel) p_pfx1 = p_pfx0; // pred selection area ending point (pixel) predselx0 = p2X((int) p_pfx0, p_cxmi, p_cxma); // pred selection area starting point (double) predselx1 = predselx0; // pred selection area ending point (double) predsel = true; // pred selection running } } } } } } /** * Mouse moved with button pressed. *
(zoom/shift area definition) */ public void mouseDragged(MouseEvent mevt) { if (nbdf == 0) { // no data file return; } double p_x1 = mevt.getX(); // store X double p_y1 = mevt.getY(); // store Y p_xmouse = (int) p_x1; // store X p_ymouse = (int) p_y1; // store Y showXY(); // show mouse position if (movebar || zoomx || zoomy || zoompasy || shiftx || shifty) { if (!SwingUtilities.isLeftMouseButton(mevt)) { // not left button return; } p_zx1 = p_x1; // store X (current end of zoom area) p_zy1 = p_y1; // store Y (current end of zoom area) // limited to the frame if (p_x1 < p_cxmi - p_gap / 2.0) { p_zx1 = p_cxmi - p_gap / 2.0; } else if (p_x1 > p_cxma + p_gap / 2.0) { p_zx1 = p_cxma + p_gap / 2.0; } if (p_y1 < p_cymi - p_gap / 2.0) { p_zy1 = p_cymi - p_gap / 2.0; } else if (p_y1 > p_cyma + p_gap / 2.0) { p_zy1 = p_cyma + p_gap / 2.0; } // define clip area to redraw if (movebar || zoomx || shiftx) { p_paintx = (int) Math.min(prevp_zx1, p_zx1); p_painty = p_epymi + p_gap / 2; p_paintwidth = (int) (Math.abs(p_zx1 - prevp_zx1) + 1); p_paintheight = p_cyma - p_epymi + 1; // vertical bar if (movebar) { xbar = p2X((int) p_zx1, p_cxmi, p_cxma); // store its position xbar = Math.max(xbar, bxmi); xbar = Math.min(xbar, bxma); p_paintx = p_paintx - p_xhalf; // due to vertical bar X selection area p_paintwidth = p_paintwidth + 2 * p_xhalf; } } else { // zoomy/zoompasy/shifty p_paintx = p_cxmi - p_gap / 2; p_painty = (int) Math.min(prevp_zy1, p_zy1); p_paintwidth = p_cwidth + p_gap; p_paintheight = (int) Math.abs(p_zy1 - prevp_zy1) + 1; } myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited along X direction prevp_zx1 = p_zx1; prevp_zy1 = p_zy1; } else if (predsel) { // predselection running p_pfx1 = p_x1; // pred selection area ending point // limited to the frame if (p_x1 < p_cxmi) { p_pfx1 = p_cxmi; } else if (p_x1 > p_cxma) { p_pfx1 = p_cxma; } selPred(Math.min(p_pfx0, p_pfx1), Math.max(p_pfx0, p_pfx1)); // show pred area and selection window jfsp.setContent(nbixpred, ixdebpred, ixpreds, ixexp); // set selection window content } } /** * Mouse button released. *
(zoom/shift ending point) */ public void mouseReleased(MouseEvent mevt) { if (nbdf == 0) { // no data file return; } double p_x1 = mevt.getX(); // store X double p_y1 = mevt.getY(); // store Y if (movebar || zoomx || zoomy || zoompasy || shiftx || shifty) { // movebar, zoom or shift if (!SwingUtilities.isLeftMouseButton(mevt)) { // not left button return; } // left button p_zx1 = p_x1; // store X (end of zoom area) p_zy1 = p_y1; // store Y (end of zoom area) // limited to the frame if (p_x1 < p_cxmi - p_gap / 2.0) { p_zx1 = p_cxmi - p_gap / 2.0; } else if (p_x1 > p_cxma + p_gap / 2.0) { p_zx1 = p_cxma + p_gap / 2.0; } if (p_y1 < p_cymi - p_gap / 2.0) { p_zy1 = p_cymi - p_gap / 2.0; } else if (p_y1 > p_cyma + p_gap / 2.0) { p_zy1 = p_cyma + p_gap / 2.0; } // if (movebar || zoomx || shiftx) { p_painty = p_epymi + p_gap / 2; p_paintheight = p_cyma - p_epymi + 1; // zoom/shift area has to be not too small if (Math.abs(p_zx1 - p_zx0) < 5) { if (zoomx || shiftx) { p_paintx = (int) Math.min(p_zx0, p_zx1); p_paintwidth = (int) Math.abs(p_zx1 - p_zx0) + 1; JOptionPane.showMessageDialog(null, "zoom/shift area to small"); if (zoomx) { zoomx = false; zoom = false; } else if (shiftx) { shiftx = false; shift = false; } } myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited along X direction return; } if (movebar) { p_paintx = (int) Math.min(prevp_zx1, p_zx1); p_paintwidth = (int) Math.abs(p_zx1 - prevp_zx1) + 1; xbar = p2X((int) p_zx1, p_cxmi, p_cxma); xbar = Math.max(xbar, bxmi); xbar = Math.min(xbar, bxma); movebar = false; myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited along X direction return; } } else { // zoomy/zoompasy/shifty p_paintx = p_cxmi - p_gap / 2; p_paintwidth = p_cwidth + p_gap; // zoom/shift area has to be not too small if (Math.abs(p_zy1 - p_zy0) < 5) { p_painty = (int) Math.min(p_zy0, p_zy1); p_paintheight = (int) Math.abs(p_zy1 - p_zy0) + 1; JOptionPane.showMessageDialog(null, "zoom/shift area to small"); if (zoomy) { zoomy = false; zoom = false; } else if (zoompasy) { zoompasy = false; zoom = false; } else if (shifty) { shifty = false; shift = false; } myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited along Y direction return; } } // if (zoomx) { // X zoom bxma = bxmi + bxdelta * (Math.max(p_zx0, p_zx1) - p_cxmi) / p_cwidth; bxmi = bxmi + bxdelta * (Math.min(p_zx0, p_zx1) - p_cxmi) / p_cwidth; bxdelta = bxma - bxmi; // window frequency range setXax(); // set X axis characteristics zoomx = false; // no x zoom area to define zoom = false; // no zoom area to draw prevp_wxma = -1; // call to setRedSpect at next repaint repaint(); // all setUnsave(); return; } else if (shiftx) { // X shift double cshift = bxdelta * (p_zx1 - p_zx0) / p_cwidth; for (int i = 0; i < nbxy[shiftedf]; i++) { // update shifted data file x[shiftedf][i] += cshift; } defxlim(shiftedf); // define first and last data points in window defxlim_red(shiftedf); // define first and last reduced data points in window // exp associated to this shifted data file ? if (efass2 == shiftedf) { // yes, update it for (int i = 0; i < efnbxy; i++) { efx[i] += cshift; } efdefxlim(); // define first and last exp points in window } // pred associated to this shifted data file ? if (pfass2 == shiftedf) { // yes, update it for (int i = 0; i < pfnbxy; i++) { pfx[i] += cshift; } pfdefxlim(); // define first and last pred points in window } xshifted[shiftedf] += cshift; // store actual shift value // set global X limits (including shift) sxmi[shiftedf] += cshift; sxma[shiftedf] += cshift; saxmi = sxmi[0]; saxma = sxma[0]; for (int i = 0; i < nbdf; i++) { saxmi = Math.min(sxmi[i], saxmi); saxma = Math.max(sxma[i], saxma); } // shiftx = false; // no x shift area to define shift = false; // no shift area to draw repaint(); // all setUnsave(); return; } // Y axis if (zoomy) { // Y zoom byma = bymi + bydelta * (p_cyma - Math.min(p_zy0, p_zy1)) / p_cheight; bymi = bymi + bydelta * (p_cyma - Math.max(p_zy0, p_zy1)) / p_cheight; bydelta = byma - bymi; cadreint(bymi, bydelta, bcadreY); // resize the Y axis zoomy = false; // no y zoom area to define zoom = false; // no zoom area to draw } else if (zoompasy) { // pred-as-stick Y zoom bpasyma = bpasymi + bpasydelta * (p_cyma - Math.min(p_zy0, p_zy1)) / p_cheight; bpasymi = bpasymi + bpasydelta * (p_cyma - Math.max(p_zy0, p_zy1)) / p_cheight; bpasydelta = bpasyma - bpasymi; cadreint(bpasymi, bpasydelta, bcadrepasY); // resize the pred-as-stick Y axis zoompasy = false; // no pred-as-stick y zoom area to define zoom = false; // no zoom area to draw } else if (shifty) { // Y shift double cshift; if (ntfile[shiftedf].equals("predas")) { cshift = bpasydelta * (p_zy0 - p_zy1) / p_cheight; // p2pasY((int) p_zy1)-p2pasY((int) p_zy0) } else { cshift = bydelta * (p_zy0 - p_zy1) / p_cheight; // p2Y((int) p_zy1)-p2Y((int) p_zy0) } if (!ntfile[shiftedf].equals("peakas")) { if (yreverse[shiftedf]) { // Y reverse -> opposit sign cshift = -cshift; } } if (ntfile[shiftedf].equals("spectrum")) { // shift taken in account in y for (int i = 0; i < nbxy[shiftedf]; i++) { y[shiftedf][i] += cshift; } } yshifted[shiftedf] += cshift; // store actual shift value shifty = false; // no y shift area to define shift = false; // no shift area to draw } // redraw data area p_paintx = 0; p_painty = p_cymi - p_gap / 2; p_paintwidth = p_wxma + 1; p_paintheight = p_cheight + p_gap; myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); setUnsave(); } else if (predsel) { // prediction selection running p_pfx1 = p_x1; // pred selection area ending point // limited to the frame if (p_x1 < p_cxmi) { p_pfx1 = p_cxmi; } else if (p_x1 > p_cxma) { p_pfx1 = p_cxma; } if (p_pfx0 == p_pfx1) { // null selection area predsel = false; return; } selPred(Math.min(p_pfx0, p_pfx1), Math.max(p_pfx0, p_pfx1)); // show pred area and selection window if (nbixpred == 0) { // no pred point in selection area predsel = false; } else { jfsp.setContent(nbixpred, ixdebpred, ixpreds, ixexp); // set selection window content // loc-sim if (lsallowed) { // test if there is enough pred associated spectrum data points in area int i; int i0; int i1; int j = pfass2; for (i = ixdeb[j]; i < ixfin[j]; i++) { if (x[j][i] >= predselx0) { // first point found break; } } i0 = Math.min(i, nbxy[j] - 1); for (i = i0; i < nbxy[j]; i++) { if (x[j][i] > predselx1) { // last point exceeded break; } } i1 = Math.max(0, i - 1); if (i1 - i0 + 1 < 3) { // at least 3 points JOptionPane.showMessageDialog(null, "Not enough pred associated spectrum data points in selection area"); endSelPred(); } else { // set pred associated spectrum Ymin and Ymax in area // will be used to scale loc-sim to spectrum lsmis = y[j][i0]; lsmas = y[j][i0]; for (i = i0; i <= i1; i++) { double cy = y[j][i]; if (cy < lsmis) { lsmis = cy; } if (cy > lsmas) { lsmas = cy; } } jfsp.setLsBasic(predselx0, predselx1, true); // set loc-sim basics in JFSelPred } } } } } /** * Mouse moved without button pressed. *
Show its position. */ public void mouseMoved(MouseEvent mevt) { p_xmouse = mevt.getX(); // store X p_ymouse = mevt.getY(); // store Y showXY(); // show mouse position } /** * Not implemented. */ public void mouseClicked(MouseEvent mevt) { } /** * Not implemented. */ public void mouseEntered(MouseEvent mevt) { } /** * Not implemented. */ public void mouseExited(MouseEvent mevt) { } ///////////////////////////////////////////////////////////////////// // show mouse position private void showXY() { if (p_xmouse > p_cxmi && // inside frame p_xmouse < p_cxma && p_ymouse > p_epymi && p_ymouse < p_cyma) { // X jlx.setText(FortranFormat.formFreq(p2X(p_xmouse, p_cxmi, p_cxma))); // Y if (p_ymouse > p_cymi) { // inside the data area jly.setText(FortranFormat.formInt(p2Y(p_ymouse, p_cymi, p_cyma))); // intensity in data scale if (nbpredasf != 0) { jlypredas.setText(FortranFormat.formInt(p2pasY(p_ymouse, p_cymi, p_cyma))); // intensity in // pred-as-stick scale } } else { // inside the exp/pred area jly.setText(""); jlypredas.setText(""); } } else { // ouside frame jlx.setText(""); jly.setText(""); jlypredas.setText(""); } } // select an exp private void selExp(double cp_x0) { double bxdeb; double bxfin; bxdeb = p2X((int) cp_x0 - p_xhalf, p_cxmi, p_cxma); // begin choice area bxfin = p2X((int) cp_x0 + p_xhalf, p_cxmi, p_cxma); // end choice area int nbep = 0; // nb of selected points int nbjmi = 0; // nb of associated jmi // select points for (int i = efixdeb; i <= efixfin; i++) { if (efx[i] < bxdeb) { // before begin, next continue; } else if (efx[i] > bxfin) { // after end, stop break; } nbep++; // nb of selected points nbjmi += efejsynpt[i].size(); // nb of associated jmi if (nbep == 1) { // 1st point, set begin index ixdebexp = i; } // always set end index ixfinexp = i; } // clean the old popup menu if any if (jpmexp != null) { jpmexp.setVisible(false); jpmexp = null; } // max nb of ticks in an exp selection popup menu int mxticks = 30; if (nbep > mxticks) { JOptionPane.showMessageDialog(null, "More then " + mxticks + " selected ticks"); return; } // if points are selected if (nbep >= 1) { // create popup menu for final choice jpmexp = new JPopupMenu(); jmiexp = new JMenuItem[nbjmi]; // for each point and each of its assignments int i; nbjmi = 0; for (int j = 0; j < nbep; j++) { i = ixdebexp + j; cejpt = efejsynpt[i].get(0); cStr = FortranFormat.formFreq(efx[i]) + " | " + FortranFormat.formInt(efy[i]) + " | " + cejpt.getJsyn(); jmiexp[nbjmi] = new JMenuItem(cStr); jmiexp[nbjmi].setFont(mono14); jmiexp[nbjmi].addActionListener(this); jpmexp.add(jmiexp[nbjmi]); nbjmi++; int k = efejsynpt[i].size(); if (k > 1) { // more than 1 assignment for (int l = 1; l < k; l++) { cejpt = efejsynpt[i].get(l); jmiexp[nbjmi] = new JMenuItem(" | " + cejpt.getJsyn()); jmiexp[nbjmi].setFont(mono14); jmiexp[nbjmi].addActionListener(this); jpmexp.add(jmiexp[nbjmi]); nbjmi++; } } } jpmexp.show(this, (int) p_zx0, (int) p_zy0); // show popup menu } } // select a pred in the selection area private void selPred(double cp_x0, double cp_x1) { if (cp_x0 == cp_x1) { // null zone ignored return; } int p_paintx0 = ip_X(predselx0, p_cxmi, p_cxma); // previous pred selection area bounds int p_paintx1 = ip_X(predselx1, p_cxmi, p_cxma); predselx0 = p2X((int) cp_x0, p_cxmi, p_cxma); // present pred selection area bounds predselx1 = p2X((int) cp_x1, p_cxmi, p_cxma); // repaint area p_paintx = Math.min(p_paintx0, ip_X(predselx0, p_cxmi, p_cxma)); p_paintwidth = Math.max(p_paintx1, ip_X(predselx1, p_cxmi, p_cxma)) - p_paintx + 1; p_painty = p_yprmi; p_paintheight = p_yprma - p_yprmi + 1; nbixpred = 0; // nb of selected points // select points for (int i = pfixdeb; i <= pfixfin; i++) { if (pfy[i] < getThreshold()) { continue; } if (pfx[i] < predselx0) { // before begin, next continue; } else if (pfx[i] > predselx1) { // after end, stop break; } nbixpred++; // nb of selected points if (nbixpred == 1) { // 1st point, set begin index ixdebpred = i; } } // if points are selected if (nbixpred > 0) { // create arrays ixpred = new int[nbixpred]; // selected pred index ixpreds = new int[nbixpred]; // selected pred status >> -1: nothing(---), 0: already in exp(ASG), 1: selected(SEL) int offset = 0; for (int j = 0; j < nbixpred; j++) { // for each selected pred while (pfy[ixdebpred + offset + j] < getThreshold()) offset++; int ci = ixdebpred + offset + j; // full index ixpred[j] = ci; // set index if (pfiexpa[ci] >= 0) { ixpreds[j] = 0; // status assigned } else { ixpreds[j] = -1; // status unselected } } } else { // no selected point setJta(); } myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited to pred selection area } ///////////////////////////////////////////////////////////////////// // define frequency and intensity global marks // ? if some mark differs // the one mark if all marks are same private void setAsog(int ci) { String gxaso; boolean plus; boolean minus; if (efnbxy != 0) { // if exp file ready int nbass = efejsynpt[ci].size(); // nb of assignments plus = false; minus = false; for (int i = 0; i < nbass; i++) { // test all cejpt = efejsynpt[ci].get(i); gxaso = cejpt.getFaso(); if (gxaso.equals("-")) { minus = true; } else if (gxaso.equals("+")) { plus = true; } } if (plus && minus) { effasog[ci] = "?"; } else { if (plus) { effasog[ci] = "+"; } else if (minus) { effasog[ci] = "-"; } else { effasog[ci] = " "; } } plus = false; minus = false; for (int i = 0; i < nbass; i++) { // test all others cejpt = efejsynpt[ci].get(i); gxaso = cejpt.getSaso(); switch (gxaso) { case " ": // do nothing break; case "+": plus = true; break; default: minus = true; break; } } if (plus && minus) { efsasog[ci] = "?"; } else { if (plus) { efsasog[ci] = "+"; } else if (minus) { efsasog[ci] = "-"; } else { efsasog[ci] = " "; } } } } /** * Update TextArea. */ public void setJta() { jta.setText(""); // empty TextArea // exp selected if (ixexp >= 0) { // exp String cStrEXASG = efexasg[ixexp]; if (cStrEXASG.length() == 0) { cStrEXASG = FortranFormat.bStr(30); // EXASG = 30 char } int k = efejsynpt[ixexp].size(); for (int l = 0; l < k; l++) { // for each assignment // type if (l == 0) { // first one jta.append("Exp : "); } else { jta.append(" "); } cejpt = efejsynpt[ixexp].get(l); // frequency, frequency mark, intensity, intensity mark jta.append(FortranFormat.formFreq(efx[ixexp]) + " " + cejpt.getFaso() + " " + FortranFormat.formInt(efy[ixexp]) + " " + cejpt.getSaso() + " | "); // assignment if (cejpt.getJsyn().trim().length() != 0) { jta.append(cejpt.getJsyn()); } else { jta.append(FortranFormat.bStr(21)); // jsyn = 21 char } boolean found = false; if (pfready) { // pred file ready int ipreda = cejpt.getIpred(); if (ipreda >= 0) { // pred frequency, pred intensity, EXASG, comment jta.append(" (" + FortranFormat.formFreq(pfx[ipreda]) + " " + FortranFormat.formInt(pfy[ipreda]) + ") | " + cStrEXASG + " " + cejpt.getComm() + lnsep); found = true; } } if (!found) { // EXASG, comment jta.append(" | " + cStrEXASG + " " + cejpt.getComm() + lnsep); } } } // pred selected if (nbixpred > 0) { // type, frequency, intensity, assignment int nbs = 0; for (int l = 0; l < nbixpred; l++) { if (ixpreds[l] == 1) { // only selected ones if (nbs == 0) { jta.append("Pred : "); nbs++; } else { jta.append(" "); } jta.append(FortranFormat.formFreq(pfx[ixpred[l]]) + " " + FortranFormat.formInt(pfy[ixpred[l]]) + " | " + pfjsyn[ixpred[l]] + lnsep); } } } } ///////////////////////////////////////////////////////////////////// /** * Process popup menu events. */ public void actionPerformed(ActionEvent evt) { // NO action while printing if (printing) { JOptionPane.showMessageDialog(null, "No action while printing"); } // -> show vertical bar if (evt.getSource() == jmishowbar) { if (showbar) { // hide current bar myrepaint(ip_X(xbar, p_cxmi, p_cxma), p_epymi + p_gap / 2, 1, p_cyma - p_epymi + 1); // limited along X direction } else { showbar = true; jmihidebar.setEnabled(true); } xbar = p2X(p_xmouse, p_cxmi, p_cxma); xbar = Math.max(xbar, bxmi); xbar = Math.min(xbar, bxma); myrepaint(ip_X(xbar, p_cxmi, p_cxma), p_epymi + p_gap / 2, 1, p_cyma - p_epymi + 1); // limited along X direction return; } // -> hide vertical bar if (evt.getSource() == jmihidebar) { showbar = false; jmihidebar.setEnabled(false); myrepaint(ip_X(xbar, p_cxmi, p_cxma), p_epymi + p_gap / 2, 1, p_cyma - p_epymi + 1); // limited along X direction return; } // -> set prediction threshold if (evt.getSource() == jmithreshold) { ThresholdDialog dialog = new ThresholdDialog(); double th = dialog.getThreshold(); threshold = th != -Double.MAX_VALUE ? th : threshold; jobplay.reloadPredFile(); return; } // -> X zoom if (evt.getSource() == jmizoomx) { zoomx = true; zoom = false; return; } // -> Y zoom if (evt.getSource() == jmizoomy) { zoomy = true; zoom = false; return; } // -> pred-as-stick Y zoom if (evt.getSource() == jmizoompasy) { zoompasy = true; zoom = false; return; } // -> X rescale for (int i = 0; i < nbscalec; i++) { if (evt.getSource() == jmivalexpx[i]) { repropx(i); // apply scaling choice repaint(); // all return; } } // -> Y rescale for (int i = 0; i < nbscalec; i++) { if (evt.getSource() == jmivalexpy[i]) { repropy(i); // apply scaling choice myrepaint(0, p_cymi - p_gap, p_wxma + 1, p_cheight + 3 * p_gap); // Y axis and all data area return; } } // -> pred-as-stick Y rescale for (int i = 0; i < nbscalec; i++) { if (evt.getSource() == jmivalexppasy[i]) { reproppasy(i); // apply scaling choice myrepaint(0, p_cymi - p_gap, p_wxma + 1, p_cheight + 3 * p_gap); // Y axis and all data area return; } } // -> restore X scale if (evt.getSource() == jmirestorex) { bxmi = (bxmi + bxma - axdelta) / 2.; // let the window be X centered bxma = bxmi + axdelta; // full original width bxdelta = bxma - bxmi; // window frequency range setXax(); // set X axis characteristics prevp_wxma = -1; // call to setRedSpect at next repaint repaint(); // all setUnsave(); return; } // -> restore Y scale if (evt.getSource() == jmirestorey) { bymi = (bymi + byma - aydelta) / 2.; // let the window be Y centered byma = bymi + aydelta; // full original height bydelta = aydelta; cadreint(bymi, bydelta, bcadreY); // resize the Y axis myrepaint(0, p_cymi - p_gap, p_wxma + 1, p_cheight + 3 * p_gap); // Y axis and all data area setUnsave(); return; } // -> restore pred-as-stick Y scale if (evt.getSource() == jmirestorepasy) { // full original window bpasymi = apasymi; bpasyma = apasyma; bpasydelta = apasydelta; bcadrepasY = acadrepasY.clone(); // set Y axis ticks myrepaint(0, p_cymi - p_gap, p_wxma + 1, p_cheight + 3 * p_gap); // Y axis and all data area return; } // exp selection if (jpmexp != null) { int nbjmi = 0; // number of jmi boolean found = false; // event not found for (int j = 0; j < ixfinexp - ixdebexp + 1; j++) { // for each visible point int ixep = ixdebexp + j; // current index for (int k = 0; k < efejsynpt[ixep].size(); k++) { // for each assignment if (evt.getSource() == jmiexp[nbjmi]) { // found ixexp = ixep; // its index found = true; break; // end this for } // not found nbjmi++; } if (found) { break; // end search } } jpmexp = null; // no more active if (found) { myrepaint(p_cxmi, p_epymi, p_cwidth, p_epyma - p_epymi + 1); // all exp/pred area setJta(); // update TextArea return; } } // assignment management if (jpmassign != null) { int nbass; // unselect exp if (jmiunselexp.isEnabled()) { if (evt.getSource() == jmiunselexp) { ixexp = -1; // no exp selected jpmassign = null; // no more active myrepaint(p_cxmi, p_epymi, p_cwidth, p_epyma - p_epymi + 1); // all exp/pred area setJta(); // update TextArea return; } } // unassign if (jmunass.isEnabled()) { boolean found = false; // event not found nbass = efejsynpt[ixexp].size(); // number of assignments for (int i = 0; i < nbass; i++) { // for each one if (evt.getSource() == jmiunass[i]) { // unassign one // found cejpt = efejsynpt[ixexp].get(i); pfiexpa[cejpt.getIpred()] = -1; if (nbass == 1) { // only one assignment, empty it cejpt.setFaso(" "); cejpt.setSaso(" "); cejpt.setJsyn(""); cejpt.setIpred(-1); efexasg[ixexp] = ""; } else { // suppress it efejsynpt[ixexp].remove(i); } found = true; // end search break; } } if (!found) { if (evt.getSource() == jmiunassall) { // unassign all for (int i = nbass - 1; i >= 0; i--) { cejpt = efejsynpt[ixexp].get(i); pfiexpa[cejpt.getIpred()] = -1; if (i == 0) { // only one assignment, empty it cejpt.setFaso(" "); cejpt.setSaso(" "); cejpt.setJsyn(""); cejpt.setIpred(-1); efexasg[ixexp] = ""; } else { // suppress it efejsynpt[ixexp].remove(i); } } found = true; // end search } } if (found) { jpmassign = null; // no more active setAsog(ixexp); // set global frequency and intensity marks myrepaint(p_cxmi, p_epymi, p_cwidth, p_epyma - p_epymi + 1); // all exp/pred area setJta(); // update TextArea updateExp(jobplay.getExpFileName()); return; } } // set frequency or intensity mark if (jmfaso.isEnabled() && jmsaso.isEnabled()) { boolean found = false; nbass = efejsynpt[ixexp].size(); // nb of assignments for (int i = 0; i < nbass; i++) { // for each one cejpt = efejsynpt[ixexp].get(i); for (int j = 0; j < nbxaso; j++) { // for each possible value of freq/int mark // frequency mark if (evt.getSource() == jmifaso[i][j]) { if (cejpt.getSaso().equals(" ") && xaso[j].equals(" ")) { // unallowed JOptionPane.showMessageDialog(null, "at least one mark (frequency or intensity) has to be set"); } else { // do it cejpt.setFaso(xaso[j]); found = true; } break; // end this for } // intensity mark if (evt.getSource() == jmisaso[i][j]) { if (cejpt.getFaso().equals(" ") && xaso[j].equals(" ")) { // unallowed JOptionPane.showMessageDialog(null, "at least one mark (frequency or intensity) has to be set"); } else { // do it cejpt.setSaso(xaso[j]); found = true; } break; // end this for } } if (found) { break; // end search } } if (found) { jpmassign = null; // no more active setAsog(ixexp); // set global frequency and intensity marks myrepaint(ip_X(efx[ixexp], p_cxmi, p_cxma) - p_ovray, p_epymi, p_ovwidth, p_yatma - p_epymi + 1); // limited along X direction setJta(); // update TextArea updateExp(jobplay.getExpFileName()); return; } } // set EXASG if (jmexasg.isEnabled()) { for (int i = 0; i < nbexasg; i++) { // for each possible EXASG if (evt.getSource() == jmiexasg[i]) { // found efexasg[ixexp] = exasg[i]; setJta(); // update TextArea updateExp(jobplay.getExpFileName()); return; } } } // set comment if (jmcomm.isEnabled()) { nbass = efejsynpt[ixexp].size(); // nb of assignments for (int i = 0; i < nbass; i++) { // for each one if (evt.getSource() == jmicomm[i]) { // found cejpt = efejsynpt[ixexp].get(i); JFnewText jfnt = new JFnewText(this, cejpt.getComm(), ixexp, i); // choose the comment jfnt.setVisible(true); return; } } } // add a new EXASG if (evt.getSource() == jmiaddexasg) { JFnewText jfnt = new JFnewText(this); // choose the new EXASG jfnt.setVisible(true); return; } // set default EXASG for (int i = 0; i < nbexasg; i++) { if (evt.getSource() == jmidefexasg[i]) { defexasg = exasg[i]; return; } } } } ///////////////////////////////////////////////////////////////////// // set ticks and interval inside the range // input : // xdeb <- beginning X // delta <- range // output : // cd[0] <- 1st X tick ( >= xdeb) // cd[1] <- interval (1Exx, 2Exx ou 5Exx) // cd[2] <- nb of intervals // cd[3] <- last X tick ( <= xdeb+delta) // // we dont try to set xdeb and delta on an interval multiple // private void cadreint(double xdeb, double delta, double[] cd) { NumberFormat nf; // to set interval DecimalFormat df; // idem String cStr; // current string int iexp; // exponent int imant; // mantissa double cxdeb; // 1st X tick double cdelta; // current range double cint; // interval int nbint; // nb of intervals inside the range double cx; // variable nf = NumberFormat.getNumberInstance(Locale.US); // ask a plain format df = (DecimalFormat) nf; // reduce to decimal format df.applyPattern(".0E00"); // define pattern cdelta = Math.abs(delta); // current range cStr = df.format(cdelta); // format it cx = Double.parseDouble(cStr); // reread if (cx < cdelta) { // rounded up cdelta = cdelta + Double.parseDouble(".05" + cStr.substring(2)); // fit range cStr = df.format(cdelta); } iexp = Integer.parseInt(cStr.substring(3)); // exponent imant = Integer.parseInt(cStr.substring(1, 2)); // mantissa // interval = 1, 2 or 5Exx if (imant > 5) { imant = 1; iexp = iexp + 1; } else if (imant > 2) { imant = 5; } cint = imant * Math.pow(10, iexp - 2); // interval // 1st X tick long l = (long) (xdeb / cint); // nb of intervals in xdeb cx = xdeb - l * cint; // difference // xdeb positive and interval multiple ? if (xdeb > 0.) { if (cx > 0.) { // no l++; } } cxdeb = l * cint; // fit xdeb // nb of intervals cdelta = xdeb + Math.abs(delta) - cxdeb; nbint = (int) (cdelta / cint); // nb of intervals in range cd[0] = cxdeb; cd[1] = cint; cd[2] = nbint; cd[3] = cxdeb + nbint * cint; } ///////////////////////////////////////////////////////////////////// /** * Show a progress bar in panel. * * @param text waiting message */ public void prepThread(String text) { removeAll(); // clean setLayout(new BorderLayout()); JProgressBar progressBar = new JProgressBar(); // create progress bar progressBar.setStringPainted(true); // with title progressBar.setString(text); // set it progressBar.setIndeterminate(true); // we don't know how long time Box box = Box.createVerticalBox(); // box box.add(Box.createVerticalStrut((int) (this.getHeight() / 2.))); // at the middle of the window box.add(progressBar); // add JProgressBar add(box, "North"); // add box revalidate(); } /** * Redraw panel after thread. */ public void endThread() { removeAll(); // clean repaint(); // all } ///////////////////////////////////////////////////////////////////// /** * Set experiment data and redraw panel. * * @param cexpf experiment file */ @SuppressWarnings("unchecked") public void setExp(ExpFile cexpf) { efready = false; // exp file not ready expf = cexpf; // exp file efnbxy = cexpf.getNbxy(); // nb of exp data efx = new double[efnbxy]; // frequency efx = cexpf.getX(); effasog = new String[efnbxy]; // global frequency mark efy = new double[efnbxy]; // intensity efy = cexpf.getY(); efsasog = new String[efnbxy]; // global intensity mark efsdobs = new String[efnbxy]; // frequency and intensity standard deviations efsdobs = cexpf.getSdobs(); efejsynpt = new ArrayList[efnbxy]; // ArrayList of ExpJsynPoint for each data efejsynpt = cexpf.getEjsynpt(); efexasg = new String[efnbxy]; // EXASG efexasg = cexpf.getExasg(); for (int i = 0; i < efnbxy; i++) { setAsog(i); // set global frequency mark and global intensity mark } alexasg = new ArrayList<>(); // possible EXASG for (int i = 0; i < efnbxy; i++) { addExasg(efexasg[i]); } addExasg(PanAff.EXASG_DEFAULT); // default SPVIEW EXASG 30 char. setDefexasg(); efdefxlim(); // define first and last exp points in window epLink(); // link exp and pred through assignments efready = true; // exp file ready myrepaint(p_cxmi, p_epymi, p_cwidth, p_yatma - p_epymi + 1); // all exp area } /** * Set prediction data and redraw panel. * * @param cpredf prediction file */ public void setPred(PredFile cpredf) { pfready = false; // pred file NOT ready pfnbxy = cpredf.getNbxy(); // nb of pred data pfx = new double[pfnbxy]; // frequency pfx = cpredf.getX(); pfy = new double[pfnbxy]; // intensity pfy = cpredf.getY(); pfj = new int[pfnbxy]; // J inf pfj = cpredf.getJinf(); pfjsyn = new String[pfnbxy]; // assignment pfjsyn = cpredf.getJsyn(); pfiexpa = new int[pfnbxy]; // index of assigned exp pfdefxlim(); // define first and last pred points in window if (efnbxy != 0) { // reload exp data (may be reduced) expf.read(); setExp(expf); } else { epLink(); // link exp and pred through assignments } jfsp = new JFSelPred(this, (int) (this.getLocationOnScreen().getX() + .75 * (p_cxma - p_cxmi)), (int) (this.getLocationOnScreen().getY() + p_epyma + 10), pfx, pfy, pfjsyn); jfsp.requestFocus(); ixpredable = -1; // NO current selectable pred pfready = true; // pred file ready myrepaint(p_cxmi, p_yprmi, p_cwidth, p_yprma - p_yprmi + 1); // all pred area } /** * Set experiment associated to a data file. * * @param cdf index of the data file */ public String expAss2(int cdf) { if (cdf > nbdf - 1) { // bad index return "!!! index out of bounds in PanAff.expAss2 (" + cdf + ")."; } if (cdf >= 0 && cdf == pfass2) { // the same file can't be associated to exp AND pred return "This data file is already associated to prediction"; } if (efass2 >= 0) { // already associated, unshift if necessary if (xshifted[efass2] != 0.) { for (int i = 0; i < efnbxy; i++) { efx[i] -= xshifted[efass2]; } } } efass2 = cdf; if (efass2 >= 0) { // not associated to null // shift if necessary if (xshifted[efass2] != 0.) { for (int i = 0; i < efnbxy; i++) { efx[i] += xshifted[efass2]; } } } efdefxlim(); // define first and last exp points in window myrepaint(p_cxmi, p_epymi, p_cwidth, p_yatma - p_epymi + 1); // exp area return ""; } /** * Set prediction associated to a data file. * * @param cdf index of the data file */ public String predAss2(int cdf) { if (cdf > nbdf - 1) { // bad index return "!!! index out of bounds in PanAff.predAss2 (" + cdf + ")."; } if (cdf >= 0 && cdf == efass2) { // the same file can't be associated to exp AND pred return "This data file is already associated to experiment"; } if (pfass2 >= 0) { // already associated, unshift if necessary if (xshifted[pfass2] != 0.) { for (int i = 0; i < pfnbxy; i++) { pfx[i] -= xshifted[pfass2]; } } } pfass2 = cdf; if (pfass2 >= 0) { // not associated to null // shift if necessary if (xshifted[pfass2] != 0.) { for (int i = 0; i < pfnbxy; i++) { pfx[i] += xshifted[pfass2]; } } if (ntfile[pfass2].equals("spectrum")) { lsallowed = true; } } else { lsallowed = false; } pfdefxlim(); // define first and last pred points in window myrepaint(p_cxmi, p_yprmi, p_cwidth, p_yprma - p_yprmi + 1); // pred area return ""; } /** * Add data file and redraw panel. * * @param cdataf data file to add */ public void addData(DataFile cdataf) { if (nbdf == 0 && cdataf.getType().equals("predas")) { // the 1st loaded data file can't be a prediction file System.out.println("Invalid 1st data file in PanAff.addData()" + lnsep + "the 1st loaded data file can't be a prediction file"); return; } // data file NOT ready ntfile[nbdf] = cdataf.getType(); // type nbxy[nbdf] = cdataf.getNbxy(); // nb of points x[nbdf] = new double[nbxy[nbdf]]; // frequency x[nbdf] = cdataf.getX(); y[nbdf] = new double[nbxy[nbdf]]; // intensity y[nbdf] = cdataf.getY(); // default yreverse[nbdf] = false; xshifted[nbdf] = 0.; yshifted[nbdf] = 0.; // set frequency limits for this file // data have to be sorted according to X double cxmi = x[nbdf][0]; // lowest freqency double cxma = x[nbdf][nbxy[nbdf] - 1]; // highest frequency // set intensity limits for this data file double cymi = y[nbdf][0]; // starting lowest intensity double cyma = y[nbdf][0]; // starting highest intensity for (int i = 0; i < nbxy[nbdf]; i++) { // for each point if (y[nbdf][i] < cymi) { cymi = y[nbdf][i]; // lowest intensity } if (y[nbdf][i] > cyma) { cyma = y[nbdf][i]; // highest intensity } } if (!ntfile[nbdf].equals("spectrum")) { // stick files, 0. has to be in cymi = Math.min(cymi, 0.); cyma = Math.max(cyma, 0.); } // set global limits for pred-as-stick and others if (nbdf == 0) { // 1st data file (can't be pred-as-stick) axmi = cxmi; axma = cxma; aymi = cymi; ayma = cyma; apasyma = 0.; // dummy default value, will be set by 1st pred-as-stick file load saxmi = axmi; saxma = axma; } else { axmi = Math.min(axmi, cxmi); axma = Math.max(axma, cxma); if (ntfile[nbdf].equals("predas")) { // pred-as-stick have their own Y settings apasymi = 0.; // base value apasyma = Math.max(apasyma, cyma); // max value apasydelta = apasyma; // max range apasyrev = apasyma; // reverse value cadreint(apasymi, apasydelta, acadrepasY); // set pred-as-stick Y axis tics nbpredasf++; // nb of pred-as-stick files loaded // allow pred-as-stick menus jmizoompasy.setEnabled(true); jmrescalepasy.setEnabled(true); jmirestorepasy.setEnabled(true); } else { aymi = Math.min(aymi, cymi); ayma = Math.max(ayma, cyma); } } axdelta = axma - axmi; cadreint(axmi, axdelta, acadreX); aydelta = ayma - aymi; ayrev = ayma + aymi; cadreint(aymi, aydelta, acadreY); // set current window limits // current window is (re)set to global limits each time a data file is loaded bxmi = axmi; // frequency mini bxma = axma; // frequency maxi bxdelta = axdelta; // frequency range bcadreX = acadreX.clone(); // set X axis ticks if (ntfile[nbdf].equals("predas")) { // pred-as-stick are specifically involved in Y settings bpasymi = apasymi; // pred-as-stick intensity mini bpasyma = apasyma; // pred-as-stick intensity maxi bpasydelta = apasydelta; // pred-as-stick intensity range bcadrepasY = acadrepasY.clone(); // set pred-as-stick Y axis ticks } else { bymi = aymi; // intensity mini byma = ayma; // intensity maxi bydelta = aydelta; // intensity range bcadreY = acadreY.clone(); // set Y axis ticks } for (int i = 0; i <= nbdf; i++) { // for each data file ixdeb[i] = 0; // first data point in window ixfin[i] = nbxy[i] - 1; // last data point in window show[nbdf] = true; // show data } // set global X limits (including shift) sxmi[nbdf] = cxmi; sxma[nbdf] = cxma; // nbdf++; // one more data file // saxmi = sxmi[0]; saxma = sxma[0]; for (int i = 0; i < nbdf; i++) { saxmi = Math.min(sxmi[i], saxmi); saxma = Math.max(sxma[i], saxma); } // efdefxlim(); // define first and last exp points in window pfdefxlim(); // define first and last pred points in window prevp_wxma = -1; // call to setRedSpect at next repaint dfready[nbdf - 1] = true; // data file ready } /** * Reload data file and redraw panel. * * @param cdataf data file to reload * @param ci data file index */ public void reloadData(DataFile cdataf, int ci) { double cshift; // current shift // set data nbxy[ci] = cdataf.getNbxy(); // nb of points x[ci] = cdataf.getX(); // frequency y[ci] = cdataf.getY(); // intensity // X shift cshift = xshifted[ci]; // current shift if (cshift != 0.) { for (int i = 0; i < nbxy[ci]; i++) { // update shifted data x[ci][i] += cshift; } } defxlim(ci); // define first and last data points in window // set global X limits (including shift) sxmi[ci] = x[ci][0]; sxma[ci] = x[ci][nbxy[ci] - 1]; saxmi = sxmi[0]; saxma = sxma[0]; for (int i = 0; i < nbdf; i++) { saxmi = Math.min(sxmi[i], saxmi); saxma = Math.max(sxma[i], saxma); } // Y shift cshift = yshifted[ci]; // current shift if (cshift != 0.) { for (int i = 0; i < nbxy[ci]; i++) { // update shifted data y[ci][i] += cshift; } } prevp_wxma = -1; // call to setRedSpect at next repaint } /** * @since SPVIEW2 */ public void reloadPredf(PredFile cpredff) { // set data double cshift; // current shift pfnbxy = cpredff.getNbxy(); // nb of points pfx = cpredff.getX(); // frequency pfy = cpredff.getY(); // intensity // X shift if (pfass2 >= 0) { cshift = xshifted[pfass2]; // current shift if (cshift != 0.) { for (int i = 0; i < pfnbxy; i++) { // update shifted data pfx[i] += cshift; } } } pfdefxlim(); // define first and last data points in window prevp_wxma = -1; // call to setRedSpect at next repaint } ///////////////////////////////////////////////////////////////////// public int getPfass2() { int indexColor; indexColor = pfass2; return indexColor; } /** * get/set the index of the color to associate to prediction file * * @since SPVIEW2 */ public void setPfass2(int val) { pfass2 = val; } public int getEfass2() { int indexColor; indexColor = efass2; return indexColor; } /** * get/set the index of the color to associate to prediction file * * @since SPVIEW2 */ public void setEfass2(int val) { efass2 = val; } ///////////////////////////////////////////////////////////////////// /** * Shift drawing one interval down along Y axis. */ public void jbyplus() { if (nbdf == 0) { // no data file return; } bymi += bcadreY[1]; // one interval byma = bymi + bydelta; // current height cadreint(bymi, bydelta, bcadreY); // reset Y axis ticks myrepaint(0, p_cymi - p_gap, p_wxma + 1, p_cheight + 3 * p_gap); // Y axis and all data area } /** * Center drawing along Y axis. */ public void jbyegal() { if (nbdf == 0) { // no data file return; } bymi = (aymi + ayma) / 2. - bydelta / 2.; // center byma = bymi + bydelta; // current height cadreint(bymi, bydelta, bcadreY); // reset Y axis ticks myrepaint(0, p_cymi - p_gap, p_wxma + 1, p_cheight + 3 * p_gap); // Y axis and all data area } /** * Shift drawing one interval up along Y axis. */ public void jbymoins() { if (nbdf == 0) { // no data file return; } bymi -= bcadreY[1]; // one interval byma = bymi + bydelta; // current height cadreint(bymi, bydelta, bcadreY); // reset Y axis ticks myrepaint(0, p_cymi - p_gap, p_wxma + 1, p_cheight + 3 * p_gap); // Y axis and all data area } /** * Shift drawing at the beginning of X axis. */ public void jbxpipeinf() { if (nbdf == 0) { // no data file return; } // start global centered then simulate << button to find beginning bxmi = (axmi + axma) / 2. - bxdelta / 2.; while (bxmi > saxmi) { bxmi -= bxdelta; } bxma = bxmi + bxdelta; // current width setXax(); // set X axis characteristics for (int i = 0; i < nbdf; i++) { // for each data file defxlim_red(i); // define 1st and last points in data area } repaint(); // all } /** * Shift drawing one screen right along X axis. */ public void jbx2inf() { if (nbdf == 0) { // no data file return; } bxmi -= bxdelta; // one window width bxma = bxmi + bxdelta; // current width setXax(); // set X axis characteristics for (int i = 0; i < nbdf; i++) { // for each data file defxlim_red(i); // define 1st and last points in data area } repaint(); // all } /** * Shift drawing one interval right along X axis. */ public void jbx1inf() { if (nbdf == 0) { // no data file return; } bxmi -= bcadreX[1]; // one interval bxma = bxmi + bxdelta; // current width setXax(); // set X axis characteristics for (int i = 0; i < nbdf; i++) { // for each data file defxlim_red(i); // define 1st and last points in data area } repaint(); // all } /** * Center X drawing. */ public void jbxegal() { if (nbdf == 0) { // no data file return; } bxmi = (axmi + axma) / 2. - bxdelta / 2.; // center bxma = bxmi + bxdelta; // current width setXax(); // set X axis characteristics for (int i = 0; i < nbdf; i++) { // for each data file defxlim_red(i); // define 1st and last points in data area } repaint(); // all } /** * Shift drawing one interval left along X axis. */ public void jbx1sup() { if (nbdf == 0) { // no data file return; } bxmi += bcadreX[1]; // one interval bxma = bxmi + bxdelta; // current width setXax(); // set X axis characteristics for (int i = 0; i < nbdf; i++) { // for each data file defxlim_red(i); // define 1st and last points in data area } repaint(); // all } /** * Shift drawing one screen left along X axis. */ public void jbx2sup() { if (nbdf == 0) { // no data file return; } bxmi += bxdelta; // one width bxma = bxmi + bxdelta; // current width setXax(); // set X axis characteristics for (int i = 0; i < nbdf; i++) { // for each data file defxlim_red(i); // define 1st and last points in data area } repaint(); // all } /** * Shift drawing at the end of the X axis. */ public void jbxpipesup() { if (nbdf == 0) { // no data file return; } // start global centered then simulate >> button to find end bxma = (axmi + axma) / 2. + bxdelta / 2.; while (bxma < saxma) { bxma += bxdelta; } bxmi = bxma - bxdelta; // current width setXax(); // set X axis characteristics for (int i = 0; i < nbdf; i++) { // for each data file defxlim_red(i); // define 1st and last points in data area } repaint(); // all } /** * Restore All in basic state. */ public void resetAll() { if (nbdf == 0) { // no data file return; } // restore basic global state showbar = false; jmihidebar.setEnabled(false); // X axis bxmi = axmi; bxma = axma; bxdelta = axdelta; bcadreX = acadreX.clone(); // Y axis bymi = aymi; byma = ayma; bydelta = aydelta; bcadreY = acadreY.clone(); // pred-as-stick Y axis bpasymi = apasymi; bpasyma = apasyma; bpasydelta = apasydelta; bcadrepasY = acadrepasY.clone(); // data files for (int i = 0; i < nbdf; i++) { show[i] = true; xUnshiftLoc(i); // X unshift if necessary yUnshiftLoc(i); // Y unshift if necessary yreverse[i] = false; // define first and last data points in window ixdeb[i] = 0; ixfin[i] = nbxy[i] - 1; } // efdefxlim(); // define first and last exp points in window pfdefxlim(); // define first and last pred points in window prevp_wxma = -1; // call to setRedSpect at next repaint repaint(); // all } ///////////////////////////////////////////////////////////////////// // set X axis characteristics private void setXax() { cadreint(bxmi, bxdelta, bcadreX); // set X axis ticks for (int i = 0; i < nbdf; i++) { defxlim(i); // define first and last data points in window } efdefxlim(); // define first and last exp points in window pfdefxlim(); // define first and last pred points in window } // apply X scaling choice private void repropx(int ci) { int jsf; // index of scaling factor double bxmed = bxmi + bxdelta / 2.; // window center if (ci < nbfact) { // multiply jsf = nbfact - ci - 1; bxdelta *= fact[jsf]; } else { // divide jsf = ci - nbfact; bxdelta /= fact[jsf]; } // define window limits bxmi = bxmed - bxdelta / 2.; // keep centered bxma = bxmi + bxdelta; bxdelta = bxma - bxmi; // window frequency range setXax(); // set X axis characteristics prevp_wxma = -1; // call to setRedSpect at next repaint } // apply Y scaling choice private void repropy(int ci) { int jsf; // index of scaling factor double bymed = bymi + bydelta / 2.; // window center if (ci < nbfact) { // multiply jsf = nbfact - ci - 1; bydelta *= fact[jsf]; } else { // divide jsf = ci - nbfact; bydelta /= fact[jsf]; } // define window limits bymi = bymed - bydelta / 2.; // keep centered byma = bymi + bydelta; cadreint(bymi, bydelta, bcadreY); // set Y axis characteristics } // apply pred-as-stick Y scaling choice private void reproppasy(int ci) { int jsf; // index of scaling factor double bpasymed = bpasymi + bpasydelta / 2.; // window center if (ci < nbfact) { // multiply jsf = nbfact - ci - 1; bpasydelta *= fact[jsf]; } else { // divide jsf = ci - nbfact; bpasydelta /= fact[jsf]; } // define window limits bpasymi = bpasymed - bpasydelta / 2.; // keep centered bpasyma = bpasymi + bpasydelta; cadreint(bpasymi, bpasydelta, bcadrepasY); // set pred-as-stick Y axis characteristics } // define first and last data points in window private void defxlim(int cj) { int i; for (i = 0; i < nbxy[cj]; i++) { // for each point if (x[cj][i] >= bxmi) { // first point found break; } } ixdeb[cj] = Math.min(i, nbxy[cj] - 1); // in array for (i = ixdeb[cj]; i < nbxy[cj]; i++) { // for each next points if (x[cj][i] > bxma) { // last point exceeded break; } } ixfin[cj] = Math.max(0, i - 1); // at max last data point if (ntfile[cj].equals("spectrum")) { // one more point at each end ixdeb[cj] = Math.max(0, ixdeb[cj] - 1); ixfin[cj] = Math.min(ixfin[cj] + 1, nbxy[cj] - 1); } } // define first and last reduced spectrum data points in window private void defxlim_red(int cj) { int i; if (use_red[cj]) { for (i = 0; i < nbxy_red[cj]; i++) { // for each point if (x[cj][i_red[cj][i]] >= bxmi) { // first point found break; } } ixdeb_red[cj] = Math.min(i, nbxy_red[cj] - 1); // at least first data point for (i = ixdeb_red[cj]; i < nbxy_red[cj]; i++) { // for each next points if (x[cj][i_red[cj][i]] > bxma) { // last point exceeded break; } } ixfin_red[cj] = Math.max(0, i - 1); // at max last data point if (ntfile[cj].equals("spectrum")) { // one more point at each end ixdeb_red[cj] = Math.max(0, ixdeb_red[cj] - 1); // ATTENTION : ceci ne represente qu'une partie du pixel precedent ixfin_red[cj] = Math.min(ixfin_red[cj] + 1, nbxy_red[cj] - 1); // ATTENTION : ceci ne represente qu'une partie du pixel suivant } } } // define first and last exp points in window private void efdefxlim() { if (efnbxy != 0) { // exp file loaded if (nbdf == 0) { // no data file -> all points efixdeb = 0; efixfin = efnbxy - 1; } else { int j; for (j = 0; j < efnbxy; j++) { // for each point if (efx[j] >= bxmi) { // first point found break; } } efixdeb = Math.min(j, efnbxy - 1); // at max last exp point for (j = efixdeb; j < efnbxy; j++) { // for each next points if (efx[j] > bxma) { // last point exceeded break; } } if (j == efnbxy) { efixfin = efnbxy - 1; // in array } else { efixfin = Math.max(0, j - 1); // at max last exp point } } } } // define first and last pred points in window private void pfdefxlim() { if (pfnbxy != 0) { if (nbdf == 0) { // no data file -> all points pfixdeb = 0; pfixfin = pfnbxy - 1; } else { int j; for (j = 0; j < pfnbxy; j++) { // for each point if (pfx[j] >= bxmi) { // first point found break; } } pfixdeb = Math.min(j, pfnbxy - 1); // at max last pred point for (j = pfixdeb; j < pfnbxy; j++) { // for each next points if (pfx[j] > bxma) { // last point exceeded break; } } if (j == pfnbxy) { pfixfin = pfnbxy - 1; // in array } else { pfixfin = Math.max(0, j - 1); // at max last pred point } } } } ///////////////////////////////////////////////////////////////////// /** * Set/unset data showable. * * @param cdf data file index * @param cbool show value */ public void setShow(int cdf, boolean cbool) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.setShow (" + cdf + ")"); return; } show[cdf] = cbool; myrepaint(p_cxmi, p_cymi, p_cwidth, p_cheight); // data area } /** * Reverse Y data reverse state. * * @param cdf data file index */ public void yReverse(int cdf) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.yReverse (" + cdf + ")"); return; } yreverse[cdf] = !yreverse[cdf]; myrepaint(p_cxmi, p_cymi, p_cwidth, p_cheight); // data area } /** * Ask to shift X data. * * @param cdf data file index */ public void xShift(int cdf) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.xShift (" + cdf + ")"); return; } shiftedf = cdf; shiftx = true; } /** * Get current X shift. * * @param cdf data file index */ public double getXShift(int cdf) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.getXShift (" + cdf + ")"); return 0.; } return xshifted[cdf]; } /** * Unshift X data to original position. * * @param cdf data file index */ public void xUnshift(int cdf) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.xUnshift (" + cdf + ")"); return; } xUnshiftLoc(cdf); myrepaint(p_cxmi, 0, p_cwidth, p_cyma + 1); // exp/pred/data area } // local method to unshift X data to original position private void xUnshiftLoc(int cdf) { for (int i = 0; i < nbxy[cdf]; i++) { // for each data point x[cdf][i] -= xshifted[cdf]; // unshift it } defxlim(cdf); // define first and last data points in window // set global X limits (including shift) sxmi[cdf] -= xshifted[cdf]; sxma[cdf] -= xshifted[cdf]; saxmi = sxmi[0]; saxma = sxma[0]; for (int i = 0; i < nbdf; i++) { saxmi = Math.min(sxmi[i], saxmi); saxma = Math.max(sxma[i], saxma); } // defxlim_red(cdf); // define first and last data points in window if (efass2 == cdf) { // exp associated to this data file for (int i = 0; i < efnbxy; i++) { // for each exp point efx[i] -= xshifted[cdf]; // unshift it } efdefxlim(); // define first and last exp points in window } if (pfass2 == cdf) { // pred associated to this data file for (int i = 0; i < pfnbxy; i++) { // for each pred point pfx[i] -= xshifted[cdf]; // unshift it } pfdefxlim(); // define first and last pred points in window } xshifted[cdf] = 0.; // X shift value } /** * Ask to shift Y data. * * @param cdf data file index */ public void yShift(int cdf) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.yShift (" + cdf + ")"); return; } shiftedf = cdf; shifty = true; } /** * Set current X shift to the value given in argument * * @since SPVIEW2 */ public void setXShift(double xshift, int cdf) { xshifted[cdf] = xshift; for (int i = 0; i < nbxy[cdf]; i++) { // for each data point x[cdf][i] += xshifted[cdf]; } defxlim(cdf); // define first and last data points in window // set global X limits (including shift) sxmi[cdf] += xshifted[cdf]; sxma[cdf] += xshifted[cdf]; saxmi = sxmi[0]; saxma = sxma[0]; for (int i = 0; i < nbdf; i++) { saxmi = Math.min(sxmi[i], saxmi); saxma = Math.max(sxma[i], saxma); } // defxlim_red(cdf); // define first and last data points in window if (efass2 == cdf) { // exp associated to this data file for (int i = 0; i < efnbxy; i++) { // for each exp point efx[i] += xshifted[cdf]; // unshift it } efdefxlim(); // define first and last exp points in window } if (pfass2 == cdf) { // pred associated to this data file for (int i = 0; i < pfnbxy; i++) { // for each pred point pfx[i] += xshifted[cdf]; // unshift it } pfdefxlim(); // define first and last pred points in window } } /** * Set current Y shift to the value given in argument * * @since SPVIEW2 */ public void setYShift(double yshift, int cdf) { yshifted[cdf] = yshift; for (int i = 0; i < nbxy[cdf]; i++) { // for each data point y[cdf][i] += yshifted[cdf]; } } /** * Set current X zoom to the value given in argument * * @since SPVIEW2 */ public void setXZoom(double boundMin, double boundMax) { // X zoom bxma = Math.max(boundMin, boundMax); bxmi = Math.min(boundMin, boundMax); bxdelta = bxma - bxmi; // window frequency range setXax(); // set X axis characteristics zoomx = false; // no x zoom area to define zoom = false; // no zoom area to draw prevp_wxma = -1; // call to setRedSpect at next repaint repaint(); // all } /** * Set current Y zoom to the value given in argument * * @since SPVIEW2 */ public void setYZoom(double boundMin, double boundMax) { byma = Math.max(boundMin, boundMax); bymi = Math.min(boundMin, boundMax); bydelta = byma - bymi; cadreint(bymi, bydelta, bcadreY); // resize the Y axis zoomy = false; // no y zoom area to define zoom = false; } /** * Get p_zx0 value of X zoom. * * @since SPVIEW2 */ public Double getbxmi() { return bxmi; } /** * Get p_zx1 value of X zoom. * * @since SPVIEW2 */ public Double getbxma() { return bxma; } /** * Get p_zy0 value of Y zoom. * * @since SPVIEW2 */ public Double getbymi() { return bymi; } /** * Get p_zy1 value of Y zoom. * * @since SPVIEW2 */ public Double getbyma() { return byma; } /** * Get current Y shift. */ public double getYShift(int cdf) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.getYShift (" + cdf + ")"); return 0.; } return yshifted[cdf]; } /** * Unshift Y data original position. * * @param cdf data file index */ public void yUnshift(int cdf) { if ((cdf < 0) || (cdf > (nbdf - 1))) { System.out.println("!!! index out of bounds in PanAff.yUnshift (" + cdf + ")"); return; } yUnshiftLoc(cdf); myrepaint(p_cxmi, p_cymi, p_cwidth, p_cheight); // data area } // local method to unshift Y data to original position private void yUnshiftLoc(int cdf) { if (ntfile[cdf].equals("spectrum")) { // only for spectrum data file for (int i = 0; i < nbxy[cdf]; i++) { // for each data point y[cdf][i] -= yshifted[cdf]; // unshift it } } yshifted[cdf] = 0.; // Y shift value } ////////////////////////////////////////////////////////////////////// // associate the corresponding pred assignment index to each exp assignment // each ExpJsynPoint MUST have his corresponding PredJsynPoint // private void epLink() { synchronized void epLink() { if (pfnbxy != 0) { // pred loading/loaded Arrays.fill(pfiexpa, -1); // default status NO assigned exp // if (efnbxy != 0) { // exp loading/loaded // store pred assignments in a PredJsynPoint array PredJsynPoint[] pjp = new PredJsynPoint[pfnbxy]; for (int i = 0; i < pfnbxy; i++) { pjp[i] = new PredJsynPoint(pfjsyn[i], i); } // sort Arrays.sort(pjp); // search int nbrem = 0; // nb of removed orphan ExpJsynPoint for (int i = 0; i < efnbxy; i++) { // for each exp point boolean again = true; // do this loop again while (again) { again = false; // if no ExpJsynPoint removed int nbass = efejsynpt[i].size(); // nb of assignments for (int j = 0; j < nbass; j++) { // for each assignment cejpt = efejsynpt[i].get(j); cStr = cejpt.getJsyn(); if (cStr.trim().length() != 0) { // not an empty assignment int k = Arrays.binarySearch(pjp, new PredJsynPoint(cStr, 0)); // search it if (k >= 0) { // found int pjpInd = pjp[k].getInd(); cejpt.setIpred(pjpInd); // index of assigned pred pfiexpa[pjpInd] = i; // index of assigned exp } else { // not found if (nbass == 1) { // only one assignment, empty it cejpt.setFaso(" "); cejpt.setSaso(" "); cejpt.setJsyn(""); cejpt.setIpred(-1); efexasg[i] = ""; } else { // suppress it efejsynpt[i].remove(j); again = true; // re-do the loop } nbrem++; // nb of removed ExpJsynPoint break; // now } } } } } setJta(); // update TextArea if (nbrem != 0) { int res = JOptionPane.showConfirmDialog( null, nbrem + " assignments not predicted are ignored." + lnsep + "Do you want to update " + jobplay.getExpFileName() + "?", "Remove not predicted lines", JOptionPane.YES_NO_OPTION); if (res == JOptionPane.YES_OPTION) { updateExp(jobplay.getExpFileName()); /* file has been updated, we need to update display */ setExp(expf); } } } } } // create assignment management popup menu private boolean setJpmAssign() { int nbass; // nb of assignments if (!efready) { // no exp file return false; } jpmassign = new JPopupMenu(); // unselect exp jmiunselexp = new JMenuItem("Unselect exp"); if (ixexp < 0) { // no exp selected jmiunselexp.setEnabled(false); } else { jmiunselexp.addActionListener(this); } jpmassign.add(jmiunselexp); jpmassign.addSeparator(); jmithreshold = new JMenuItem("Set prediction threshold"); jmithreshold.setEnabled(true); jmithreshold.addActionListener(this); jpmassign.add(jmithreshold); jpmassign.addSeparator(); // remove an assignment jmunass = new JMenu("Unassign"); if (ixexp < 0) { // no exp selected jmunass.setEnabled(false); } else { nbass = efejsynpt[ixexp].size(); cejpt = efejsynpt[ixexp].get(0); if (cejpt.getJsyn().length() == 0) { // empty assignment jmunass.setEnabled(false); } else { jmiunass = new JMenuItem[nbass]; // a jmi for each assignment for (int i = 0; i < nbass; i++) { cejpt = efejsynpt[ixexp].get(i); jmiunass[i] = new JMenuItem(cejpt.getJsyn()); jmiunass[i].setFont(mono14); jmiunass[i].addActionListener(this); jmunass.add(jmiunass[i]); } } jmiunassall = new JMenuItem("Unassign all"); jmiunassall.addActionListener(this); jmunass.addSeparator(); jmunass.add(jmiunassall); } jpmassign.add(jmunass); // set frequency and intensity mark jmfaso = new JMenu("Set frequency mark"); jmsaso = new JMenu("Set intensity mark"); if (ixexp < 0) { // no exp selected jmfaso.setEnabled(false); jmsaso.setEnabled(false); } else { // create the related menu nbass = efejsynpt[ixexp].size(); cejpt = efejsynpt[ixexp].get(0); if (nbass == 1 && cejpt.getJsyn().length() == 0) { // empty assignment jmfaso.setEnabled(false); jmsaso.setEnabled(false); } else { // a jmi for each assignment and xaso value // set frequency mark JMenu[] jmjsynfaso = new JMenu[nbass]; jmifaso = new JMenuItem[nbass][]; for (int i = 0; i < nbass; i++) { cejpt = efejsynpt[ixexp].get(i); jmjsynfaso[i] = new JMenu(cejpt.getJsyn() + " (" + cejpt.getFaso() + ")"); jmjsynfaso[i].setFont(mono14); jmifaso[i] = new JMenuItem[nbxaso]; for (int j = 0; j < nbxaso; j++) { jmifaso[i][j] = new JMenuItem("'" + xaso[j] + "'"); jmifaso[i][j].setFont(mono14); jmifaso[i][j].addActionListener(this); jmjsynfaso[i].add(jmifaso[i][j]); } jmfaso.add(jmjsynfaso[i]); } // set intensity mark JMenu[] jmjsynsaso = new JMenu[nbass]; jmisaso = new JMenuItem[nbass][]; for (int i = 0; i < nbass; i++) { cejpt = efejsynpt[ixexp].get(i); jmjsynsaso[i] = new JMenu(cejpt.getJsyn() + " (" + cejpt.getSaso() + ")"); jmjsynsaso[i].setFont(mono14); jmisaso[i] = new JMenuItem[nbxaso]; for (int j = 0; j < nbxaso; j++) { jmisaso[i][j] = new JMenuItem("'" + xaso[j] + "'"); jmisaso[i][j].setFont(mono14); jmisaso[i][j].addActionListener(this); jmjsynsaso[i].add(jmisaso[i][j]); } jmsaso.add(jmjsynsaso[i]); } } } jpmassign.add(jmfaso); jpmassign.add(jmsaso); // modify exasg if (ixexp < 0) { // no exp selected jmexasg = new JMenu("Modify EXASG ()"); jmexasg.setEnabled(false); } else { jmexasg = new JMenu("Modify EXASG (" + efexasg[ixexp] + ")"); // with current value cejpt = efejsynpt[ixexp].get(0); if (cejpt.getJsyn().length() == 0) { // empty assignment jmexasg.setEnabled(false); } else { // a jmi for each possible EXASG value jmiexasg = new JMenuItem[nbexasg]; for (int i = 0; i < nbexasg; i++) { jmiexasg[i] = new JMenuItem("'" + exasg[i] + "'"); jmiexasg[i].setFont(mono14); jmiexasg[i].addActionListener(this); jmexasg.add(jmiexasg[i]); } } } jpmassign.add(jmexasg); // modify comment jmcomm = new JMenu("Set comment"); if (ixexp < 0) { // no exp selected jmcomm.setEnabled(false); } else { // a jmi for each assignment nbass = efejsynpt[ixexp].size(); jmicomm = new JMenuItem[nbass]; for (int i = 0; i < nbass; i++) { cejpt = efejsynpt[ixexp].get(i); jmicomm[i] = new JMenuItem(cejpt.getJsyn() + " (" + cejpt.getComm() + ")"); jmicomm[i].setFont(mono14); jmicomm[i].addActionListener(this); jmcomm.add(jmicomm[i]); } } jpmassign.add(jmcomm); jpmassign.addSeparator(); // add a new EXASG jmiaddexasg = new JMenuItem("Add a new EXASG string"); jmiaddexasg.addActionListener(this); jpmassign.add(jmiaddexasg); // set default EXASG // set default EXASG JMenu jmdefexasg = new JMenu("Set default EXASG (" + defexasg + ")"); jmidefexasg = new JMenuItem[nbexasg]; // a jmi for each possible EXASG value for (int i = 0; i < nbexasg; i++) { jmidefexasg[i] = new JMenuItem("'" + exasg[i] + "'"); jmidefexasg[i].setFont(mono14); jmidefexasg[i].addActionListener(this); jmdefexasg.add(jmidefexasg[i]); } jpmassign.add(jmdefexasg); return true; } // sort an efejsynpt private void sortEfejsynpt(int ci) { // copy in an array, sort, restore in original ExpJsynPoint[] aejpt; int nbass = efejsynpt[ci].size(); if (nbass > 1) { // more than one assignment aejpt = new ExpJsynPoint[nbass]; // array for (int k = 0; k < nbass; k++) { aejpt[k] = efejsynpt[ci].get(k); // copy } Arrays.sort(aejpt); // sort efejsynpt[ci] = new ArrayList<>(); for (int k = 0; k < nbass; k++) { efejsynpt[ci].add(aejpt[k]); // restore } } } /** * Add a new EXASG value. * * @param cexasg new EXASG string */ public void addExasg(String cexasg) { if (cexasg.trim().length() == 0) { // empty string return; } if (!alexasg.contains(cexasg)) { // not already in ArrayList alexasg.add(cexasg); // add nbexasg = alexasg.size(); // nb of possible EXASG exasg = new String[nbexasg]; // update exasg[] for (int i = 0; i < nbexasg; i++) { exasg[i] = alexasg.get(i); } Arrays.sort(exasg); // sort } } // set default EXASG private void setDefexasg() { if (alexasg.contains(PanAff.EXASG_DEFAULT)) { // found in ArrayList defexasg = PanAff.EXASG_DEFAULT; } } /** * Modify an exp assignment comment. * * @param cstr comment string * @param cixexp exp index * @param ciass assignment index */ public void setComm(String cstr, int cixexp, int ciass) { cejpt = efejsynpt[cixexp].get(ciass); cejpt.setComm(cstr); setJta(); // update TextArea updateExp(jobplay.getExpFileName()); } private boolean updateExp(String filename) { try { out1 = new PrintWriter(new BufferedWriter(new FileWriter(filename))); for (int i = 0; i < efnbxy; i++) { // for each exp point int nbass = efejsynpt[i].size(); // nb of assignments for (int j = 0; j < nbass; j++) { // for each assignment cejpt = efejsynpt[i].get(j); // NUO (5 char, RIGHT) String cStrNUO = " " + ("" + (i + 1)).trim(); cStrNUO = cStrNUO.substring(cStrNUO.length() - 5); // SDFOBS,SDSOBS (16 char) String cStrSDOBS = efsdobs[i] + FortranFormat.bStr(16); cStrSDOBS = cStrSDOBS.substring(0, 16); // JSYN (21 char) String cStrJSYN = cejpt.getJsyn() + FortranFormat.bStr(21); cStrJSYN = cStrJSYN.substring(0, 21); // EXASG (30 char) String cStrEXASG = efexasg[i] + FortranFormat.bStr(30); cStrEXASG = cStrEXASG.substring(0, 30); /* we don't want to store shifted experiment data, so we unshift for the record if any */ double sefx = efx[i]; double sefy = efy[i]; if (efass2 >= 0) { sefx -= xshifted[efass2]; sefy -= yshifted[efass2]; } cStr = (" " + // ISP cStrNUO + // NUO line number FortranFormat.formFreq(sefx) + // FOBS frequency " " + cejpt.getFaso() + // FASO frequency mark FortranFormat.formInt(sefy) + // SOBS intensity " " + cejpt.getSaso() + // SASO intensity mark cStrSDOBS + // SDFOBS,SDSOBS frequency and intensity standard deviations string " " + cStrJSYN + // JSYN assignment " " + cStrEXASG + // EXASG " " + cejpt.getComm()); // comment int l; for (l = cStr.length(); l > 0; l--) { if (cStr.charAt(l - 1) != ' ') { break; } } out1.println(cStr.substring(0, l)); // without extra spaces } } } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null, "IO error while writing file" + lnsep + nsavexp + lnsep + ioe); return false; } finally { // close the file if (out1 != null) { out1.close(); if (out1.checkError()) { JOptionPane.showMessageDialog(null, "PrintWriter error while creating exp file" + lnsep + nsavexp); } } } return true; } /** * Save updated experiment file
* following FORMAT 3000 of eq_tds.f * * @since SPVIEW2 Or update current experiment file */ public void saveExp() { if (!efready) { // no exp file loaded return; } // choose output file JFileChooser jfcfile = new JFileChooser(workd); // default = working directory jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // only files jfcfile.setDialogTitle("Define the name of the saved experiment file"); Container parent = this.getParent(); int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select if (choice == JFileChooser.APPROVE_OPTION) { nsavexp = jfcfile.getSelectedFile().getAbsolutePath(); // full file name if (updateExp(nsavexp)) { // finished JOptionPane.showMessageDialog(null, "Assignment file" + lnsep + nsavexp + lnsep + "is saved"); } } } /** * Add assignment(s) to selected exp. */ public void addAss2exp(boolean freq) { // add assignment(s) for (int k = 0; k < nbixpred; k++) { if (ixpreds[k] == 1) { // pred selected int cixpred = ixpred[k]; // add it pfiexpa[cixpred] = ixexp; // index of assigned exp int nbass = efejsynpt[ixexp].size(); // number of assignments cejpt = efejsynpt[ixexp].get(0); if (nbass == 1 && cejpt.getJsyn().length() == 0) { // the only assignment is empty, replace it if (freq) { // frequency cejpt.setFaso("+"); cejpt.setSaso(" "); } else { // intensity cejpt.setFaso(" "); cejpt.setSaso("+"); } cejpt.setJsyn(pfjsyn[cixpred]); cejpt.setIpred(cixpred); efexasg[ixexp] = defexasg; // 1st one -> default EXASG } else { // add one if (freq) { // frequency efejsynpt[ixexp].add(new ExpJsynPoint("+", " ", pfjsyn[cixpred], cixpred, "")); } else { // intensity efejsynpt[ixexp].add(new ExpJsynPoint(" ", "+", pfjsyn[cixpred], cixpred, "")); } sortEfejsynpt(ixexp); // sort -> easier to read in TextArea } } } setAsog(ixexp); // set global frequency and intensity marks myrepaint(ip_X(efx[ixexp], p_cxmi, p_cxma) - p_ovray, p_epymi, 2 * p_ovray, p_yatma - p_epymi + 1); // exp marks // area endSelPred(); // end pred selection updateExp(jobplay.getExpFileName()); } /** * End pred selection. */ public void endSelPred() { if (predsel) { // pred selection running predsel = false; // No pred selection running nbixpred = 0; // NO pred selected jfsp.setContent(nbixpred, ixdebpred, ixpreds, ixexp); // set pred selection window content setLocSim(false); // NO loc-sim drawPredSel(); // draw pred selection area setJta(); // update TextArea } } /** * Draw pred selection area */ public void drawPredSel() { myrepaint(ip_X(predselx0, p_cxmi, p_cxma) - p_ovray, p_yprmi, ip_X(predselx1, p_cxmi, p_cxma) - ip_X(predselx0, p_cxmi, p_cxma) + p_ovwidth + 1, p_yprma - p_yprmi + 1); // pred selection area } /** * set current selectable pred. */ public void setPredable(int cixpredable) { ixpredable = cixpredable; drawPredSel(); } /** * Set local simulation spectrum. */ public void setLocSim(boolean clocsim) { boolean prevlocsim = locsim; // previous locsim int lslen = 0; // nb of loc-sim points int p_prevx0 = 0; // pixel starting point of previous loc-sim int p_prevx1 = 0; // pixel ending point of previous loc-sim if (prevlocsim) { // set previous loc-sim limits p_prevx0 = ip_X(lsx[0], p_cxmi, p_cxma); p_prevx1 = ip_X(lsx[lsx.length - 1], p_cxmi, p_cxma); } locsim = clocsim; if (locsim) { // get points and show them lsx = jfsp.getLsx(); lsy = jfsp.getLsy(); lslen = lsx.length; if (lslen < 2) { // not enough points locsim = false; } else { // rescale to fit spectrum for (int i = 0; i < lslen; i++) { lsy[i] = lsmis + (lsy[i] - lsmi) * (lsmas - lsmis) / (lsma - lsmi); } } } // define limited draw area if (prevlocsim) { if (locsim) { p_paintx = Math.min(p_prevx0, ip_X(lsx[0], p_cxmi, p_cxma)); p_paintwidth = Math.max(p_prevx1, ip_X(lsx[lslen - 1], p_cxmi, p_cxma)) - p_paintx + 1; } else { p_paintx = p_prevx0; p_paintwidth = p_prevx1 - p_paintx + 1; } } else { if (locsim) { p_paintx = ip_X(lsx[0], p_cxmi, p_cxma); p_paintwidth = ip_X(lsx[lslen - 1], p_cxmi, p_cxma) - p_paintx + 1; } else { return; } } myrepaint(p_paintx, p_cymi, p_paintwidth, p_cheight); // limited along X direction } /** * Set loc-sim spectrum Ymin and Ymax. */ public void setLsScale() { lsy = jfsp.getLsy(); // loc-sim Y // nb of points lsmi = lsy[0]; lsma = lsy[0]; for (double v : lsy) { if (v < lsmi) { lsmi = v; } if (v > lsma) { lsma = v; } } } // Set reduced spectra for speeding draw private void setRedSpect() { if (nbdf == 0) { // no data file return; } int[] ci_red; // index of reduced data int ind0; // running index : original (non-reduced) data int ip_0; // pixel position of running original point int ind_red; // running index : reduced data int ciymi; int ciyma; double cymi; // Y min of current pixel area double cyma; // Y max of current pixel area double cy; // running y int nbred; // original nb of data points for one pixel width area for (int j = 0; j < nbdf; j++) { // for each data file if (ntfile[j].equals("spectrum")) { // spectrum style use_red[j] = false; // status int nbpix = ip_X(x[j][nbxy[j] - 1], p_cxmi, p_cxma) - ip_X(x[j][0], p_cxmi, p_cxma) + 1; // visible area full nb of pixel of the spectrum (in actual scale) nbpix = Math.max(1, nbpix); nbred = nbxy[j] / nbpix; // nb of points/pixel // the reduced spectrum has up to 4 points/pixel // 1st = 1st original point with this pixel value // 2nd = original point with this pixel value and Y min value // 3rd = original point with this pixel value and Y max value // 4th = last original point with this pixel value // if 2nd and/or 3rd are yet 1st or 4th, they are not duplicated (usefull if nbred near 4) // they all have the same X value : mean frequency of these 1st and last original points if (nbred > 4) { // do we have to reduce ? // reduction use_red[j] = true; ci_red = new int[nbxy[j]]; // define array ind0 = 0; // index of 1st original point of 1st pixel area ip_0 = ip_X(x[j][ind0], p_cxmi, p_cxma); // pixel value of this pixel area ciymi = ind0; cymi = y[j][ciymi]; // Y min of this pixel area ciyma = ciymi; cyma = y[j][ciyma]; // Y max of this pixel area ind_red = 0; // index of 1st reduced point for (int i = 0; i < nbxy[j]; i++) { // for each original point if (ip_X(x[j][i], p_cxmi, p_cxma) != ip_0 || // new pixel area i == nbxy[j] - 1) { // end of the original data // 1st point ci_red[ind_red] = ind0; // frequency of this pixel area ind_red++; // next reduced point index // MIN point if not the same as 1st and/or last one if (cymi != y[j][ind0] && // NOT the same as the 1st one cymi != y[j][i - 1]) { // NOT the same as the last one ci_red[ind_red] = ciymi; // frequency of this pixel area ind_red++; // next reduced point index } // MAX point if not the same as 1st and/or last one if (cyma != y[j][ind0] && // NOT the same as the 1st one cyma != y[j][i - 1]) { // NOT the same as the last one ci_red[ind_red] = ciyma; // frequency of this pixel area ind_red++; // next reduced point index } // last point ci_red[ind_red] = i - 1; // frequency of this pixel area ind_red++; // next reduced point index // ind0 = i; // index of the 1st original point of the next pixel area ip_0 = ip_X(x[j][ind0], p_cxmi, p_cxma); // pixel position of this point ciymi = ind0; cymi = y[j][ciymi]; // Y min of this pixel area ciyma = ciymi; cyma = y[j][ciyma]; // Y max of this pixel area } else { // end of the pixel area cy = y[j][i]; // current Y if (cy < cymi) { // test min ciymi = i; cymi = cy; } if (cy > cyma) { // test max ciyma = i; cyma = cy; } } } nbxy_red[j] = ind_red; // effective nb of reduced points i_red[j] = new int[nbxy_red[j]]; System.arraycopy(ci_red, 0, i_red[j], 0, nbxy_red[j]); defxlim_red(j); // set 1st and last points in data area } } } } private void setUnsave() { if (this.jobplay.getProject() != null) { this.jobplay.getProject().setUnsave(true); } } } spview.iml000644 001750 001750 00000001374 14046454633 013422 0ustar00cyrilcyril000000 000000 resources/pixmaps/play.png000644 001750 001750 00000002436 14000365526 016540 0ustar00cyrilcyril000000 000000 PNG  IHDR szzIDATXWMoTU~s?f:ҺnHBM\c4lI XH ;$6YIv3˝̤ss1Rj,ɣ$|$H\h6#E伈@xKɋɫ$l4v/NȼR-DEpιJŵ_).)Dk O+ϲ,ݎH\OFZnŰGPJgp\5ݮwa~|( _ ^$H+w!ISLn$ M18i9bܺynw|C+tֆ&w0p6mzٳgCWG؀1ל&$OϜA^9wFH$WI+d\+'h4 "D6 yIVONNӧ(#\.ZDNw12JJ(Vվ-&&&~'&7 8 dۭXD1`:(8^ZwsiEe{E"_~R09N ha(ZZ\Vj@۷J)u[7dY$InsKdY4Mjt -[\Zhvw|eّ,Pn1# cСCιS9 i)OKKK{:W̒< x."uBVwT;WQIENDB`resources/PanAff/jmisaso.html000644 001750 001750 00000000165 14000365526 017067 0ustar00cyrilcyril000000 000000

Assignment

Set intensity mark.

LICENSE.md000644 001750 001750 00000104361 14070613233 012774 0ustar00cyrilcyril000000 000000 The GNU General Public License, Version 3, 29 June 2007 (GPLv3) =============================================================== > Copyright © 2007 > Free Software Foundation, Inc. > <> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble -------- The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS -------------------- ### 0. Definitions. "This License refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. ### 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. ### 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. ### 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. ### 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: * **a)** The work must carry prominent notices stating that you modified it, and giving a relevant date. * **b)** The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". * **c)** You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. * **d)** If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. ### 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: * **a)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. * **b)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. * **c)** Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. * **d)** Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. * **e)** Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non- consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. ### 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: * **a)** Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or * **b)** Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or * **c)** Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or * **d)** Limiting the use for publicity purposes of names of licensors or authors of the material; or * **e)** Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or * **f)** Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. ### 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. ### 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. ### 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. ### 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non- exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. ### 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. ### 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. ### 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. ### 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ### 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs --------------------------------------------- If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. The hypothetical commands 'show w' and 'show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <>.resources/JobPlay/jmiloadspect.html000644 001750 001750 00000000237 14000365526 020305 0ustar00cyrilcyril000000 000000

Load spectrum file

Load an XY spectrum file to plot it in the data area.

resources/PanAff/jmiunass.html000644 001750 001750 00000000251 14000365526 017247 0ustar00cyrilcyril000000 000000

Assignment

Deassociate one of the predictions from the selected experimental peak.

resources/JobPlay/jmiprint.html000644 001750 001750 00000000174 14000365526 017463 0ustar00cyrilcyril000000 000000

Print

Print data displayed at screen.

sources/org/spview/point/StickPoint.java000644 001750 001750 00000002730 14205637707 021253 0ustar00cyrilcyril000000 000000 package org.spview.point; /** * This class allows sorting stick file data. */ public class StickPoint implements Comparable { private final double x; // frequency private final double y; // intensity /** * Construct a new StickPoint. * * @param cx frequency * @param cy intensity */ public StickPoint(double cx, double cy) { x = cx; // frequency y = cy; // intensity } ///////////////////////////////////////////////////////////////////// /** * Get frequency. */ public double getX() { return x; } /** * Get intensity. */ public double getY() { return y; } /** * Compare frequencies only. * * @param csp the StickPoint to compare to */ public int compareTo(Object csp) { if( ! (csp instanceof StickPoint) ) { // not the right object throw new ClassCastException(); } double delta = ((StickPoint)csp).getX() - x; // compare frequencies if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } return 0; // equal } } resources/JobPlay/jmireload.html000644 001750 001750 00000000161 14000365526 017571 0ustar00cyrilcyril000000 000000

Reload

Reload a data file.

resources/JFSelPred/jmipredall.html000644 001750 001750 00000000177 14000365526 020173 0ustar00cyrilcyril000000 000000

Select

Select all predictions in a list.

resources/PanAff/jmiexp.html000644 001750 001750 00000000216 14000365526 016713 0ustar00cyrilcyril000000 000000

Assignment

Select an experimental peak in a popup list.

sources/org/spview/gui/JobPlay.java000644 001750 001750 00000145611 14205637234 020160 0ustar00cyrilcyril000000 000000 package org.spview.gui; /* * Class to play with SPVIEW */ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Cursor; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowAdapter; import java.awt.print.PageFormat; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Objects; import java.util.prefs.Preferences; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.WindowConstants; import javax.swing.filechooser.FileNameExtensionFilter; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.SAXException; import java.awt.Toolkit; import org.spview.filehandler.ExpFile; import org.spview.filehandler.DataFile; import org.spview.filehandler.PredFile; import org.spview.filehandler.SPVProject; import org.spview.residuals.ObsCalc; ///////////////////////////////////////////////////////////////////// /** * This is the master window. */ public class JobPlay extends JFrame implements ActionListener, Runnable { /** * */ private static final long serialVersionUID = -8418036777163263772L; private SPVProject project = null; private final String workd; // working directory private File lastProjectPath; // last Project path private String threadType; // dataread, expread, predread or print RecentItems openRecent; // menus private JMenuItem jmiexit; // Exit of FILE menu // private JMenuItem jmiloadspect; // Load spectrum of DATA menu private JMenuItem jmiloadstick; // Load stick of DATA menu private JMenuItem jmiloadpeakas; // Load peak/experiment as stick of DATA menu private JMenuItem jmiloadtdsas; // Load pred-as-stick (TDS) of DATA menu private JMenuItem jmiloadhitras; // Load pred-as-stick (HITRAN) of DATA menu private JMenu jmiopenrecents; // private JMenu jmdataman; // DataManagement private JMenuItem jmiresetall; // Restore all in basic state of DATAMANAGEMENT menu private final JMenu[] jmndataf; // data file names private final JMenuItem[] jmishow; // Show of DATAMANAGEMENT menu private final JMenuItem[] jmihide; // Hide of DATAMANAGEMENT menu private final JMenuItem[] jmireload; // Reload of DATAMANAGEMENT menu private final JMenuItem[] jmixshift; // X shift of DATAMANAGEMENT menu private final JMenuItem[] jmixunshift; // X unshift of DATAMANAGEMENT menu private final JMenuItem[] jmiyshift; // Y shift of DATAMANAGEMENT menu private final JMenuItem[] jmiyunshift; // Y unshift of DATAMANAGEMENT menu private final JMenuItem[] jmiyreverse; // Y reverse of DATAMANAGEMENT menu // private JMenu jmcreatepeakf; // Create peak file of ASSIGNMENT menu private JMenuItem jmicpfnone; // no default spectrum file name private final JMenuItem[] jmicpf; // data file names private JMenuItem jmiloadexpf; // Load experiment file of ASSIGNMENT menu private JMenu jmloadpredf; // Load prediction file of ASSIGNMENT menu private JMenuItem jmilpTDS; // Load prediction file (TDS) of ASSIGNMENT menu private JMenuItem jmilpHITRAN; // Load prediction file (HITRAN) of ASSIGNMENT menu private JMenu jmexp2data; // Associate experiment to data of ASSIGNMENT menu private JMenuItem jmiexp2null; // Associate experiment to null of ASSIGNMENT menu private final JMenuItem[] jmiexp2; // data file names private JMenu jmpred2data; // Associate prediction to data of ASSIGNMENT menu private JMenuItem jmipred2null; // Associate prediction to null of ASSIGNMENT menu private final JMenuItem[] jmipred2; // data file names private JMenuItem jmisavexp; // Update experiment file of ASSIGNMENT menu // private JMenuItem jmiprint; // Print of PLOT menu // private JMenuItem jmiabout; // About SPVIEW of HELP menu private final JPanel pccc; private final JButton jbyplus; // Y + private final JButton jbyegal; // Y = private final JButton jbymoins; // Y - private final JButton jbxpipeinf; // X |< private final JButton jbx2inf; // X << private final JButton jbx1inf; // X < private final JButton jbxegal; // X = private final JButton jbx1sup; // X > private final JButton jbx2sup; // X >> private final JButton jbxpipesup; // X >| private final JTextArea jta; // show selected exp/pred // labels to show mouse position private final JLabel jlx; // X mouse private final JLabel jly; // Y mouse private final JLabel jlypredas; // Y mouse in pred-as-stick scale private JLabel cjl; // value // drawing window private PanAff panaff; private final Color[] coul = { Color.BLUE, Color.RED, Color.GRAY, Color.GREEN, Color.MAGENTA, // colors choice Color.ORANGE, Color.BLACK, Color.PINK, Color.CYAN, Color.YELLOW }; private final float coulength; // size of color array private File lastUsedPath; // data files private DataFile[] dataf; // data file private String[] ndataf; // data file name private int nbdf; // nb of data files private final int mxnbdf = 20; // max nb of data files private int relfi; // reloadable file index // experiment file private ExpFile expf; // experiment file private String nexpf; // name of experiment file // prediction file private PredFile predf; // prediction file private String npredf; // name of prediction file private final String lnsep; // line separator private JMenuItem jmiopenproject; private JMenuItem jmisaveasproject; private JMenuItem jmisaveproject; private String spvFileName = null; // path file name private final ArrayList pathListToSave = new ArrayList<>(); // List of paths of the project to save private final ArrayList typeOfFileToSave = new ArrayList<>(); // Contains all attribute-types of DataFiles in order to save private final ArrayList AttributeOfFileToSave = new ArrayList<>(); // Contains attribute "dataread" to // differentiate them from predread or expread // Prediction files private String typeOfPredFile; // Contains the type of the PrefFile loaded private String attributeOfPredFile; // Contains "predread", if one has been loaded private String pathOfPredfileToSave; // Contains the path of the prediction file loaded private int PredfileToSave = 0; // Indicates if there is a prediction file to save or not // Experimental files private String typeOfExpFile; // Contains the type of the ExpFile loaded private String attributeOfExpFile; // Contains "expread", if one has been loaded private String pathOfExpfileToSave; // Contains the path of the experimental file loaded private int ExpfileToSave = 0; // Indicates if there is an experimental file to save or not // Y reverse private final ArrayList yReverseElementToSave = new ArrayList<>(); // Contains information about "y-reverse" state // hide private final ArrayList hideElementToSave = new ArrayList<>(); private JMenuItem jmixobscalc; private JMenuItem jmiyobscalc; private JMenuItem jmilpreload; private final JLabel expFileLabel; private JMenuItem jmicloseproject; private JSeparator datamanSeparator; ///////////////////////////////////////////////////////////////////// /** * Construct a new JobPlay. */ public JobPlay() { super("Managing SPVIEW"); // main window setIconImage(Toolkit.getDefaultToolkit().getImage(JobPlay.class.getResource("/pixmaps/spview.png"))); setName("JobPlay"); // for help files setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { // clean end public void windowClosing(WindowEvent e) { closeApplication(); } }); new JavaAwtDesktop(); workd = System.getProperty("user.dir"); // working directory lnsep = System.getProperty("line.separator"); // Monospaced 14 font Font mono14 = new Font("Monospaced", Font.PLAIN, 14); // menu bar buildMenu(); // build menu bar getContentPane().setLayout(new BorderLayout()); // center panel for drawing // panels JPanel pcentre = new JPanel(new BorderLayout()); JPanel pco = new JPanel(new BorderLayout()); JPanel pcc = new JPanel(new BorderLayout()); JPanel pccs = new JPanel(new GridLayout(2, 0, 5, 5)); pccc = new JPanel(new BorderLayout()); pcc.add(pccc, "Center"); pcc.add(pccs, "South"); pcentre.add(pco, "West"); pcentre.add(pcc, "Center"); // Y buttons // boxes and buttons // box for Y buttons Box boxy = Box.createVerticalBox(); jbyplus = new JButton(""); jbyplus.setContentAreaFilled(false); jbyplus.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbyplus.setBorder(null); jbyplus.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/up.png")))); jbyplus.setFont(new Font("Monospaced", Font.BOLD, 12)); jbyplus.setToolTipText("Shifts drawing one interval down along Y axis"); jbyplus.addActionListener(this); jbyegal = new JButton(""); jbyegal.setContentAreaFilled(false); jbyegal.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbyegal.setBorder(null); jbyegal.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/equal.png")))); jbyegal.setFont(new Font("Monospaced", Font.BOLD, 12)); jbyegal.setToolTipText("Centers drawing along Y axis"); jbyegal.addActionListener(this); jbymoins = new JButton(""); jbymoins.setContentAreaFilled(false); jbymoins.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbymoins.setBorder(null); jbymoins.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/bottom.png")))); jbymoins.setFont(new Font("Monospaced", Font.BOLD, 12)); jbymoins.setToolTipText("Shifts drawing one interval up along Y axis"); jbymoins.addActionListener(this); boxy.add(Box.createVerticalGlue()); boxy.add(jbyplus); boxy.add(Box.createVerticalStrut(50)); boxy.add(jbyegal); boxy.add(Box.createVerticalStrut(50)); boxy.add(jbymoins); boxy.add(Box.createVerticalGlue()); pco.add(boxy); // mouse position jlx = new JLabel(); jlx.setFont(new Font("Monospaced", Font.PLAIN, 14)); jly = new JLabel(); jly.setFont(new Font("Monospaced", Font.PLAIN, 14)); jlypredas = new JLabel(); jlypredas.setFont(new Font("Monospaced", Font.PLAIN, 14)); JPanel pcs = new JPanel(new GridLayout(0, 7, 5, 5)); pcs.setBackground(Color.WHITE); cjl = new JLabel("x =", null, JLabel.RIGHT); cjl.setFont(new Font("Monospaced", Font.PLAIN, 14)); pcs.add(cjl); pcs.add(jlx); cjl = new JLabel("y =", null, JLabel.RIGHT); cjl.setFont(new Font("Monospaced", Font.PLAIN, 14)); pcs.add(cjl); pcs.add(jly); cjl = new JLabel("", null, JLabel.RIGHT); cjl.setFont(new Font("Monospaced", Font.PLAIN, 14)); pcs.add(cjl); pcs.add(jlypredas); pcs.setBorder(BorderFactory.createLineBorder(Color.BLACK)); pccs.add(pcs); // X buttons // box for X buttons Box boxx = Box.createHorizontalBox(); jbxpipeinf = new JButton(""); jbxpipeinf.setContentAreaFilled(false); jbxpipeinf.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbxpipeinf.setBorder(null); jbxpipeinf.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/beg.png")))); jbxpipeinf.setToolTipText("Shifts drawing at the beginning of the X axis"); jbxpipeinf.addActionListener(this); jbx2inf = new JButton(""); jbx2inf.setContentAreaFilled(false); jbx2inf.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbx2inf.setBorder(null); jbx2inf.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/backward.png")))); jbx2inf.setToolTipText("Shifts drawing one screen right along X axis"); jbx2inf.addActionListener(this); jbx1inf = new JButton(""); jbx1inf.setContentAreaFilled(false); jbx1inf.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbx1inf.setBorder(null); jbx1inf.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/playback.png")))); jbx1inf.setToolTipText("Shifts drawing one interval right along X axis"); jbx1inf.addActionListener(this); jbxegal = new JButton(""); jbxegal.setContentAreaFilled(false); jbxegal.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbxegal.setBorder(null); jbxegal.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/pause.png")))); jbxegal.setToolTipText("Centers drawing along X axis"); jbxegal.addActionListener(this); jbx1sup = new JButton(""); jbx1sup.setContentAreaFilled(false); jbx1sup.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbx1sup.setBorder(null); jbx1sup.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/play.png")))); jbx1sup.setToolTipText("Shifts drawing one interval left along X axis"); jbx1sup.addActionListener(this); jbx2sup = new JButton(""); jbx2sup.setContentAreaFilled(false); jbx2sup.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbx2sup.setBorder(null); jbx2sup.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/forward.png")))); jbx2sup.setToolTipText("Shifts drawing one screen left along X axis"); jbx2sup.addActionListener(this); jbxpipesup = new JButton(""); jbxpipesup.setContentAreaFilled(false); jbxpipesup.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); jbxpipesup.setBorder(null); jbxpipesup.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/end.png")))); jbxpipesup.setToolTipText("Shifts drawing at the end of the X axis"); jbxpipesup.addActionListener(this); boxx.add(Box.createHorizontalGlue()); boxx.add(jbxpipeinf); boxx.add(Box.createHorizontalStrut(25)); boxx.add(jbx2inf); boxx.add(Box.createHorizontalStrut(25)); boxx.add(jbx1inf); boxx.add(Box.createHorizontalStrut(25)); boxx.add(jbxegal); boxx.add(Box.createHorizontalStrut(25)); boxx.add(jbx1sup); boxx.add(Box.createHorizontalStrut(25)); boxx.add(jbx2sup); boxx.add(Box.createHorizontalStrut(25)); boxx.add(jbxpipesup); boxx.add(Box.createHorizontalStrut(25)); boxx.add(Box.createHorizontalGlue()); pccs.add(boxx); // TextArea for selected exp/pred characteristics jta = new JTextArea(); panaff = new PanAff(this, mxnbdf, coul, jta, jlx, jly, jlypredas); expFileLabel = new JLabel("", null, SwingConstants.TRAILING); expFileLabel.setFont(new Font("Monospaced", Font.PLAIN, 14)); pcs.add(expFileLabel); panaff.setBackground(Color.WHITE); pccc.add(panaff, "Center"); pccc.setBorder(BorderFactory.createLineBorder(Color.BLACK)); // south panel contains TextArea JPanel psud = new JPanel(new GridLayout(1, 0, 5, 5)); jta.setBackground(Color.BLACK); jta.setForeground(Color.WHITE); jta.setEditable(false); // not editable jta.setFont(mono14); jta.setLineWrap(false); // no line wrap jta.setRows(8); // nb of lines // with lifts JScrollPane jsp = new JScrollPane(jta); // with lifts psud.add(jsp); // insert panels getContentPane().add(pcentre, "Center"); getContentPane().add(psud, "South"); pack(); // initialisation dataf = new DataFile[mxnbdf]; // data file ndataf = new String[mxnbdf]; // name of data file nbdf = 0; // nb of data files jmndataf = new JMenu[mxnbdf]; // data file menus jmishow = new JMenuItem[mxnbdf]; // show jmihide = new JMenuItem[mxnbdf]; // hide jmireload = new JMenuItem[mxnbdf]; // reload jmixshift = new JMenuItem[mxnbdf]; // X shift jmixunshift = new JMenuItem[mxnbdf]; // X unshift jmiyshift = new JMenuItem[mxnbdf]; // Y shift jmiyunshift = new JMenuItem[mxnbdf]; // Y unshift jmiyreverse = new JMenuItem[mxnbdf]; // Y reverse jmicpf = new JMenuItem[mxnbdf]; // data file names jmiexp2 = new JMenuItem[mxnbdf]; // exp associated to data jmipred2 = new JMenuItem[mxnbdf]; // pred associated to data coulength = coul.length; // size of color array } ///////////////////////////////////////////////////////////////////// private void updateOpenRecentMenu(RecentItems recentItems) { jmiopenrecents.removeAll(); for (int i = 0; i < recentItems.size(); i++) { String item = recentItems.get(i); JMenuItem recentItem = new JMenuItem(item); recentItem.addActionListener(e -> loadProjectFileName(item)); jmiopenrecents.add(recentItem); } } // build the menu bar private void buildMenu() { // menu bar JMenuBar jmb = new JMenuBar(); // FILE JMenu jmfile = new JMenu("File"); // DATA JMenu jmload = new JMenu("Data"); jmiloadspect = new JMenuItem("Load spectrum file"); jmiloadspect.addActionListener(this); jmiloadstick = new JMenuItem("Load stick file"); jmiloadstick.addActionListener(this); jmiloadpeakas = new JMenuItem("Load (as stick) peak/experiment file"); jmiloadpeakas.addActionListener(this); // Load pred-as-stick of DATA menu JMenu jmloadpredas = new JMenu("Load (as stick) prediction file"); jmiloadtdsas = new JMenuItem("TDS format"); jmiloadtdsas.setEnabled(false); jmiloadtdsas.addActionListener(this); jmloadpredas.add(jmiloadtdsas); jmiloadhitras = new JMenuItem("HITRAN format"); jmiloadhitras.setEnabled(false); jmiloadhitras.addActionListener(this); jmloadpredas.add(jmiloadhitras); jmload.add(jmiloadspect); jmload.addSeparator(); jmload.add(jmiloadstick); jmload.add(jmiloadpeakas); jmload.addSeparator(); jmload.add(jmloadpredas); // DATAMANAGEMENT jmdataman = new JMenu("DataManagement"); jmiresetall = new JMenuItem("Restore all in basic state"); jmiresetall.addActionListener(this); jmdataman.add(jmiresetall); // ASSIGNMENT JMenu jmassign = new JMenu("Assignment"); jmcreatepeakf = new JMenu("Create peak file from spectrum file"); jmicpfnone = new JMenuItem("Unknown default spectrum file name"); jmicpfnone.addActionListener(this); jmcreatepeakf.add(jmicpfnone); jmiloadexpf = new JMenuItem("Load peak/experiment file"); jmiloadexpf.setEnabled(false); jmiloadexpf.addActionListener(this); jmloadpredf = new JMenu("Load prediction file"); jmloadpredf.setEnabled(false); jmilpTDS = new JMenuItem("TDS format"); jmilpTDS.addActionListener(this); jmloadpredf.add(jmilpTDS); jmilpHITRAN = new JMenuItem("HITRAN format"); jmilpHITRAN.addActionListener(this); jmloadpredf.add(jmilpHITRAN); jmexp2data = new JMenu("Associate experiment to data"); jmexp2data.setEnabled(false); jmiexp2null = new JMenuItem("null"); jmiexp2null.addActionListener(this); jmexp2data.add(jmiexp2null); jmpred2data = new JMenu("Associate prediction to data"); jmpred2data.setEnabled(false); jmipred2null = new JMenuItem("null"); jmipred2null.addActionListener(this); jmpred2data.add(jmipred2null); jmisavexp = new JMenuItem("Save assignment file as..."); jmisavexp.setEnabled(false); jmisavexp.addActionListener(this); jmassign.add(jmcreatepeakf); jmassign.addSeparator(); jmassign.add(jmiloadexpf); jmassign.add(jmloadpredf); jmilpreload = new JMenuItem("Reload"); jmilpreload.addActionListener(this); jmloadpredf.addSeparator(); jmloadpredf.add(jmilpreload); jmassign.addSeparator(); jmassign.add(jmexp2data); jmassign.add(jmpred2data); jmassign.addSeparator(); jmassign.add(jmisavexp); // PLOT JMenu jmplot = new JMenu("Plot"); jmiprint = new JMenuItem("Print"); jmiprint.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); jmiprint.addActionListener(this); jmixobscalc = new JMenuItem("X Obs.-Calc."); jmplot.add(jmixobscalc); jmixobscalc.addActionListener(this); jmiyobscalc = new JMenuItem("Y Obs.-Calc."); jmplot.add(jmiyobscalc); jmiyobscalc.addActionListener(this); jmplot.addSeparator(); jmplot.add(jmiprint); // HELP JMenu jmhelp = new JMenu("Help"); jmiabout = new JMenuItem("About SPVIEW"); jmiabout.addActionListener(this); jmhelp.add(jmiabout); if (System.getProperty("os.name").contains("OS X")){ jmiabout.setVisible(false); } jmb.add(jmfile); // FILE jmiopenproject = new JMenuItem("Open Project..."); jmiopenproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); jmiopenproject.addActionListener(this); jmfile.add(jmiopenproject); jmiopenrecents = new JMenu("Open Recent"); Preferences prefs = Preferences.userRoot().node(JobPlay.class.getSimpleName()); int maxrecent = 10; openRecent = new RecentItems(maxrecent, prefs); updateOpenRecentMenu(openRecent); jmfile.add(jmiopenrecents); jmfile.addSeparator(); jmicloseproject = new JMenuItem("Close Project"); jmicloseproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); jmicloseproject.addActionListener(this); jmfile.add(jmicloseproject); jmfile.addSeparator(); jmisaveasproject = new JMenuItem("Save Project As..."); jmisaveasproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | java.awt.event.InputEvent.SHIFT_DOWN_MASK)); jmisaveasproject.addActionListener(this); jmfile.add(jmisaveasproject); jmisaveproject = new JMenuItem("Save"); jmisaveproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); jmisaveproject.addActionListener(this); jmfile.add(jmisaveproject); jmfile.addSeparator(); jmiexit = new JMenuItem("Exit"); jmiexit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); jmiexit.addActionListener(this); jmfile.add(jmiexit); if (System.getProperty("os.name").contains("OS X")){ jmiexit.setVisible(false); } jmb.add(jmload); jmb.add(jmdataman); jmb.add(jmassign); jmb.add(jmplot); jmb.add(jmhelp); setJMenuBar(jmb); // set menu bar } private boolean checkForSave() { boolean fileExist = true; if (nbdf == 0) return true; if (project == null) { project = new SPVProject(this); spvFileName = workd; project.setUnsave(true); fileExist = false; } if (project.isUnsave()) { int n = JOptionPane.showConfirmDialog(null, "The current project is in a unsaved state, Do you want to save it?", "Project file is unsaved", JOptionPane.YES_NO_CANCEL_OPTION); if (n == JOptionPane.YES_OPTION) { project.saveSPVFile(spvFileName, fileExist); } else return n != JOptionPane.CANCEL_OPTION; } return true; } ///////////////////////////////////////////////////////////////////// /** * Process events. */ public void actionPerformed(ActionEvent evt) { // FILE // -> Exit if (evt.getSource() == jmiexit) { closeApplication(); return; } // NO action while printing if (panaff.getPrinting()) { JOptionPane.showMessageDialog(null, "No action while printing"); return; } // BUTTONS // Y + if (evt.getSource() == jbyplus) { panaff.jbyplus(); setUnsave(true); return; } // Y = if (evt.getSource() == jbyegal) { panaff.jbyegal(); setUnsave(true); return; } // Y - if (evt.getSource() == jbymoins) { panaff.jbymoins(); setUnsave(true); return; } // X |< if (evt.getSource() == jbxpipeinf) { panaff.jbxpipeinf(); setUnsave(true); return; } // X << if (evt.getSource() == jbx2inf) { panaff.jbx2inf(); setUnsave(true); return; } // X < if (evt.getSource() == jbx1inf) { panaff.jbx1inf(); setUnsave(true); return; } // X = if (evt.getSource() == jbxegal) { panaff.jbxegal(); setUnsave(true); return; } // X > if (evt.getSource() == jbx1sup) { panaff.jbx1sup(); setUnsave(true); return; } // X >> if (evt.getSource() == jbx2sup) { panaff.jbx2sup(); setUnsave(true); return; } // X >| if (evt.getSource() == jbxpipesup) { panaff.jbxpipesup(); setUnsave(true); return; } // all following actions reset pred selection in PanAff panaff.endSelPred(); // PROJECT SPVIEW 2.0 // -> Load project files if (evt.getSource() == jmiopenproject) { loadProjectFile(); return; } // -> Close project if (evt.getSource() == jmicloseproject) { if (checkForSave()) { spvFileName = null; closeAll(); } return; } // -> Save project files if (evt.getSource() == jmisaveasproject) { if (spvFileName == null) { project = new SPVProject(this); spvFileName = workd; } project.saveSPVFile(spvFileName, false); return; } if (evt.getSource() == jmisaveproject) { if (spvFileName == null) { project = new SPVProject(this); spvFileName = workd; // if save is cancelled or failed, no new project is created if (!project.saveSPVFile(spvFileName, false)) { spvFileName = null; } } else { project.saveSPVFile(spvFileName, true); } return; } // DATA // -> Load spectrum file if (evt.getSource() == jmiloadspect) { loadDataFile("spectrum"); return; } // -> Load stick file if (evt.getSource() == jmiloadstick) { loadDataFile("stick"); return; } // -> Load peak/experiment file as stick if (evt.getSource() == jmiloadpeakas) { loadDataFile("peakas"); return; } // -> Load prediction file (TDS) as stick if (evt.getSource() == jmiloadtdsas) { loadDataFile("tdsas"); return; } // -> Load prediction file (HITRAN) as stick if (evt.getSource() == jmiloadhitras) { loadDataFile("hitras"); return; } // DATAMANAGEMENT // -> Reset All if (evt.getSource() == jmiresetall) { if (nbdf > 0) { panaff.resetAll(); // call panaff for (int i = 0; i < nbdf; i++) { resetDatafJMI(i); // reset jmi of this dataf (like show) } } setUnsave(true); return; } // -> data files if (nbdf > 0) { for (int i = 0; i < nbdf; i++) { // -> Show if (evt.getSource() == jmishow[i]) { panaff.setShow(i, true); // call panaff resetDatafJMI(i); // reset jmi of this dataf setHideState(i, false); setUnsave(true); return; } // -> Hide if (evt.getSource() == jmihide[i]) { jmishow[i].setEnabled(true); // disallow jmi of this dataf jmihide[i].setEnabled(false); if (dataf[i].getType().equals("spectrum")) { jmireload[i].setEnabled(false); } jmixshift[i].setEnabled(false); jmixunshift[i].setEnabled(false); jmiyshift[i].setEnabled(false); jmiyunshift[i].setEnabled(false); jmiyreverse[i].setEnabled(false); panaff.setShow(i, false); // call panaff setHideState(i, true); setUnsave(true); return; } // -> Reload if (evt.getSource() == jmireload[i]) { reloadDataFile(i); return; } // -> X shift if (evt.getSource() == jmixshift[i]) { panaff.xShift(i); // call panaff jmixunshift[i].setEnabled(true); setUnsave(true); return; } // -> X unshift if (evt.getSource() == jmixunshift[i]) { panaff.xUnshift(i); // call panaff jmixunshift[i].setEnabled(false); setUnsave(true); return; } // -> Y shift if (evt.getSource() == jmiyshift[i]) { panaff.yShift(i); // call panaff jmiyunshift[i].setEnabled(true); setUnsave(true); return; } // -> Y unshift if (evt.getSource() == jmiyunshift[i]) { panaff.yUnshift(i); // call panaff jmiyunshift[i].setEnabled(false); setUnsave(true); return; } // -> Y reverse if (evt.getSource() == jmiyreverse[i]) { panaff.yReverse(i); // call panaff setyReverseState(i); setUnsave(true); } } } // ASSIGNMENT // -> Create peak file from spectrum if (evt.getSource() == jmicpfnone) { // Unknown default spectrum file name JFCreatePeakFile jfcpf = new JFCreatePeakFile(null); // create the window jfcpf.setVisible(true); // show it return; } for (int i = 0; i < nbdf; i++) { // search in spectrum type data files if (evt.getSource() == jmicpf[i]) { // found JFCreatePeakFile jfcpf = new JFCreatePeakFile(dataf[i]); // create the window jfcpf.setVisible(true); // show it return; } } // -> Load peak/experiment file if (evt.getSource() == jmiloadexpf) { loadExpFile(); typeOfExpFile = "peakExp"; attributeOfExpFile = "expread"; return; } // -> Load prediction file (TDS) if (evt.getSource() == jmilpTDS) { loadPredFile("TDS"); typeOfPredFile = "TDS"; return; } // -> Load prediction file (HITRAN) if (evt.getSource() == jmilpHITRAN) { loadPredFile("HITRAN"); typeOfPredFile = "HITRAN"; return; } // -> Reload prediction file if (evt.getSource() == jmilpreload) { reloadPredFile(); return; } // -> Associate experiment to data if (evt.getSource() == jmiexp2null) { panaff.expAss2(-1); // unassociate setUnsave(true); return; } String str; for (int i = 0; i < nbdf; i++) { // for each datafile if (evt.getSource() == jmiexp2[i]) { // found str = panaff.expAss2(i); // associate if (str.length() != 0) { // show error message JOptionPane.showMessageDialog(null, str); } setUnsave(true); return; } } // -> Associate prediction to data if (evt.getSource() == jmipred2null) { panaff.predAss2(-1); // unassociate setUnsave(true); return; } for (int i = 0; i < nbdf; i++) { // for each data file if (evt.getSource() == jmipred2[i]) { // found str = panaff.predAss2(i); // associate if (str.length() != 0) { // show error message JOptionPane.showMessageDialog(null, str); } setUnsave(true); return; } } // -> Save experiment file if (evt.getSource() == jmisavexp) { panaff.saveExp(); // call panaff return; } // PLOT // -> Print if (evt.getSource() == jmiprint) { // thread panaff.prepThread("Printing File ..."); // wait message threadType = "print"; // define action of run Thread threadThis = new Thread(this); // define the thread (run of this instance) threadThis.start(); // start it return; } if (evt.getSource() == jmixobscalc) { try { ObsCalc obsCalc = new ObsCalc(expf, predf); obsCalc.show(this, "X"); } catch (Exception e) { JOptionPane.showMessageDialog(null, "You must first load prediction and peak/experiment files."); } } if (evt.getSource() == jmiyobscalc) { try { ObsCalc obsCalc = new ObsCalc(expf, predf); obsCalc.show(this, "Y"); } catch (Exception e) { JOptionPane.showMessageDialog(null, "You must first load prediction and peak/experiment files."); } } // HELP // -> About if (evt.getSource() == jmiabout) { About.show(); } } ///////////////////////////////////////////////////////////////////// // reset JMenuItem of a data file private void resetDatafJMI(int ci) { jmishow[ci].setEnabled(false); jmihide[ci].setEnabled(true); if (dataf[ci].getType().equals("spectrum")) { jmireload[ci].setEnabled(true); } jmixshift[ci].setEnabled(true); jmixunshift[ci].setEnabled(panaff.getXShift(ci) != 0.); jmiyshift[ci].setEnabled(true); // NO yshift jmiyunshift[ci].setEnabled(panaff.getYShift(ci) != 0.); jmiyreverse[ci].setEnabled(true); } private void closeAll() { if (nbdf == 0) return; dataf = new DataFile[mxnbdf]; ndataf = new String[mxnbdf]; pccc.remove(panaff); panaff = new PanAff(this, mxnbdf, coul, jta, jlx, jly, jlypredas); panaff.setBackground(Color.WHITE); pccc.add(panaff, "Center"); pccc.setBorder(BorderFactory.createLineBorder(Color.BLACK)); pccc.repaint(); project = null; pathListToSave.clear(); typeOfFileToSave.clear(); AttributeOfFileToSave.clear(); yReverseElementToSave.clear(); hideElementToSave.clear(); PredfileToSave = 0; ExpfileToSave = 0; expFileLabel.setText(""); predf = null; expf = null; jmdataman.remove(datamanSeparator); for (int i = 0; i < nbdf; i++) { jmdataman.remove(jmndataf[i]); jmcreatepeakf.remove(jmicpf[i]); jmexp2data.remove(jmiexp2[i]); jmpred2data.remove(jmipred2[i]); } jmiloadexpf.setEnabled(false); jmloadpredf.setEnabled(false); jmexp2data.setEnabled(false); jmpred2data.setEnabled(false); jmisavexp.setEnabled(false); nbdf = 0; } //////////////////////////////////////////////////////////// /** * Load files by file name * * @param cntfile file type * @since SPVIEW2 */ public void loadFileByFilename(String filename, String cntfile, int i) { // -> Show // instantiate a data file String attribute = project.getAttributeOfFileList().get(i); switch (attribute) { case "dataread": ndataf[i] = filename; dataf[i] = new DataFile(ndataf[i]); // full data file name dataf[i].setType(cntfile); // type // thread panaff.prepThread("Loading File ..."); // wait message nbdf = i; threadType = "dataread"; // define action of run() this.run(); break; case "predread": PredfileToSave = 1; pathOfPredfileToSave = npredf; // instantiate the prediction file npredf = filename; predf = new PredFile(npredf, cntfile); // full pred file name, type // Color associated panaff.setPfass2(project.getIndexColorPred()); // thread panaff.prepThread("Loading File ..."); // wait message threadType = "predread"; // define action of run() this.run(); break; case "expread": ExpfileToSave = 1; // instantiate the experimental file nexpf = filename; expf = new ExpFile(nexpf); // full exp file name pathOfExpfileToSave = nexpf; // Color associated panaff.setEfass2(project.getIndexColorExp()); // thread panaff.prepThread("Loading File ..."); // wait message threadType = "expread"; // define action of run() this.run(); break; } } //////////////////////////////////////////////////////////// /** * Get the yReverse-state of each file loaded in order to save this state * * @since SPVIEW2 */ private void setyReverseState(int j) { if (j < yReverseElementToSave.size()) { String reverse = yReverseElementToSave.get(j); yReverseElementToSave.set(j, reverse.equals("true") ? "false" : "true"); } else { yReverseElementToSave.add("true"); } } /** * Get the hide-state of each file loaded in order to save this state * * @since SPVIEW2 */ private void setHideState(int j, boolean hide) { if (j < hideElementToSave.size()) { String tag = hide ? "true" : "false"; hideElementToSave.set(j, tag); } else { hideElementToSave.add("false"); } } private void displayName(String filename) { File file = new File(filename); expFileLabel.setText(/*"Peak/experiment file: " + */file.getName()); } //////////////////////////////////////////////////////////// public void addInOpenRecentList(String projectPath) { openRecent.push(projectPath); updateOpenRecentMenu(openRecent); } public void removeInOpenRecentList(String projectPath) { openRecent.remove(projectPath); updateOpenRecentMenu(openRecent); } public void loadProjectFileName(String filename) { spvFileName = filename; panaff.prepThread("Loading Project File ..."); // wait message threadType = "project"; // define action of run() Thread threadThis = new Thread(this); // define thread (run of this instance) threadThis.start(); // start it } /** * Open a dialog box to save the files as a project * * @since SPVIEW2 */ private void loadProjectFile() { JFileChooser jfcfile = new JFileChooser(); if (lastProjectPath != null) { jfcfile.setCurrentDirectory(lastProjectPath); } jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only jfcfile.setDialogTitle("Open Project"); FileNameExtensionFilter filter = new FileNameExtensionFilter("Project file (*.spv)", "spv"); // Only files .spv // can be chosen jfcfile.addChoosableFileFilter(filter); // add the filter created above jfcfile.setAcceptAllFileFilterUsed(false); // All types of file are NOT accepted Container parent = getParent(); int choice = jfcfile.showOpenDialog(parent); if (choice == JFileChooser.APPROVE_OPTION) { String projectPath = jfcfile.getSelectedFile().getAbsolutePath(); loadProjectFileName(projectPath); lastProjectPath = jfcfile.getSelectedFile().getParentFile(); } } //////////////////////////////////////////////////////////// /** * Load a data file. * * @param cntfile file type */ private void loadDataFile(String cntfile) { // max nb of file reached ? if (nbdf == mxnbdf) { JOptionPane.showMessageDialog(null, "Max number of data files is reached (" + mxnbdf + ")"); return; } // choose the file JFileChooser jfcfile = new JFileChooser(); if (lastUsedPath != null) { jfcfile.setCurrentDirectory(lastUsedPath); } jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only jfcfile.setDialogTitle("Define the file to be visualized"); Container parent = getParent(); int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select if (choice == JFileChooser.APPROVE_OPTION) { // instantiate a data file lastUsedPath = jfcfile.getSelectedFile().getParentFile(); ndataf[nbdf] = jfcfile.getSelectedFile().getAbsolutePath(); dataf[nbdf] = new DataFile(ndataf[nbdf]); // full data file name dataf[nbdf].setType(cntfile); // type // thread panaff.prepThread("Loading File ..."); // wait message threadType = "dataread"; // define action of run() Thread threadThis = new Thread(this); // define thread (run of this instance) threadThis.start(); // start it } } //////////////////////////////////////////////////////////// /** * Reload a data file. * * @param ci file index */ private void reloadDataFile(int ci) { // thread relfi = ci; // reloadable file index panaff.prepThread("Reloading File ..."); // wait message threadType = "datareload"; // define action of run() Thread threadThis = new Thread(this); // define thread (run of this instance) threadThis.start(); // start it } //////////////////////////////////////////////////////////// /** * Reload a data file. * * */ public void reloadPredFile() { // thread panaff.prepThread("Reloading File ..."); // wait message threadType = "predreload"; // define action of run() Thread threadThis = new Thread(this); // define thread (run of this instance) threadThis.start(); // start it } //////////////////////////////////////////////////////////// /** * Load an experiment file. */ private void loadExpFile() { // a file is already loaded if (expf != null) { int n = JOptionPane.showConfirmDialog(null, "An experiment file is already loaded" + lnsep + "Would you like to overload a new experiment file ?" + lnsep, "New experiment file", JOptionPane.YES_NO_OPTION); if (n != JOptionPane.YES_OPTION) { // nothing to do return; } } // choose the file JFileChooser jfcfile = new JFileChooser(); if (lastUsedPath != null) { jfcfile.setCurrentDirectory(lastUsedPath); } jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only jfcfile.setDialogTitle("Define the experiment file to be loaded"); Container parent = getParent(); int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select if (choice == JFileChooser.APPROVE_OPTION) { // instantiate an exp file lastUsedPath = jfcfile.getSelectedFile().getParentFile(); nexpf = jfcfile.getSelectedFile().getAbsolutePath(); expf = new ExpFile(nexpf); // full exp file name // thread panaff.prepThread("Loading File ..."); // wait message threadType = "expread"; // define action of run() Thread threadThis = new Thread(this); // define thread (run of this instance) threadThis.start(); // start it } } /** * Load a prediction file. * * @param ctype file type */ private void loadPredFile(String ctype) { // a file is already loaded if (predf != null) { int n = JOptionPane.showConfirmDialog( null, "A prediction file is already loaded" + lnsep + "Would you like to overload a new prediction file ?" + lnsep, "New prediction file", JOptionPane.YES_NO_OPTION); if (n != JOptionPane.YES_OPTION) { // nothing to do return; } } // choose the file JFileChooser jfcfile = new JFileChooser(); if (lastUsedPath != null) { jfcfile.setCurrentDirectory(lastUsedPath); } jfcfile.setSize(400, 300); jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only jfcfile.setDialogTitle("Define the prediction file to be loaded"); Container parent = getParent(); int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select if (choice == JFileChooser.APPROVE_OPTION) { // instantiate a pred file npredf = jfcfile.getSelectedFile().getAbsolutePath(); lastUsedPath = jfcfile.getSelectedFile().getParentFile(); predf = new PredFile(npredf, ctype); // full pred file name, type // thread panaff.prepThread("Loading File ..."); // wait message threadType = "predread"; // define action of run() Thread threadThis = new Thread(this); // define thread (run of this instance) threadThis.start(); // start it } } //////////////////////////////////////////////////////////// /** * Thread launched by start.
* Processes read of data files
* or read of experiment file
* or read of prediction file
* or reload of data files
* or print of graphs. */ public void run() { // what to do switch (threadType) { case "dataread": // read data file if (dataf[nbdf].read()) { // read data file if (nbdf == 0) { jmiloadtdsas.setEnabled(true); jmiloadhitras.setEnabled(true); } typeOfFileToSave.add(dataf[nbdf].getType()); // show data if (dataf[nbdf].getType().equals("tdsas") || dataf[nbdf].getType().equals("hitras") || dataf[nbdf].getType().equals("pickettas")) { dataf[nbdf].setType("predas"); // generic type for pred-as-stick files jmndataf[nbdf] = new JMenu(ndataf[nbdf] + " (pred-as-stick)"); if (cjl.getText().trim().length() == 0) { // not already set : 1st pred-as-stick file cjl.setText("pred-as-stick y ="); // last x/y/pasy label for mouse position } pathListToSave.add(ndataf[nbdf]); // add the path of the file loaded AttributeOfFileToSave.add("dataread"); yReverseElementToSave.add("false"); // add the y-state : "false" by default hideElementToSave.add("false"); } else if (dataf[nbdf].getType().equals("peakas")) { jmndataf[nbdf] = new JMenu(ndataf[nbdf] + " (peak-as-stick)"); pathListToSave.add(ndataf[nbdf]); // add the path of the file loaded AttributeOfFileToSave.add("dataread"); yReverseElementToSave.add("false"); // add the y-state : "false" by default hideElementToSave.add("false"); } else { // spectrum/stick jmndataf[nbdf] = new JMenu(ndataf[nbdf] + " (" + dataf[nbdf].getType() + ")"); } jmndataf[nbdf].setBackground(Color.WHITE); jmndataf[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]); jmishow[nbdf] = new JMenuItem("Show"); jmishow[nbdf].setEnabled(false); jmishow[nbdf].addActionListener(this); jmndataf[nbdf].add(jmishow[nbdf]); jmihide[nbdf] = new JMenuItem("Hide"); jmihide[nbdf].addActionListener(this); jmndataf[nbdf].add(jmihide[nbdf]); if (dataf[nbdf].getType().equals("spectrum")) { jmireload[nbdf] = new JMenuItem("Reload"); jmireload[nbdf].addActionListener(this); jmndataf[nbdf].add(jmireload[nbdf]); } jmndataf[nbdf].addSeparator(); jmixshift[nbdf] = new JMenuItem("X shift"); jmixshift[nbdf].addActionListener(this); jmndataf[nbdf].add(jmixshift[nbdf]); jmixunshift[nbdf] = new JMenuItem("X unshift"); jmixunshift[nbdf].setEnabled(false); jmixunshift[nbdf].addActionListener(this); jmndataf[nbdf].add(jmixunshift[nbdf]); jmndataf[nbdf].addSeparator(); jmiyshift[nbdf] = new JMenuItem("Y shift"); jmiyshift[nbdf].addActionListener(this); jmndataf[nbdf].add(jmiyshift[nbdf]); jmiyunshift[nbdf] = new JMenuItem("Y unshift"); jmiyunshift[nbdf].setEnabled(false); jmiyunshift[nbdf].addActionListener(this); jmndataf[nbdf].add(jmiyunshift[nbdf]); jmndataf[nbdf].addSeparator(); jmiyreverse[nbdf] = new JMenuItem("Y reverse"); jmiyreverse[nbdf].addActionListener(this); jmndataf[nbdf].add(jmiyreverse[nbdf]); if (nbdf == 0) { datamanSeparator = new JSeparator(); jmdataman.add(datamanSeparator); } jmdataman.add(jmndataf[nbdf]); // if (dataf[nbdf].getType().equals("spectrum") || dataf[nbdf].getType().equals("stick")) { // only spectrum type data file for jmcreatpeakf jmicpf[nbdf] = new JMenuItem(ndataf[nbdf]); jmicpf[nbdf].setBackground(Color.WHITE); jmicpf[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]); jmicpf[nbdf].addActionListener(this); jmcreatepeakf.add(jmicpf[nbdf]); pathListToSave.add(ndataf[nbdf]); // add the path of the file loaded AttributeOfFileToSave.add("dataread"); yReverseElementToSave.add("false"); // add the y-state : "false" by default hideElementToSave.add("false"); } else { jmicpf[nbdf] = new JMenuItem(); // instantiated but not activated } // jmiexp2[nbdf] = new JMenuItem(ndataf[nbdf]); jmiexp2[nbdf].setBackground(Color.WHITE); jmiexp2[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]); jmiexp2[nbdf].addActionListener(this); jmexp2data.add(jmiexp2[nbdf]); if (expf != null) { jmexp2data.setEnabled(true); } jmipred2[nbdf] = new JMenuItem(ndataf[nbdf]); jmipred2[nbdf].setBackground(Color.WHITE); jmipred2[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]); jmipred2[nbdf].addActionListener(this); jmpred2data.add(jmipred2[nbdf]); if (predf != null) { jmpred2data.setEnabled(true); } panaff.addData(dataf[nbdf]); // call panaff jmiloadexpf.setEnabled(true); jmloadpredf.setEnabled(true); nbdf++; setUnsave(true); } else { // read error dataf[nbdf] = null; ndataf[nbdf] = null; } break; case "expread": // read exp file if (expf.read()) { // show exp panaff.setExp(expf); // call panaff if (nbdf != 0) { jmexp2data.setEnabled(true); } jmisavexp.setEnabled(true); typeOfExpFile = "peakExp"; pathOfExpfileToSave = nexpf; attributeOfExpFile = "expread"; ExpfileToSave = 1; displayName(nexpf); setUnsave(true); } else { // read error expf = null; } break; case "predread": // read pred file if (predf.read()) { // show pred panaff.setPred(predf); // call panaff if (nbdf != 0) { jmpred2data.setEnabled(true); pathOfPredfileToSave = npredf; attributeOfPredFile = "predread"; PredfileToSave = 1; } setUnsave(true); } else { // read error predf = null; } break; case "datareload": // reload data file if (dataf[relfi].read()) { // reload data file panaff.reloadData(dataf[relfi], relfi); } break; case "predreload": if (predf.read()) { /* update prediction file */ panaff.setPred(predf); /* update shifts */ panaff.reloadPredf(predf); } break; case "project": try { if ((project == null && nbdf > 0) || (project != null && project.isUnsave())) { int n = JOptionPane.showConfirmDialog(null, "The current project is in a unsaved state, Do you want to save it?", "Project file is unsaved", JOptionPane.YES_NO_CANCEL_OPTION); if (n == JOptionPane.YES_OPTION) { project.saveSPVFile(workd, false); } else if (n == JOptionPane.CANCEL_OPTION) { panaff.endThread(); return; } } closeAll(); project = new SPVProject(this, spvFileName); project.open(); project.setUnsave(false); } catch (ParserConfigurationException | SAXException | IOException e) { JOptionPane.showMessageDialog(null, "Cannot Parse File " + spvFileName, "Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } break; default: // print PrinterJob pjob = PrinterJob.getPrinterJob(); // define a printing job PageFormat pf = pjob.defaultPage(); // define page format pf.setOrientation(PageFormat.LANDSCAPE); pjob.setPrintable(panaff, pf); // associate to PanAff // if (pjob.printDialog(new HashPrintRequestAttributeSet())) { supprime // LANDSCAPE if (pjob.printDialog()) { // define printing try { pjob.print(); // print } catch (PrinterException pe) { JOptionPane.showMessageDialog(null, "Printer exception" + lnsep + pe); } } break; } panaff.endThread(); } private void closeApplication() { if (checkForSave()) System.exit(0); } /* getters and setters */ public PanAff getPanAff() { return panaff; } public SPVProject getProject() { return project; } public ArrayList getPathsList() { return pathListToSave; } public ArrayList getAttributeOfFileList() { return AttributeOfFileToSave; } public ArrayList getTypeOfFileList() { return typeOfFileToSave; } public ArrayList getReverseYElementList() { return yReverseElementToSave; } public ArrayList getHideElementList() { return hideElementToSave; } public String getPathOfPredFile() { return pathOfPredfileToSave; } public void setPathOfPredFile(String path) { this.pathOfPredfileToSave = path; } public String getAttributeOfPredFile() { return attributeOfPredFile; } public void setAttributefPredFile(String attribute) { this.attributeOfPredFile = attribute; } public String getTypeOfPredFile() { return typeOfPredFile; } public void setTypeOfPredFile(String type) { this.typeOfPredFile = type; } public String getPathOfExpfile() { return pathOfExpfileToSave; } public void setPathOfExpFile(String path) { pathOfExpfileToSave = path; } public void setTypeOfExpFile(String type) { this.typeOfExpFile = type; } public String getAttributeOfExpFile() { return attributeOfExpFile; } public void setAttributefExpFile(String attribute) { attributeOfExpFile = attribute; } public String getTypeOfExpFile() { return typeOfExpFile; } public int getPredfileToSave() { return PredfileToSave; } public int getExpfileToSave() { return ExpfileToSave; } public int getNbOfDataFile() { return nbdf; } public String getPredFileName() { return npredf; } public String getExpFileName() { return nexpf; } public void enableXUnshiftMenu(int n, boolean enable) { jmixunshift[n].setEnabled(enable); } public void enableYUnshiftMenu(int n, boolean enable) { jmiyunshift[n].setEnabled(enable); } public void enableHideMenu(int n, boolean enable) { jmihide[n].setEnabled(enable); } public void enableShowMenu(int n, boolean enable) { jmishow[n].setEnabled(enable); } public void setUnsave(boolean unsave) { if (this.project != null) { this.project.setUnsave(unsave); } } } resources/JobPlay/000755 001750 001750 00000000000 14325453166 014747 5ustar00cyrilcyril000000 000000 resources/pixmaps/pause.png000644 001750 001750 00000002501 14000365526 016701 0ustar00cyrilcyril000000 000000 PNG  IHDR szzIDATXWMEWUkv^ID.x& !WO%$ȚGcra ^\=ʒͰ;0S4{+bM)5ArI>K$IΑlv]GH3"2)!` $t:v7ȔRDe'>7sKKK7vJ@H~D)%Zkaq\2I""ݻn@DG 5Zy>`8$SJw?kOcXke»r ;.@9WM)-[ &d >j0ƬR 9yL6齟*cR57~Qmcc IDw֘@D0 IZ t,I4MFERŹT?:Z[m7Zkyq)`"r|γ,CE;VF28p*D1FGQ>v-xƘBJjƘ47DDs98pFSO;k׮>8y9|{*VVV֝$)zX6 r޽pPihORMIv*Ill"vH-$Oph6zEvZnd4UDx[pl0LF$o=vK8FZECLjQ N"rVDl8"!n'0m>66GRl *ՅzEZ[d>k-QT j:pqq1s:wZ BLj1#I<~Zkq\J2~j)(nٳJ)uNF4E/4CezH ВڿZjVG6۷u_9*1&;Z볷o߾Ɩ]{{e|Z(.Oiz$#\EdNDZ"2nk7f`gIENDB`resources/pixmaps/forward.png000644 001750 001750 00000002637 14000365526 017242 0ustar00cyrilcyril000000 000000 PNG  IHDR szzfIDATXW]h\EΙI7yyIiK`ۇjɋV(b_T>HR'JB ƿ(P֢ݧH Z!]dٹ&2p93979g&#"JD@De"&MU*:ID1/44,׈}Y*f1!B03>;=ZLD !BQEQ $If>߿uB"HA0 6$[bƍbXYi)n >̻d3+NJC"mB!. !FgffbKpw jZ#JA)I!(Be#Z$HK)O*j, JAMߌR v 3o\.ir3fzl@[yR²,XuWǍBl3A췵kcH)qf`}}>,ZIxHeRҲ,1ZTJuB)5D43K 2 FGG_J(,mSmS`O6>?q6,KXXLDIIAcO $9tZ#Cܹ_J%AS`f&ZM4>({3s{f{bYlZɌ LDQcxUs8\Ǧ8ͷq)4"$J5iK.޽?100Q|.vieannJ)s)N"s& 0G#JM4.`fT*!ɠ ! Bk ðm4U۶ <3JDD[(TPV*Q(b4LD+j$ijd5b h#":ODZIӻj ۶a6,˺_Jy?3CC\JLfxlj)|>L:yg m .t:YXT*T*q FHL&s'nj u]+R+ffAr qbpuYt,k_2usV4.2_֨똛mǵq))勹\X.Z T*pݺuD$ôЉyj%(V&\.7ߊլQm15iSxmt=gif1TPS'?IENDB`sources/org/spview/filehandler/PredFile.java000644 001750 001750 00000014301 14205636315 021771 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; /* * Class of prediction file */ import java.awt.geom.Point2D; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import javax.swing.JOptionPane; import org.spview.point.PredXPoint; /** * This class defines prediction data. */ public class PredFile { private final String name; // file name private final String type; // type (TDS/HITRAN/PICKETT) private double[] x; // X private double[] y; // Y private String[] jsyn; // assignment string private int[] jinf; // jinf extracted from jsyn private int nbxy; // nb of points private BufferedReader br; private final String lnsep; // line separator //////////////////////////////////////////////////////////////////// /** * Construct a new PredFile.
* (TDS, HITRAN or PICKETT type) * * @param cname name of the file * @param ctype type of the file */ public PredFile(String cname, String ctype) { name = cname; // file name type = ctype; // type (TDS/HITRAN/PICKETT) lnsep = System.getProperty("line.separator"); nbxy = 0; // nb of points } //////////////////////////////////////////////////////////////////// /** * Read file.
* validity is tested following FORMAT : 1022 of spect.f, 1000 of spech.f */ public boolean read() { // to keep points, unknown nb of points ArrayList alx = new ArrayList<>(); // frequency ArrayList aly = new ArrayList<>(); // intensity ArrayList aljsyn = new ArrayList<>(); // assignment ArrayList alxjsyn = new ArrayList<>(); // extended assignment String cjsyn = ""; // current assignment String cxjsyn = ""; // current extended assignment try { Point2D data; br = new BufferedReader(new FileReader(name)); // open // to read file String str; while ((str = br.readLine()) != null) { // line read if (type.equals("HITRAN")) { data = HITRANFile.extractXY(str); if (data != null) { cjsyn = HITRANFile.extractAssignment(str); cxjsyn = HITRANFile.extractExtendedAssignment(str); } } else if (type.equals("PICKETT")) { data = PickettFile.extractXY(str); if (data != null) { cjsyn = PickettFile.extractAssignment(str); } } else { data = TDSFile.extractXY(str); if (data != null) { cjsyn = TDSFile.extractAssignment(str); } } if (data != null) { alx.add(data.getX()); // keep data aly.add(data.getY()); // test if jsyn is not empty if (cjsyn.length() == 0) { JOptionPane.showMessageDialog(null, str + lnsep + "Empty jsyn in file" + lnsep + name); return false; } // test if xjsyn (HITRAN) or jsyn (TDS) is uniq if (type.equals("HITRAN")) { // HITRAN if (alxjsyn.contains(cxjsyn)) { JOptionPane.showMessageDialog(null, cxjsyn + lnsep + "Duplicated in file" + lnsep + name); return false; } alxjsyn.add(cxjsyn); // keep data } else { // TDS if (aljsyn.contains(cjsyn)) { JOptionPane.showMessageDialog(null, cjsyn + lnsep + "Duplicated in file" + lnsep + name); return false; } } aljsyn.add(cjsyn); // keep data } } } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null, "IO error while reading file" + lnsep + name + lnsep + ioe); return false; } finally { // close if (br != null) { try { br.close(); } catch (IOException ignored) { } } } // save data nbxy = alx.size(); // nb of points if (nbxy == 0) { // no point JOptionPane.showMessageDialog(null, "No valid data found in file" + lnsep + name); return false; } x = new double[nbxy]; y = new double[nbxy]; jsyn = new String[nbxy]; jinf = new int[nbxy]; // sort PredXPoint[] pxpt = new PredXPoint[nbxy]; // compare X for (int i = 0; i < nbxy; i++) { pxpt[i] = new PredXPoint(alx.get(i), aly.get(i), aljsyn.get(i)); } Arrays.sort(pxpt); // sort // save data for (int i = 0; i < nbxy; i++) { x[i] = pxpt[i].getX(); y[i] = pxpt[i].getY(); jsyn[i] = pxpt[i].getJsyn(); try { // extract jinf from jsyn // TDS, HITRAN jinf[i] = Integer.parseInt(jsyn[i].substring(0, 3).trim()); } catch (NumberFormatException e) { // format error jinf[i] = -1; } } return true; } /** * Get number of data points. */ public int getNbxy() { return nbxy; } /** * Get X array. */ public double[] getX() { return x; } /** * Get Y array. */ public double[] getY() { return y; } /** * Get X value. */ public double getX(int id) { return x[id]; } /** * Get Y value. */ public double getY(int id) { return y[id]; } /** * Get Jinf array. */ public int[] getJinf() { return jinf; } /** * Get Jsyn assignment array. */ public String[] getJsyn() { return jsyn; } } resources/pixmaps/playback.png000644 001750 001750 00000006131 14000365526 017355 0ustar00cyrilcyril000000 000000 PNG  IHDR szzzTXtRaw profile type exifxڭW[v*f Hu, iwsX)P\rI-5V u;]ɿ'<)Ǽ|]>_(=q֩g!^ϼz|\&vPO.[ڮ&FF 7%i}ċ ̃j,x(.\ B"BoU< 3_\%+bi0 é8~PP ]31O;5̳(0Ҕ*(DBr; 6Ȅ•moSmFi3ҦZASRE DIADDs)KιdQZbIEJ.ҊXSk[D [ Tbiۊ{KϽ[ׁiȣ:3N̳Ygh!VZ*t#viλևjGWr߫FG5S,̥|,AVN4bSͦU)%6L1B8L1(lzhܷIJ7r/ &QWި6OYW(lrU;~{8g0R ^kR&-0l 00O?p2F8. ^w?|gң - E #C` x .wȭ#F Y]5{ [K/YQ:Yߑ In{D}#mi w1d Z\;{ֶ{Lø %WGpwƦчlu 7&`kFhN_OX%#$Zx$SU#Lq(#M.Up(X)bcoDWx`(,JQH , H@k>Y@?IDoUb(VEUtkmn3s/;׻|lZٽΜ9N긪Uz^UC^ϴ}PTW)UIDPUXU""~@V:Vz900𼪾($Hm B@aFDy9fi\~WU0P  k-0s!"zEUKTtk,!Q <@XpgD(\Dri1ZXk!"X\\3#IFU1TƘ+$IBsi5w͛'_9rsi }.@B@e(p)]~-022sw _ c Y!MhIR1U}"%Zbts~7mMܞ(QI9V&-R7VA9d-%Vn\j9=k׮!kg^)ZMfO}VBNI|x$ZD713on7‚[nDZwϞ^vOfUjAe(JkU3fQ1"D\^Hil#;kѷF&oi"MSXkj~5]: D~1Gi^*J(Jh6(˹+Wxz,ih``` /Nzt#k-yY̞v߸q㯎իrj4T*0>jMaqq{[R D\&j'"y+izb_P׳O1c,69ܹs (9Z-xADHIZ$I2+W5f۶m{{svef30]|{ `ll̊”~Nїsssޜv[VEdBDE=0LVFeIENDB`resources/PanAff/jmiunselexp.html000644 001750 001750 00000000313 14000365526 017760 0ustar00cyrilcyril000000 000000 Unselect the selected experimental peak.

Assignment

Deassociate all predictions from the selected experimental peak.

sources/org/spview/point/ExpJsynPoint.java000644 001750 001750 00000006333 14205637574 021603 0ustar00cyrilcyril000000 000000 package org.spview.point; /** * This class allows sorting experiment data following assignment order. */ public class ExpJsynPoint implements Comparable { private String faso; // frequency mark private String saso; // intensity mark private String jsyn; // assignment private int ipred; // index of related prediction line private String comm; // comment /** * Construct a new ExpJsynPoint. * * @param cfaso frequency mark * @param csaso intensity mark * @param cjsyn assignment * @param cipred index of related prediction line * @param ccomm comment */ public ExpJsynPoint(String cfaso, String csaso, String cjsyn, int cipred, String ccomm) { faso = cfaso; // frequency mark saso = csaso; // intensity mark jsyn = cjsyn; // assignment ipred = cipred; // index of related prediction line comm = ccomm.trim(); // comment (without extra spaces) } ///////////////////////////////////////////////////////////////////// /** * Get frequency mark. */ public String getFaso() { return faso; } /** * Set frequency mark. */ public void setFaso(String cfaso) { faso = cfaso; } /** * Get intensity mark. */ public String getSaso() { return saso; } /** * Set intensity mark. */ public void setSaso(String csaso) { saso = csaso; } /** * Get related assignment. */ public String getJsyn() { return jsyn; } /** * Set related assignment. */ public void setJsyn(String cjsyn) { jsyn = cjsyn; } /** * Get index of related prediction line. */ public int getIpred() { return ipred; } /** * Set index of related prediction line. */ public void setIpred(int cipred) { ipred = cipred; } /** * Get comment. */ public String getComm() { return comm; } /** * Set comment. */ public void setComm(String cstr) { comm = cstr.trim(); // without extra spaces } /** * Compare assignment strings. * * @param cejpt the ExpJsynPoint to compare to */ public int compareTo(Object cejpt) { if( ! (cejpt instanceof ExpJsynPoint) ) { // not the right object throw new ClassCastException(); } int delta = ((ExpJsynPoint)cejpt).getJsyn().compareTo(jsyn); // compare assignment strings if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } return 0; // equal } } sources/org/spview/point/000755 001750 001750 00000000000 14325453166 016315 5ustar00cyrilcyril000000 000000 resources/JobPlay/jmicpfnone.html000644 001750 001750 00000000233 14000365526 017753 0ustar00cyrilcyril000000 000000

Create Peak File

Create peak file from a spectrum file to be chosen.

resources/JobPlay/jmilpTDS.html000644 001750 001750 00000000274 14000365526 017316 0ustar00cyrilcyril000000 000000

Load prediction file

Load the prediction file in TDS format to plot it as ticks in the pred/exp area.

test/XMLXSDChecker.java000644 001750 001750 00000004367 14000365526 015525 0ustar00cyrilcyril000000 000000 import java.io.File; import java.io.IOException; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class XMLXSDChecker { /* * This program is for checking the XML file and XSD schema. * It uses the class "SimpleErrorHandler" and catch errors in order to create a correct * XML XSD groupe of files */ public void String (String XMLFileName) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { //These 3 lines are for informing that the validation is done via an XSD file. SchemaFactory sfactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); //The XSD schema is created //Here, it is an intern schema, for an extern one an URI is required Schema schema = sfactory.newSchema(new File("ProjectFile.xsd")); //The schema is assigned to the factory so that the document takes in charge the XSD file factory.setSchema(schema); DocumentBuilder builder = factory.newDocumentBuilder(); //Creation of the error handler ErrorHandler errHandler = new SimpleErrorHandler(); //Assignment of the error handler to the document in order to catch some. builder.setErrorHandler(errHandler); File fileXML = new File("ProjectFile.xml"); //A block of capture is added to catch error if there are some. try { Document xml = builder.parse(fileXML); Element root = xml.getDocumentElement(); System.out.println(root.getNodeName()); } catch (SAXParseException e) {} } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }sources/org/spview/filehandler/EndianDataInputStream.java000644 001750 001750 00000006705 14205456676 024506 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * Simple class to add endian support to DataInputStream. * User: michael * Date: 9/12/13 * Time: 4:39 PM */ public class EndianDataInputStream extends InputStream implements DataInput { DataInputStream dataIn; private final ByteBuffer buffer = ByteBuffer.allocate(8); ByteOrder order = ByteOrder.LITTLE_ENDIAN; public EndianDataInputStream( InputStream stream ){ dataIn = new DataInputStream( stream ); } public EndianDataInputStream order(ByteOrder o){ order = o; return this; } @Override public int read(byte[] b) throws IOException { return dataIn.read(b); } @Override public int read(byte[] b, int off, int len) throws IOException { return dataIn.read(b, off, len); } @Deprecated @Override public String readLine() throws IOException { return dataIn.readLine(); } @Override public boolean readBoolean() throws IOException { return dataIn.readBoolean(); } @Override public byte readByte() throws IOException { return dataIn.readByte(); } @Override public int read() throws IOException { return readByte(); } @Override public boolean markSupported(){ return dataIn.markSupported(); } @Override public void mark(int readlimit) { dataIn.mark(readlimit); } @Override public void reset() throws IOException { dataIn.reset(); } @Override public char readChar() throws IOException { return dataIn.readChar(); } @Override public void readFully(byte[] b) throws IOException { dataIn.readFully(b); } @Override public void readFully(byte[] b, int off, int len) throws IOException { dataIn.readFully(b, off, len); } @Override public String readUTF() throws IOException { return dataIn.readUTF(); } @Override public int skipBytes(int n) throws IOException { return dataIn.skipBytes(n); } @Override public double readDouble() throws IOException { long tmp = readLong(); return Double.longBitsToDouble( tmp ); } @Override public float readFloat() throws IOException { int tmp = readInt(); return Float.intBitsToFloat( tmp ); } @Override public int readInt() throws IOException { buffer.clear(); buffer.order( ByteOrder.BIG_ENDIAN ) .putInt( dataIn.readInt() ) .flip(); return buffer.order( order ).getInt(); } @Override public long readLong() throws IOException { buffer.clear(); buffer.order( ByteOrder.BIG_ENDIAN ) .putLong( dataIn.readLong() ) .flip(); return buffer.order( order ).getLong(); } @Override public short readShort() throws IOException { buffer.clear(); buffer.order( ByteOrder.BIG_ENDIAN ) .putShort( dataIn.readShort () ) .flip(); return buffer.order( order ).getShort(); } @Override public int readUnsignedByte() throws IOException { return dataIn.readByte(); } @Override public int readUnsignedShort() throws IOException { return readShort(); } }sources/org/spview/gui/PlotData.java000644 001750 001750 00000016334 14325453236 020330 0ustar00cyrilcyril000000 000000 package org.spview.gui; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import javax.swing.JPanel; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import org.spview.point.ResidualPoint; public class PlotData extends JPanel { /** * */ private static final long serialVersionUID = -2309468385394812985L; private final int padding = 50; private final int labelPadding = 25; private final Color lineColor = new Color(44, 102, 230, 180); private final Color pointColor = new Color(100, 100, 100, 180); private final Color gridColor = new Color(200, 200, 200, 200); private static final Stroke GRAPH_STROKE = new BasicStroke(2f); final private int accuracyRadius = 12; private final List data; private JFText jft; public PlotData(JobPlay jobplay, List data) { addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent me) { setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); } @Override public void mouseExited(MouseEvent me) { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } @Override public void mouseClicked(MouseEvent me) { if (!me.isConsumed()) { if (me.getClickCount() == 2) { // handle double click me.consume(); Point2D point = me.getPoint(); // get Min and Max; double maxX = getRealX(point.getX() + accuracyRadius / 2.0); double minX = getRealX(point.getX() - accuracyRadius / 2.0); jobplay.getPanAff().setXZoom(minX, maxX); jobplay.setUnsave(true); } else { Point2D point = me.getPoint(); ArrayList result = findPoint(point); if (result.size() > 0) { showPoint(result); } } } } }); this.data = data; } private double getXScale() { return ((double) getWidth() - (2 * padding) - labelPadding) / (getMaxXData() - getMinXData()); } private double getYScale() { return ((double) getHeight() - (2 * padding) - labelPadding) / (getMaxYData() - getMinYData()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); List graphPoints = new ArrayList<>(); for (ResidualPoint datum : data) { int x1 = (int) ((double) getWidth() - padding - (getMaxXData() - datum.getX()) * getXScale()); int y1 = (int) ((getMaxYData() - datum.getY()) * getYScale() + padding); graphPoints.add(new Point(x1, y1)); } // draw white background g2.setColor(Color.WHITE); g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding); g2.setColor(Color.BLACK); // create hatch marks and grid lines for y axis. int pointWidth = 4; int numberYDivisions = 10; for (int i = 0; i < numberYDivisions + 1; i++) { int x0 = padding + labelPadding; // int x1 = pointWidth + padding + labelPadding; int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding); if (data.size() > 0) { g2.setColor(gridColor); g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y0); g2.setColor(Color.BLACK); double yLabelDouble = ((int) ((getMinYData() + (getMaxYData() - getMinYData()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0; String yLabel = String.format("%.2f", yLabelDouble); FontMetrics metrics = g2.getFontMetrics(); int labelWidth = metrics.stringWidth(yLabel); g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3); } // g2.drawLine(x0, y0, x1, y1); } // and for x axis for (int i = 0; i < data.size(); i++) { if (data.size() > 1) { int x0 = i * (getWidth() - padding * 2 - labelPadding) / (data.size() - 1) + padding + labelPadding; int y0 = getHeight() - padding - labelPadding; // int y1 = y0 - pointWidth; int interval = (int) (data.size() / 3.0); if (i % (interval + 1) == 0) { g2.setColor(gridColor); g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x0, padding); g2.setColor(Color.BLACK); String xLabel = String.format("%.3f", data.get(i).getX()); FontMetrics metrics = g2.getFontMetrics(); int labelWidth = metrics.stringWidth(xLabel); g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3); } else if (i == data.size() - 1) { String xLabel = String.format("%.3f", data.get(i).getX()); FontMetrics metrics = g2.getFontMetrics(); int labelWidth = metrics.stringWidth(xLabel); g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3); } // g2.drawLine(x0, y0, x1, y1); } } // create x and y axes g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding); g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding); Stroke oldStroke = g2.getStroke(); g2.setColor(lineColor); g2.setStroke(GRAPH_STROKE); g2.setStroke(oldStroke); g2.setColor(pointColor); for (Point graphPoint : graphPoints) { int x = graphPoint.x - pointWidth / 2; int y = graphPoint.y - pointWidth / 2; g2.fillOval(x, y, pointWidth, pointWidth); } } private void showPoint(ArrayList result) { if (jft != null) { // free old frame jft.dispose(); } jft = new JFText(result, 0, 50); jft.setVisible(true); } private ArrayList findPoint(Point2D mouse) { ArrayList bingo = new ArrayList<>(); for (ResidualPoint point : data) { Point pxPoint = new Point(getPixelX(point.getX()), getPixelY(point.getY())); double dist = pxPoint.distance(mouse.getX(), mouse.getY()); if (dist < accuracyRadius) { bingo.add(point.toString()); } } return bingo; } public double getRealX(double x) { return ((x - (double) getWidth() + padding) / getXScale()) + getMaxXData(); } public int getPixelX(double x) { return (int) ((double) getWidth() - padding - (getMaxXData() - x) * getXScale()); } public int getPixelY(double y) { return (int) ((getMaxYData() - y) * getYScale() + padding); } private double getMinYData() { double minData = Double.MAX_VALUE; for (ResidualPoint point : data) { minData = Math.min(minData, point.getY()); } return minData; } private double getMaxYData() { double maxData = Double.MIN_VALUE; for (ResidualPoint point : data) { maxData = Math.max(maxData, point.getY()); } return maxData; } private double getMinXData() { double minData = Double.MAX_VALUE; for (ResidualPoint point : data) { minData = Math.min(minData, point.getX()); } return minData; } private double getMaxXData() { double maxData = Double.MIN_VALUE; for (ResidualPoint point : data) { maxData = Math.max(maxData, point.getX()); } return maxData; } public List getData() { return data; } public JFText getJFT() { return jft; } }sources/org/spview/point/PredJsynPoint.java000644 001750 001750 00000003103 14205637617 021727 0ustar00cyrilcyril000000 000000 package org.spview.point; /** * This class allows sorting prediction jsyn data. */ public class PredJsynPoint implements Comparable { private final String jsyn; // assignment private final int ind; // index before sort /** * Construct a new PredJsynPoint. * * @param cjsyn assignment * @param cind index before sort */ public PredJsynPoint(String cjsyn, int cind) { jsyn = cjsyn; // assignment ind = cind; // index before sort } ///////////////////////////////////////////////////////////////////// /** * Get prediction assignment string. */ public String getJsyn() { return jsyn; } /** * Get index before sort. */ public int getInd() { return ind; } /** * Compare following assignment string. * * @param cpjp the PredJsynPoint to compare to */ public int compareTo(Object cpjp) { if( ! (cpjp instanceof PredJsynPoint) ) { // not the right object throw new ClassCastException(); } int delta = ((PredJsynPoint)cpjp).getJsyn().compareTo(jsyn); // compare assignment strings if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } return 0; // equal } } sources/org/spview/filehandler/FortranFormat.java000644 001750 001750 00000007464 14205636246 023102 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.awt.geom.Point2D; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Locale; import javax.swing.JOptionPane; public class FortranFormat { /** * Format a frequency.
* F12.6 FORTRAN FORMAT
* WARNING : only positive values */ public static String formFreq(double cx) { StringBuilder sb; NumberFormat nff = NumberFormat.getNumberInstance(Locale.US); // ask a plain format DecimalFormat dff = (DecimalFormat) nff; // reduce to decimal format dff.applyPattern("00000.000000"); // define pattern sb = new StringBuilder(dff.format(cx)); // format for (int i = 0; i < sb.length(); i++) { if (sb.charAt(i) == '0') { sb.setCharAt(i, ' '); // replace leading 0s with spaces } else { // end of leading 0s break; } } return sb.toString(); } public static String formResidus(double cx) { StringBuilder sb; NumberFormat nff = NumberFormat.getNumberInstance(Locale.US); // ask a plain format DecimalFormat dff = (DecimalFormat) nff; // reduce to decimal format dff.applyPattern(".0000"); // define pattern sb = new StringBuilder(dff.format(cx)); // format if (sb.charAt(0) != '-') { // if not beginning with a - sb.insert(0, ' '); // insert a space } for (int i = 0; i < sb.length(); i++) { if (sb.charAt(i) == '0') { sb.setCharAt(i, ' '); // replace leading 0s with spaces } else { // end of leading 0s break; } } return sb.toString(); } /** * Format an intensity.
* E11.4 FORTRAN FORMAT */ public static String formInt(double cy) { StringBuilder sb; int ie; NumberFormat nfi = NumberFormat.getNumberInstance(Locale.US); // ask a plain format DecimalFormat dfi = (DecimalFormat) nfi; // reduce to decimal format dfi.applyPattern(".0000E00"); // define pattern sb = new StringBuilder(dfi.format(cy)); // format if (sb.charAt(0) != '-') { // if not beginning with a - sb.insert(0, ' '); // insert a space } ie = sb.indexOf("."); if (ie >= 0) { sb.insert(ie, '0'); // insert a 0 before the . } ie = sb.indexOf("E"); ie++; if (sb.charAt(ie) != '-') { // if positive exponent sb.insert(ie, '+'); // insert a + } return sb.toString(); } // return an blank string public static String bStr(int nbc) { return " ".repeat(Math.max(0, nbc)); } public static void printAssignmentFormattedOutput(ArrayList peakPositions, Point2D uncert, File file) { String lnsep = System.getProperty("line.separator"); PrintWriter out1 = null; try { out1 = new PrintWriter(file); for (int i = 0; i < peakPositions.size(); i++) { double x = peakPositions.get(i).getX(); double y = peakPositions.get(i).getY(); // NUO (5 char, RIGHT) String cStrNUO = " " + ("" + (i + 1)).trim(); cStrNUO = cStrNUO.substring(cStrNUO.length() - 5); String cStr = (" " + // ISP cStrNUO + // NUO line number FortranFormat.formFreq(x) + // FOBS frequency " " + FortranFormat.formInt(y) + // SOBS intensity " " + String.format("%9.6f %4.1f", uncert.getX(), uncert.getY()));// SDFOBS,SDSOBS frequency // and intensity // standard deviations string int l; for (l = cStr.length(); l > 0; l--) { if (cStr.charAt(l - 1) != ' ') { break; } } out1.println(cStr.substring(0, l)); // without extra spaces } } catch (IOException ioe) { // IO error JOptionPane.showMessageDialog(null, "IO error while writing file" + lnsep + ioe); } finally { // close the file if (out1 != null) { out1.close(); if (out1.checkError()) { JOptionPane.showMessageDialog(null, "PrintWriter error while creating exp file" + lnsep); } } } } } resources/pixmaps/text-x-spv.svg000644 001750 001750 00000753572 14205450376 017670 0ustar00cyrilcyril000000 000000 image/svg+xml test/SimpleErrorHandler.java000644 001750 001750 00000001127 14000365526 016751 0ustar00cyrilcyril000000 000000 import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class SimpleErrorHandler implements ErrorHandler { public void warning(SAXParseException e) throws SAXException { System.out.println("WARNING : " + e.getMessage()); } public void error(SAXParseException e) throws SAXException { System.out.println("ERROR : " + e.getMessage()); throw e; } public void fatalError(SAXParseException e) throws SAXException { System.out.println("FATAL ERROR : " + e.getMessage()); throw e; } }resources/JobPlay/jmiloadstick.html000644 001750 001750 00000000240 14000365526 020276 0ustar00cyrilcyril000000 000000

Load as stick

Load an XY peak list to plot it as sticks in the data area.

resources/PanAff/jmivalexppasy.html000644 001750 001750 00000000233 14000365526 020312 0ustar00cyrilcyril000000 000000

Scale

Pred-as-stick Y-axis zoom/unzoom by choosing a scaling factor.

sources/org/spview/filehandler/OpusFile.java000644 001750 001750 00000166431 14212335500 022027 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import org.spview.gui.OpusDialog; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; //---------------------------------------------------------------------------------------------- // ** STRUCTURES IN OPUS FILES ** class Header { // ***** DATA ***** public int magic_number; // Magic number (= 0xFEFE0A0AL for OPUS files) public double pgm_ver; // Program Version Number public int dir_ptr; // Pointer to first directory in bytes public int max_entries; // Max size of directory in terms of blocks public int cur_entries; // Current size of directory in terms of blocks } //---------------------------------------------------------------------------------------------- class DirectoryEntry { // ***** DATA ***** public int type; // Directory block type public int length; // Length in 32 bits words public int pointer; // Offset File Pointer (in bytes) } //---------------------------------------------------------------------------------------------- class ParameterBlock implements Serializable { // ***** DATA ***** public String name; public short type; public short rs; } class data_status_parameters { boolean valid; // true when data available int pointer; // Pointer to the data points int length; // Number of points (as float) int DPF; // data point format (long, float, ...) int NPT; // number of data points double FXV; // 1st X value double LXV; // last X value double CSF; // common factor for all Y-values ("SCALING FACTOR") double MXY; // maximum Y value double MNY; // minimum Y value String DXU; // data X-units (WN, MI, LGW, MIN, PNT) // int DER; // derivative (0, 1, 2, ...) String DAT; // date String TIM; // time } class instrument_parameters { boolean valid; // true when data available boolean supported_instrument; // true if instrument an IFS125HR, IFS120HR or IFS120M double HFL, // High folding limit LFL; // Low folding limit long DFR, // Digital filter reduction DFC; // Number of filter coef. double HFF, // Digital filter HFL LFF; // Digital filter LFL long ASG, // Actual signal gain ARG, // Actual ref. signal gain ALF, // Actual low pass filter AHF, // Actual high pass filter ASS, // Number of sample scans ARS, // Number of background scans GFW, // Number of good FW scans GBW, // Number of good BW scans BFW, // Number of bad FW scans BBW; // Number of bad BW scans double DUR; // Scan time (sec) long RSN; // Running sample number int PKA; // Peak amplitude long PKL; // Peak location int PRA; // Backward peak amplitude long PRL, // Backward peak location SSM, // Sample spacing multiplicator SSP, // Sample spacing divisor SGP, // Switch gain position SGW; // Gain switch window String INS, // Instrument type ITF; // Interface type for optic long SIM, // Simulation mode DEB, // Debug printer mode LOG, // Logfile for measurement ADR; // AQP address double RMX; // Resolution limit long PLL, // Maximum PLL setting FFT, // Maximum FT size in K MXD; // Maximum ADC state in Hz double FOC; // Focal length long ABP; // Absolute peak position in Laser*2 double LWN, // Laser wavenumber RLW, // Raman laser wavenumber NLA, // Non-linearity correction: ALPHA NLB; // Non-linearity correction: BETA } class acquisition_parameters { boolean valid; // true when data available String SGN; // Signal gain, sample String RGN; // Signal gain, background long GSW; // Gain switch window String GSG; // Gain switch gain String AQM; // Acquisition mode long AQR; // Acquisition to RAM long NSS; // Number of scans double MIN; // Measurement duration in min. String COR; // Correlation test mode long DLY; // Delay before measurement double HFW; // Wanted high frequency limit double LFW; // Wanted low frequency limit double RES; // Resolution long TDL; // To do list String PLF; // Result spectrum } class ft_parameters { boolean valid; // true when data available String APF; // Apodization function double HFQ; // High frequency cutoff double LFQ; // Low frequency cutoff String PHZ; // Phase correction mode double PHR; // Phase resolution long PIP; // Phase interferogram points long PTS; // Phase transform size long DIG; // Digital filter String SPZ; // Stored phase mode String ZFF; // Zero filling factor } class processing_parameters { boolean valid; // true when data available String APT; // Aperture String BMS; // Beamsplitter String DTC; // Detector String OPF; // Optical filter String PGN; // Preamplifier gain String CHN; // Measurement channel String SRC; // Source String VEL; // Scanner velocity String HPF; // High pass filter String LPF; // Low pass filter } class sample_origin_parameters { boolean valid; // true when data available String SNM; // Sample name String SFM; // Sample form String CNM; // Chemist name String HIS; // History of last operations leading to this file String PTH; // Measurement path String EXP; // Experiment String NAM; // Filename String IST; // Instrument status } public class OpusFile { private enum data_type { DT_SPEC, DT_IGM, DT_PHAS, DT_ABS, DT_TR, DT_COUNT, DT_NONE } private final String[] data_type_string = { "Spectrum", "Inteferogram", "Phase Spectrum", "Absorbance Spectrum", "Transmitance Spectrum" }; // Possible types (cf "Description of the OPUS datafile format", JGR 23.1.92, p.6) private enum datafile_format {INT_32, REAL64, STRING, ENUM, SENUM} // Masks and constants to determine the type of data in Opus files // See "Description of the OPUS datafile format", JGR 23.1.92 // ** SHIFTS and MASKS private static final long DBSCPLX = 0L, DBMCPLX = 3L, // Bits 0-1: Kind of data //DBSSTYP = 2L, DBMSTYP = 3L, // Bits 2-3: Type of data DBSPARM = 4L, DBMPARM = 63L, // Bits 4-9: Parameters DBSDATA = 10L, DBMDATA = 7L; // Bits 10-16: Only 10-12 taken into account private static final long DBBCPLX = DBMCPLX << DBSCPLX, // Isolate data type no. 1 //DBBSTYP = DBMSTYP << DBSSTYP, // data type no. 2 DBBPARM = DBMPARM << DBSPARM, // parameters DBBDATA = DBMDATA << DBSDATA; // data // ** CONSTANTS private static final long /*DBTREAL = 1L,*/ // Real part of complex data -> DBMCPLX //DBTIMAG = 2L, // Imag part of complex data DBTAMPL = 3L, // Amplitude data //DBTSAMP = 1L, // Sample data -> DBMSTYP //DBTREF = 2L, // Reference data //DBTRATIO = 3L, // Ratioed data DBTDSTAT = 1L, // Data status parameter -> DBMPARM DBTINSTR = 2L, // Instrument status parameters DBTAQPAR = 3L, // Standard acquisition parameters DBTFTPAR = 4L, // FT parameters DBTPRCPAR = 6L, // Processing (optics) parameters DBTORGPAR = 10L, // Sample origin parameters DBTSPEC = 1L, // Spectrum, undefined Y-units -> DBMDATA DBTIGRM = 2L, // Interferogram DBTPHAS = 3L, // Phase spectrum DBTAB = 4L, // Absorbance spectrum DBTTR = 5L, // Transmittance spectrum DBTDIR = 13L; // DIRECTORY // All other values -> Spectrum assumed (DBTSPEC). private static final long DBBAMPL = DBTAMPL << DBSCPLX, // Shifted masks //DBBIMAG = DBTIMAG << DBSCPLX, //DBBREAL = DBTREAL << DBSCPLX, //DBBSAMP = DBTSAMP << DBSSTYP, //DBBREF = DBTREF << DBSSTYP, //DBBRATIO = DBTRATIO << DBSSTYP, DBBDSTAT = DBTDSTAT << DBSPARM, DBBINSTR = DBTINSTR << DBSPARM, DBBAQPAR = DBTAQPAR << DBSPARM, DBBFTPAR = DBTFTPAR << DBSPARM, DBBPRCPAR = DBTPRCPAR << DBSPARM, DBBORGPAR = DBTORGPAR << DBSPARM, DBBSPEC = DBTSPEC << DBSDATA, DBBIGRM = DBTIGRM << DBSDATA, DBBPHAS = DBTPHAS << DBSDATA, DBBAB = DBTAB << DBSDATA, DBBTR = DBTTR << DBSDATA, DBBDIR = DBTDIR << DBSDATA; private final String name; ArrayList alx = new ArrayList<>(); // frequency ArrayList aly = new ArrayList<>(); // intensity private final Header opusHeader = new Header(); private DirectoryEntry[] opusDirectory; private final ParameterBlock obp = new ParameterBlock(); private static final instrument_parameters ins = new instrument_parameters(); private static final acquisition_parameters acq = new acquisition_parameters(); private static final sample_origin_parameters so = new sample_origin_parameters(); private final data_status_parameters[] dsp = new data_status_parameters[data_type.DT_COUNT.ordinal()]; private int nbxy; // nb of points boolean isCancelled = false; /** * Construct a new Opusfile. * * @param cname file name */ public OpusFile(String cname) { name = cname; // file name nbxy = 0; // nb of points } /** * Get name selection string. */ public String getname() { return name; } private boolean readHeader(File f) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { opusHeader.magic_number = dataInputStream.readInt(); // 4 bytes opusHeader.pgm_ver = dataInputStream.readDouble(); // 8 bytes opusHeader.dir_ptr = dataInputStream.readInt(); // 4 bytes opusHeader.max_entries = dataInputStream.readInt(); // 4 bytes opusHeader.cur_entries = dataInputStream.readInt(); // 4 bytes if (opusHeader.magic_number != 0xFEFE0A0A) return false; System.out.println("OPUS Program Version: " + opusHeader.pgm_ver + "\tDirectory address: " + opusHeader.dir_ptr + "\tMax. entries: " + opusHeader.max_entries + "\tCurrent: " + opusHeader.cur_entries); return true; } } private boolean readDirectoryEntry(File f) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { dataInputStream.readNBytes(24); // --> skip header for (int l = 0; l < opusHeader.cur_entries; l++) { DirectoryEntry directory = new DirectoryEntry(); directory.type = dataInputStream.readInt(); directory.length = dataInputStream.readInt(); directory.pointer = dataInputStream.readInt(); opusDirectory[l] = directory; } } return true; } private void readDataStatusParameters(File f, int pointer, data_status_parameters d) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { dataInputStream.readNBytes(pointer); d.valid = false; label: while (true) { byte[] bytes = dataInputStream.readNBytes(4); obp.name = new String(bytes,0,3, StandardCharsets.US_ASCII); obp.type = dataInputStream.readShort(); obp.rs = dataInputStream.readShort(); switch (obp.name) { case "DPF": assert (obp.type == datafile_format.INT_32.ordinal()); d.DPF = dataInputStream.readInt(); break; case "NPT": assert (obp.type == datafile_format.INT_32.ordinal()); d.NPT = dataInputStream.readInt(); break; case "FXV": assert (obp.type == datafile_format.REAL64.ordinal()); d.FXV = dataInputStream.readDouble(); break; case "LXV": assert (obp.type == datafile_format.REAL64.ordinal()); d.LXV = dataInputStream.readDouble(); break; case "CSF": assert (obp.type == datafile_format.REAL64.ordinal()); d.CSF = dataInputStream.readDouble(); break; case "DXU": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); d.DXU = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "MXY": assert (obp.type == datafile_format.REAL64.ordinal()); d.MXY = dataInputStream.readDouble(); break; case "MNY": assert (obp.type == datafile_format.REAL64.ordinal()); d.MNY = dataInputStream.readDouble(); break; case "DAT": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); d.DAT = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "TIM": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); d.TIM = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "END": break label; default: { int length = obp.rs << 1; dataInputStream.readNBytes(length); break; } } } // Data exist d.valid = true; } } private void readInstrumentParameters(File f, int pointer) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { dataInputStream.readNBytes(pointer); OpusFile.ins.valid = false; label: while (true) { byte[] bytes = dataInputStream.readNBytes(4); obp.name = new String(bytes, 0, 3, StandardCharsets.US_ASCII); obp.type = dataInputStream.readShort(); obp.rs = dataInputStream.readShort(); switch (obp.name) { case "HFL": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.HFL = dataInputStream.readDouble(); break; } case "LFL": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.LFL = dataInputStream.readDouble(); break; } case "DFR": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.DFR = dataInputStream.readInt(); break; } case "DFC": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.DFC = dataInputStream.readInt(); break; } case "HFF": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.HFF = dataInputStream.readDouble(); break; } case "LFF": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.LFF = dataInputStream.readDouble(); break; } case "ASG": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.ASG = dataInputStream.readInt(); break; } case "ARG": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.ARG = dataInputStream.readInt(); break; } case "ALF": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.ALF = dataInputStream.readInt(); break; } case "AHF": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.AHF = dataInputStream.readInt(); break; } case "ASS": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.ASS = dataInputStream.readInt(); break; } case "ARS": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.ARS = dataInputStream.readInt(); break; } case "GFW": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.GFW = dataInputStream.readInt(); break; } case "GBW": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.GBW = dataInputStream.readInt(); break; } case "BFW": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.BFW = dataInputStream.readInt(); break; } case "BBW": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.BBW = dataInputStream.readInt(); break; } case "DUR": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.DUR = dataInputStream.readDouble(); break; } case "RSN": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.RSN = dataInputStream.readInt(); break; } case "PKA": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.PKA = dataInputStream.readInt(); break; } case "PKL": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.PKL = dataInputStream.readInt(); break; } case "PRA": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.PRA = dataInputStream.readInt(); break; } case "PRL": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.PRL = dataInputStream.readInt(); break; } case "SSM": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.SSM = dataInputStream.readInt(); break; } case "SSP": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.SSP = dataInputStream.readInt(); break; } case "SGP": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.SGP = dataInputStream.readInt(); break; } case "SGW": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.SGW = dataInputStream.readInt(); break; } case "INS": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.ins.INS = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "ITF": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.ins.ITF = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "SIM": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.SIM = dataInputStream.readInt(); break; } case "DEB": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.DEB = dataInputStream.readInt(); break; } case "LOG": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.LOG = dataInputStream.readInt(); break; } case "ADR": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.ADR = dataInputStream.readInt(); break; } case "RMX": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.RMX = dataInputStream.readDouble(); break; } case "PLL": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.PLL = dataInputStream.readInt(); break; } case "FFT": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.FFT = dataInputStream.readInt(); break; } case "MXD": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.MXD = dataInputStream.readInt(); break; } case "FOC": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.FOC = dataInputStream.readDouble(); break; } case "ABP": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.ins.ABP = dataInputStream.readInt(); break; } case "LWN": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.LWN = dataInputStream.readDouble(); break; } case "RLW": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.RLW = dataInputStream.readDouble(); break; } case "NLA": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.NLA = dataInputStream.readDouble(); break; } case "NLB": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.ins.NLB = dataInputStream.readDouble(); break; } case "END": break label; default: { int length = obp.rs << 1; dataInputStream.readNBytes(length); break; } } } // Instrument type and maximum values switch (OpusFile.ins.INS) { case "IFS125": case "IFS120 to 125 HR upgrade": OpusFile.ins.supported_instrument = true; OpusFile.ins.RMX = 0.00126; OpusFile.ins.PLL = 4L; OpusFile.ins.FFT = 64000L; OpusFile.ins.MXD = 80000L; break; case "IFS120": OpusFile.ins.supported_instrument = true; OpusFile.ins.RMX = 0.00185; OpusFile.ins.PLL = 8L; OpusFile.ins.FFT = 2048L; OpusFile.ins.MXD = 200000L; break; case "IFS120M": OpusFile.ins.supported_instrument = true; OpusFile.ins.RMX = 0.0035; OpusFile.ins.PLL = 8L; OpusFile.ins.FFT = 2048L; OpusFile.ins.MXD = 200000L; break; default: OpusFile.ins.supported_instrument = false; break; } // Data exist OpusFile.ins.valid = true; } } private void readStandardAcquisitionParameters(File f, int pointer) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { dataInputStream.readNBytes(pointer); OpusFile.acq.valid = false; label: while (true) { byte[] bytes = dataInputStream.readNBytes(4); obp.name = new String(bytes, 0, 3, StandardCharsets.US_ASCII); obp.type = dataInputStream.readShort(); obp.rs = dataInputStream.readShort(); switch (obp.name) { case "SGN": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.acq.SGN = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "RGN": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.acq.RGN = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "GSW": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.acq.GSW = dataInputStream.readInt(); break; } case "GSG": { assert (obp.type == datafile_format.SENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.acq.GSG = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "AQM": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.acq.AQM = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "AQR": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.acq.AQR = dataInputStream.readInt(); break; } case "NSS": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.acq.NSS = dataInputStream.readInt(); break; } case "MIN": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.acq.MIN = dataInputStream.readDouble(); break; } case "COR": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.acq.COR = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "DLY": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.acq.DLY = dataInputStream.readInt(); break; } case "HFW": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.acq.HFW = dataInputStream.readDouble(); break; } case "LFW": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.acq.LFW = dataInputStream.readDouble(); break; } case "RES": { assert (obp.type == datafile_format.REAL64.ordinal()); OpusFile.acq.RES = dataInputStream.readDouble(); break; } case "TDL": { assert (obp.type == datafile_format.INT_32.ordinal()); OpusFile.acq.TDL = dataInputStream.readInt(); break; } case "PLF": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.acq.PLF = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "END": break label; default: { int length = obp.rs << 1; dataInputStream.readNBytes(length); break; } } } // Data exist OpusFile.acq.valid = true; } } private void readFtParameters(File f, int pointer, ft_parameters ft) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { dataInputStream.readNBytes(pointer); ft.valid = false; label: while (true) { byte[] bytes = dataInputStream.readNBytes(4); obp.name = new String(bytes, 0, 3, StandardCharsets.US_ASCII); obp.type = dataInputStream.readShort(); obp.rs = dataInputStream.readShort(); switch (obp.name) { case "APF": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); ft.APF = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "HFQ": { assert (obp.type == datafile_format.REAL64.ordinal()); ft.HFQ = dataInputStream.readDouble(); break; } case "LFQ": { assert (obp.type == datafile_format.REAL64.ordinal()); ft.LFQ = dataInputStream.readDouble(); break; } case "PHZ": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); ft.PHZ = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "PHR": { assert (obp.type == datafile_format.REAL64.ordinal()); ft.PHR = dataInputStream.readDouble(); break; } case "PIP": { assert (obp.type == datafile_format.INT_32.ordinal()); ft.PIP = dataInputStream.readInt(); break; } case "PTS": { assert (obp.type == datafile_format.INT_32.ordinal()); ft.PTS = dataInputStream.readInt(); break; } case "DIG": { assert (obp.type == datafile_format.INT_32.ordinal()); ft.DIG = dataInputStream.readInt(); break; } case "SPZ": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); ft.SPZ = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "ZFF": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); ft.ZFF = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "END": break label; default: { int length = obp.rs << 1; dataInputStream.readNBytes(length); break; } } } // Data exist ft.valid = true; } } private void readProcessingParameters(File f, int pointer, processing_parameters proc) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { dataInputStream.readNBytes(pointer); proc.valid = false; label: while (true) { byte[] bytes = dataInputStream.readNBytes(4); obp.name = new String(bytes, 0, 3, StandardCharsets.US_ASCII); obp.type = dataInputStream.readShort(); obp.rs = dataInputStream.readShort(); switch (obp.name) { case "APT": { assert (obp.type == datafile_format.SENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.APT = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "BMS": { assert (obp.type == datafile_format.SENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.BMS = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "DTC": { assert (obp.type == datafile_format.SENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.DTC = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "OPF": { assert (obp.type == datafile_format.SENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.OPF = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "PGN": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.PGN = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "CHN": { assert (obp.type == datafile_format.SENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.CHN = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "SRC": { assert (obp.type == datafile_format.SENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.SRC = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "VEL": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.VEL = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "HPF": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.HPF = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "LPF": { assert (obp.type == datafile_format.ENUM.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); proc.LPF = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "END": break label; default: { int length = obp.rs << 1; dataInputStream.readNBytes(length); break; } } } // Data exist proc.valid = true; } } private void readSampleOriginParameters(File f, int pointer) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(f))) { dataInputStream.readNBytes(pointer); OpusFile.so.valid = false; label: while (true) { byte[] bytes = dataInputStream.readNBytes(4); obp.name = new String(bytes, 0, 3, StandardCharsets.US_ASCII); obp.type = dataInputStream.readShort(); obp.rs = dataInputStream.readShort(); switch (obp.name) { case "SNM": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.SNM = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "SFM": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.SFM = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "CNM": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.CNM = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "HIS": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.HIS = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "PTH": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.PTH = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "EXP": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.EXP = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "NAM": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.NAM = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "IST": { assert (obp.type == datafile_format.STRING.ordinal()); int length = obp.rs << 1; byte[] buf = dataInputStream.readNBytes(length); OpusFile.so.IST = new String(buf, 0, length, StandardCharsets.US_ASCII).trim(); break; } case "END": break label; default: { int length = obp.rs << 1; dataInputStream.readNBytes(length); break; } } } // Data exist OpusFile.so.valid = true; } } private static List reverseList(List list) { List reverse = new ArrayList<>(list); Collections.reverse(reverse); return reverse; } private void extractXY(File file, data_status_parameters dsp) throws IOException { try (EndianDataInputStream dataInputStream = new EndianDataInputStream(new FileInputStream(file))) { double x_min = dsp.FXV, x_max = dsp.LXV; boolean ascending = true; if(x_min > x_max) { double tmp; ascending = false; tmp = x_min; x_min = x_max; x_max = tmp; } nbxy = dsp.NPT; double point_spacing = (x_max - x_min)/(double)(nbxy - 1), cur_x = x_min, d = 1.0; int data_ptr = dsp.pointer; double scale_factor = dsp.CSF; dataInputStream.readNBytes(data_ptr); for (int i = 0; i < nbxy; ++i, d += 1.0) { alx.add(cur_x); cur_x = d; cur_x *= point_spacing; cur_x += x_min; float f = dataInputStream.readFloat(); aly.add(scale_factor * (double) f); } if(!ascending) { aly = (ArrayList) reverseList(aly); } } } /* Yet we only accept wn as units */ private boolean checkUnits(data_status_parameters dsp) { /* if null we assume it is wn by default */ if (dsp.DXU == null) return true; return dsp.DXU.equals("WN"); } static public String InstrumentParameters() { if (!so.valid) { System.out.println("Instrument parameters not available"); return null; } // Peak locations String szPKL; if(ins.PKL > 0L) { szPKL = String.format("%d points", ins.PKL); } else { szPKL = "None"; } String szPRL; if(ins.PRL > 0L) { szPRL = String.format("%d points", ins.PRL); } else { szPRL = "None"; } return String.format("

Instrument parameters

" + "

High folding limit: %.6f cm-1

Low folding limit: %.6f cm-1

" + "Digital filter reduction: %d

Number of filter coefficients: %d

" + "Digital filter HFL: %.6f cm-1

Digital filter LFL: %.6f cm-1

" + "Actual signal gain: %d

Actual reference signal gain: %d

" + "Actual low pass filter: %d

Actual high pass filter: %d

" + "Number of sample scans: %d

Number of background scans: %d

" + "Number of good FW scans: %d

Number of good BW scans: %d

" + "Number of bad FW scans: %d

Number of bad BW scans: %d

" + "Scan time: %.3f sec.

Running sample number: %d

" + "Peak amplitude (at ADC): %d counts (%.0f mV)

" + "Peak location: %s

" + "Backward peak amplitude (ADC): %d counts (%.0f mV)

" + "Backward peak location: %s

" + "Sample spacing multiplicator: %d

Sample spacing divisor: %d

" + "Switch gain position: %d points

Gain switch window: %d points

" + "Instrument type: %s

Interface type for optic: %s

" + "Simulation mode: %d

Debug printer mode: %d

" + "Logfile for measurement: %d

AQP address: %d

" + "Resolution limit: %.5f cm-1

Maximum PLL setting: %d

" + "Maximum FT size in K: %d kBytes

Maximum ADC state: %d Hz

" + "Focal length: %.1f mm

Absolute peak position in Laser*2: %d

" + "Absolute peak position: %.4f cm

" + "Laser wavenumber: %.2f cm-1

Raman laser wavenumber

Not applicable

" + "Alpha (Non-Linearity correction): %.6f

Beta (NL correction): %.6f", ins.HFL, ins.LFL, ins.DFR, ins.DFC, ins.HFF, ins.LFF, ins.ASG, ins.ARG, ins.ALF, ins.AHF, ins.ASS, ins.ARS, ins.GFW, ins.GBW, ins.BFW, ins.BBW, ins.DUR, ins.RSN, ins.PKA, 1000.0*(double)(ins.PKA)/32768.0, szPKL, ins.PRA, 1000.0*(double)(ins.PRA)/32768.0, szPRL, ins.SSM, ins.SSP, ins.SGP, ins.SGW, ins.INS, ins.ITF, ins.SIM, ins.DEB, ins.LOG, ins.ADR, ins.RMX, ins.PLL, ins.FFT, ins.MXD, ins.FOC, ins.ABP, ((ins.LWN > 0.0) ? (double)(ins.ABP)/(2.0*ins.LWN) : 0.0), ins.LWN, ins.NLA, ins.NLB); } static public String SampleOriginParameters() { if (!so.valid) { System.out.println("Sample Origin parameters not available"); return null; } // Write information return String.format("

Sample origin parameters

" + "

Sample name: %s

Sample form: %s

Chemist name: %s

" + "History of ops. leading to this file: %s

Measurement path: %s

" + "Experiment: %s

Filename: %s

", so.SNM, so.SFM, so.CNM, so.HIS, so.PTH, so.EXP, so.NAM); } static public String AcquisitionParameters() { if (!acq.valid) { System.out.println("Acquisition parameters not available"); return null; } String szSGN; String szRGN; // SGN, RGN if (acq.SGN != null) { long lTmp = Long.parseLong(acq.SGN); if (lTmp >= 0L) { szSGN = Double.toString(Math.pow(2, (double) lTmp)); } else { szSGN = "Automatic"; } } else { szSGN = "N/A"; } if (acq.RGN != null) { long lTmp = Long.parseLong(acq.RGN); if (lTmp >= 0L) { szRGN = Double.toString(Math.pow(2, (double) lTmp)); } else { szRGN = "Automatic"; } } else { szRGN = "N/A"; } // Acquisition mode String[] szAcq = { "SN", "DN", "SF", "SD", "DD", "DF" }; String[] szAcqMode = { "Single Sided", "Double Sided", "Single Sided, Fast return", "Single Sided, Forward-Backward", "Double Sided, Forward-Backward", "Double Sided, Fast return", "Unknown" }; int i; for(i = 0; i < 6; i++) { if((acq.AQM.equals(szAcq[i]))) break; } // Result spectrum String[] szPLF = { "TR", "AB", "KM", "RAM", "EMI", "RFL", "LRF", "ATR", "PAS" }; String[] szResSpec = { "Transmittance", "Absorbance", "Kubelka Munk", "Raman Spectrum", "Emission", "Reflectance", "Log Reflectance", "ATR Spectrum", "PAS Spectrum", "Unknown" }; int j; for(j = 0; j < 9; j++) { if((acq.PLF.equals(szPLF[j]))) break; } return String.format("

Acquisition parameters

" + "

Signal gain, sample: %s [%s]

Signal gain, background: %s [%s]

" + "Gain switch window: %d points

Gain switch gain: %s

" + "Acquisition mode: t%s [%s]

Number of scans: %d

" + "Measurement duration: %.2f min.

Correlation test mode: %s

" + "Delay before measurement: %d sec.

Wanted high frequency limit: %.4f cm-1

" + "Wanted low frequency limit: %.4f cm-1

Resolution: %.4f cm-1

" + "To do list: %d

Result spectrum: %s [%s]

", szSGN, acq.SGN, szRGN, acq.RGN, acq.GSW, acq.GSG, szAcqMode[i], acq.AQM, acq.NSS, acq.MIN, acq.COR, acq.DLY, acq.HFW, acq.LFW, acq.RES, acq.TDL, szResSpec[j], acq.PLF); } public boolean getCancelled() { return isCancelled; } public ArrayList get_XArray() { return alx; } public ArrayList get_YArray() { return aly; } public boolean read() throws IOException { File file = new File(name); if (!readHeader(file)) { System.out.println("Not an OPUS file"); return false; } opusDirectory = new DirectoryEntry[opusHeader.cur_entries]; if (!readDirectoryEntry(file)) { System.out.println("Error reading Directory Entry"); return false; } for (int i = 0; i < opusHeader.cur_entries; i++) { // Skip directory block if (opusDirectory[i].type == DBBDIR) continue; long data = opusDirectory[i].type & DBBDATA; if (data != 0L) { // DATA POINTS OR DATA STATUS PARAMETERS BLOCK // -> Determine the kind of information available here // (Any DBBSTYP is OK) if ((opusDirectory[i].type & DBBCPLX) != DBBAMPL) { // Error: Unknown data type (DBBREAL, DBBIMAG or something else...?) continue; } // Compute index into 'dsp' (from 0 to ) if (data != DBBSPEC && data != DBBIGRM && data != DBBPHAS && data != DBBAB && data != DBBTR) { // Error: Unsupported data type continue; } data /= DBBSPEC; --data; if (dsp[(int)data] == null) dsp[(int)data] = new data_status_parameters(); if ((opusDirectory[i].type & DBBDSTAT) != 0L) { // Get data status parameters readDataStatusParameters(file, opusDirectory[i].pointer, dsp[(int)data]); } else { // Store data pointer and length dsp[(int)data].pointer = opusDirectory[i].pointer; dsp[(int)data].length = opusDirectory[i].length; } } else { // INSTRUMENT, ACQUISITION, FT, ... PARAMETERS long type = opusDirectory[i].type & DBBPARM; if (type == DBBINSTR) { // INSTRUMENT PARAMETERS readInstrumentParameters(file, opusDirectory[i].pointer); } else if (type == DBBAQPAR) { // STANDARD ACQUISITION PARAMETERS readStandardAcquisitionParameters(file, opusDirectory[i].pointer); } else if (type == DBBFTPAR) { // FT PARAMETERS ft_parameters ft = new ft_parameters(); readFtParameters(file, opusDirectory[i].pointer, ft); } else if (type == DBBPRCPAR) { // PROCESSING PARAMETERS processing_parameters proc = new processing_parameters(); readProcessingParameters(file, opusDirectory[i].pointer, proc); } else if (type == DBBORGPAR) { // SAMPLE ORIGIN PARAMETERS readSampleOriginParameters(file, opusDirectory[i].pointer); } else { // NONE OF THE ABOVE } } } int count = 0, i = 0; ArrayList values = new ArrayList<>(); for (data_status_parameters d : dsp) { if (d != null && d.valid) { values.add(data_type_string[i]); count++; } i++; } if (count > 0) { OpusDialog dialog = new OpusDialog(values.toArray(new String[0]), data_type_string[data_type.DT_ABS.ordinal()]); Object selected = dialog.getSelected(); if (selected != null) { //null if the user cancels. String selectedString = selected.toString(); for (int k = 0; k < data_type.DT_COUNT.ordinal(); k++) { if (data_type_string[k].equals(selectedString)) { if (checkUnits(dsp[k])) { extractXY(file, dsp[k]); return true; } else { System.out.println("Units not handled"); return false; } } } } else{ System.out.println("User cancelled"); isCancelled = true; return true; } } return false; } }sources/META-INF/MANIFEST.MF000644 001750 001750 00000000070 14045245505 015642 0ustar00cyrilcyril000000 000000 Manifest-Version: 1.0 Main-Class: org.spview.Spview resources/JobPlay/jmicpf.html000644 001750 001750 00000000230 14000365526 017070 0ustar00cyrilcyril000000 000000

Create Peak File

Create peak file from one of the loaded spectra.

sources/org/spview/point/ResidualPoint.java000644 001750 001750 00000002060 14205637701 021734 0ustar00cyrilcyril000000 000000 package org.spview.point; import org.spview.filehandler.FortranFormat; public class ResidualPoint { private final double x; private final double y; private final String attrib; public ResidualPoint(double x, double y, String attribution) { this.x = x; this.y = y; this.attrib = attribution; } public double getX() { return this.x; } public double getY() { return this.y; } public String getAttrib() { return this.attrib; } public static double distanceSq(double x1, double y1, double x2, double y2) { x2 -= x1; y2 -= y1; return x2 * x2 + y2 * y2; } public static double distance(double x1, double y1, double x2, double y2) { return Math.sqrt(distanceSq(x1, y1, x2, y2)); } public double distance(double x, double y) { return distance(getX(), getY(), x, y); } public String toString() { String lnsep = System.getProperty("line.separator"); String strX = FortranFormat.formFreq(getX()); String strY = FortranFormat.formResidus(getY()); return "x=" + strX + ", delta=" + strY + " ass=" + getAttrib() + lnsep; } } resources/PanAff/000755 001750 001750 00000000000 14325453166 014542 5ustar00cyrilcyril000000 000000 resources/JobPlay/jmipred2null.html000644 001750 001750 00000000226 14000365526 020234 0ustar00cyrilcyril000000 000000

Deassociate prediction to data

Deassociate the prediction file.

resources/pixmaps/beg.png000644 001750 001750 00000002714 14000365526 016327 0ustar00cyrilcyril000000 000000 PNG  IHDR szzIDATXWOlTE}̼on!!)5ƐpQInƤQ&BbŃ P hlF B)Iٔ]oymN2o77C@B䉨Q'uhD4MDrID'W +7<P*fgQ!.!qp Ƙ>7 ...NlB3BRaF$N/0B}="RK)Z# 2qVvcuf/?R~,5|߇ڒ0M!u!Ļsss% ш`j$I(V5Za"+Y1f4)boI)UJ%PV8Rﶭ:rn7I*YZYy񚈠B^q;BWbzi#6n$R"+ǀ0>}lkց"Rq1}VJEM$<JWY}me'yᛱ1lݺM߃ !ԯR*#7˚5ˡ===fQݻŋܹ&.L^m`K$N b'ax)zz{!ʏ:;;1225޽fucIDY j3Msl`zz.tãu~#j 62DZ=Ws*cL}&._ĥK}\kh`||W\igm j:u 9) 4"?1;;`fah#"Dt7>h5 XslGqܿw?=| caa:ST*GDVvEZ뺨Vm[(JHd2<JY"zHDU$/u#V! uݗ\.jA'"d2T*(J) F(T*jφBdDDD%z <Ju]8sJ%0Ifv؜Z`x6ţGH$GJyrffNDsTJ2;&!D 7y*"A2D2D"P(#oD:fͯ<2j<\Zk,--!HDMJ)?윶}c>Sk0 aϊV"PJZ'O@J uܟH$&OOO7E+rlٲ"BzjY" CJZh_K)[y5۶m pMQj`W)%RvߖRuյ0{9ݾ};c|j9`V<3833iSٹsh/93y' ½/_TPIENDB`resources/PanAff/jmivalexpx.html000644 001750 001750 00000000215 14000365526 017605 0ustar00cyrilcyril000000 000000

Scale

X-axis zoom/unzoom by choosing a scaling factor.

resources/PanAff/jmirestorepasy.html000644 001750 001750 00000000207 14000365526 020477 0ustar00cyrilcyril000000 000000

Scale

Restore the initial pred-as-stick Y scale.

resources/JobPlay/jmiyshift.html000644 001750 001750 00000000250 14000365526 017630 0ustar00cyrilcyril000000 000000

Y-shift

Y-shift of a data file. The shift area is defined through click and drag.

sources/org/spview/filehandler/000755 001750 001750 00000000000 14325453166 017441 5ustar00cyrilcyril000000 000000 resources/JobPlay/jmiyunshift.html000644 001750 001750 00000000172 14000365526 020176 0ustar00cyrilcyril000000 000000

Y-unshift

Y-unshift of a data file.

resources/JobPlay/jmilpPICKETT.html000644 001750 001750 00000000300 14000365526 017755 0ustar00cyrilcyril000000 000000

Load prediction file

Load the prediction file in PICKETT format to plot it as ticks in the pred/exp area.

resources/JFSelPred/jmiaddassi.html000644 001750 001750 00000000315 14000365526 020152 0ustar00cyrilcyril000000 000000

Add pred assignment(s) to exp

Add the selected predictions as intensity assignments to the selected experimental peak.

test/000755 001750 001750 00000000000 14325453166 012354 5ustar00cyrilcyril000000 000000 resources/JobPlay/jmiloadpeakas.html000644 001750 001750 00000000301 14000365526 020423 0ustar00cyrilcyril000000 000000

Load (as stick) peak/experiment file

Load a peak list in PKF format to plot it as sticks in the data area.

resources/pixmaps/org.spview.svg000644 001750 001750 00000756635 14200311016 017715 0ustar00cyrilcyril000000 000000 sources/org/spview/gui/JFSelPred.java000644 001750 001750 00000055474 14176570725 020415 0ustar00cyrilcyril000000 000000 package org.spview.gui; /* * Class to select predictions */ import org.spview.filehandler.FortranFormat; import javax.swing.*; import java.awt.*; import java.awt.event.*; ///////////////////////////////////////////////////////////////////// /** * Window to select predictions. */ public class JFSelPred extends JFrame implements ActionListener, MouseListener { /** * */ private static final long serialVersionUID = 2287307861585753581L; // private JobPlay jobplay; // calling JobPlay private final PanAff calling; // calling PanAff private final double[] pfx; // frequency private final double[] pfy; // intensity private final String[] pfjsyn; // assignment string private final JScrollPane jsp; // menus private final JMenu jmls; // local simulation private final JMenuItem jmilsshow; // show local simulation private final JMenuItem jmilshide; // hide local simulation private final JMenuItem jmilssetp; // set local simulation parameters private final JCheckBox jcpredall; // slect all pred private final boolean lschecked; // loc-sim checked private final JButton jbaddassf; private final JButton jbaddassi; private final JFSimul jfs; // simulation manager private final String lnsep; // line separator private final Font mono14; // Monospaced 14 font // panels private JPanel pcentre; private int currentpos; // current position of vertical scroll bar private boolean locsim; // local simulation status private boolean lspending; // loc-sim pending private boolean lsallowed; // loc-sim allowed private boolean lsscaleok; // loc-sim spectrum scaling factor // content private int nbixpred; // nb of (area) pred private JButton[] jbpred; // a button for each (area) pred private int ixdebpred; // (full) index of 1st (area) pred private int[] ixpreds; // (area) pred status >> -1: nothing(---), 0: already in exp(ASG), 1: selected(SEL) private int ixexp; // selected exp index private double[] predx; // (area) pred x private double[] predy; // (area) pred y ///////////////////////////////////////////////////////////////////// /** * Construct a JFSelPred to select predictions. * * @param ccall calling PanAff * @param cix0 location * @param ciy0 location * @param cpfx frequency * @param cpfy intensity * @param cpfjsyn assignment string */ public JFSelPred(PanAff ccall, int cix0, int ciy0, double[] cpfx, double[] cpfy, String[] cpfjsyn) { super("Predictions selection"); // main window setName("JFSelPred"); // for help files calling = ccall; // calling PanAff pfx = cpfx; // frequency pfy = cpfy; // intensity pfjsyn = cpfjsyn; // assignment string lnsep = System.getProperty("line.separator"); mono14 = new Font("Monospaced", Font.PLAIN, 14); currentpos = 0; // current position of vertical scroll bar lschecked = false; // loc-sim NOT checked lsallowed = false; // loc-sim NOT allowed addWindowListener(new WindowAdapter() { // clean end public void windowClosing(WindowEvent e) { if (locsim) { jmilshide.doClick(); // hide local simulation } calling.endSelPred(); // end prediction selection dispose(); // free window } }); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setLocation(cix0, ciy0); setSize(650, 200); jfs = new JFSimul(this, cix0, ciy0 + getSize().height + 10); // simulation manager // // MENUS JMenuBar jmb = new JMenuBar(); // local simulation jmls = new JMenu("Local simulation"); jmilsshow = new JMenuItem("Show"); jmilsshow.addActionListener(this); jmls.add(jmilsshow); jmilshide = new JMenuItem("Hide"); jmilshide.addActionListener(this); jmls.add(jmilshide); jmilssetp = new JMenuItem("Set parameters"); jmilssetp.addActionListener(this); jmls.add(jmilssetp); jmb.add(jmls); jmls.setVisible(false); setJMenuBar(jmb); // set menu bar jcpredall = new JCheckBox("Select All"); jcpredall.setSelected(false); jcpredall.addActionListener(this); jbaddassf = new JButton("Frequency"); jbaddassf.setEnabled(false); jbaddassf.addActionListener(this); jbaddassi = new JButton("Intensity"); jbaddassi.setEnabled(false); jbaddassi.addActionListener(this); getContentPane().setLayout(new BorderLayout()); // panels jsp = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); getContentPane().add(jsp, "Center"); JPanel jp = new JPanel(); jp.add(jcpredall); jp.add(jbaddassf); jp.add(jbaddassi); getContentPane().add(jp, "South"); setVisible(false); } ///////////////////////////////////////////////////////////////////// /** * Show area selected predictions. * * @param cnbixpred nb of (area) pred * @param cixdebpred (full) index of 1st (area) pred * @param cixpreds (area) pred status >> -1: nothing(---), 0: already assigned(ASG), 1: selected(SEL) * @param cixexp selected exp index */ public void setContent(int cnbixpred, int cixdebpred, int[] cixpreds, int cixexp) { nbixpred = cnbixpred; // nb of (area) pred ixdebpred = cixdebpred; // (full) index of 1st (area) pred ixpreds = cixpreds; // (area) pred status ixexp = cixexp; // selected exp index if (nbixpred == 0) { // nothing to show setVisible(false); } else { jbpred = new JButton[nbixpred]; // for each point int nbsable = 0; // nb of selectable pred int nbassi = 0; // nb of assigned pred pcentre = new JPanel(new GridLayout(Math.max(5, nbixpred), 1, 0, 0)); // at least 5 lines // create prediction buttons int offset = 0; for (int j = 0; j < nbixpred; j++) { while (pfy[ixdebpred + offset + j] < PanAff.getThreshold()) offset++; int i = ixdebpred + offset + j; // (full) index of (area) pred if (ixpreds[j] == -1) { // unselected jbpred[j] = new JButton("--- : " + FortranFormat.formFreq(pfx[i]) + " | " + FortranFormat.formInt(pfy[i]) + " | " + pfjsyn[i]); jbpred[j].setToolTipText("Select/unselect prediction"); nbsable++; } else if (ixpreds[j] == 0) { // assigned jbpred[j] = new JButton("ASG : " + FortranFormat.formFreq(pfx[i]) + " | " + FortranFormat.formInt(pfy[i]) + " | " + pfjsyn[i]); nbassi++; } else { // selected jbpred[j] = new JButton("SEL : " + FortranFormat.formFreq(pfx[i]) + " | " + FortranFormat.formInt(pfy[i]) + " | " + pfjsyn[i]); jbpred[j].setToolTipText("Select/unselect prediction"); } jbpred[j].setFont(mono14); jbpred[j].addActionListener(this); jbpred[j].addMouseListener(this); pcentre.add(jbpred[j]); } // set menu buttons status // Select jcpredall.setSelected(nbsable == 0); // Local simulation if (nbassi == 0) { jmls.setEnabled(false); } else { jmls.setEnabled(true); // set predx, predy of assigned pred predx = new double[nbassi]; predy = new double[nbassi]; int curi = 0; // current index for (int j = 0; j < nbixpred; j++) { int i = ixdebpred + j; // (full) index of (area) pred if (ixpreds[j] == 0) { predx[curi] = pfx[i]; predy[curi] = pfy[i]; curi++; } } } jmilsshow.setEnabled(true); jmilshide.setEnabled(false); jmilssetp.setEnabled(false); locsim = false; // loc-sim NOT enabled lspending = false; // loc-sim NOT pending lsscaleok = false; // loc-sim spectrum scaling factor NOT set // Add pred assignment(s) to exp //jmaddass.setEnabled(false); jbaddassf.setEnabled(false); jbaddassi.setEnabled(false); // show it jsp.setViewportView(pcentre); setVisible(true); } } // Locally update content private void locSetContent() { int nbs = 0; // nb of selected pred int nbsable = 0; // nb of selectable pred int nbsassi = 0; // nb of selected or assigned pred pcentre = new JPanel(new GridLayout(Math.max(5, nbixpred), 1, 0, 0)); // create prediction buttons int offset = 0; for (int j = 0; j < nbixpred; j++) { while (pfy[ixdebpred + offset + j] < PanAff.getThreshold()) offset++; int i = ixdebpred + offset + j; if (ixpreds[j] == -1) { // unselected jbpred[j] = new JButton("--- : " + FortranFormat.formFreq(pfx[i]) + " | " + FortranFormat.formInt(pfy[i]) + " | " + pfjsyn[i]); jbpred[j].setToolTipText("Select/unselect prediction"); nbsable++; } else if (ixpreds[j] == 0) { // assigned jbpred[j] = new JButton("ASG : " + FortranFormat.formFreq(pfx[i]) + " | " + FortranFormat.formInt(pfy[i]) + " | " + pfjsyn[i]); nbsassi++; } else { // selected jbpred[j] = new JButton("SEL : " + FortranFormat.formFreq(pfx[i]) + " | " + FortranFormat.formInt(pfy[i]) + " | " + pfjsyn[i]); jbpred[j].setToolTipText("Select/unselect prediction"); nbs++; nbsassi++; } jbpred[j].setFont(mono14); jbpred[j].addActionListener(this); jbpred[j].addMouseListener(this); pcentre.add(jbpred[j]); } // set menu buttons status // Select-All jcpredall.setSelected(nbsable == 0); if (nbsassi != 0) { // some are selected or assigned // set predx, predy of selected or assigned pred predx = new double[nbsassi]; predy = new double[nbsassi]; int curi = 0; // current index for (int j = 0; j < nbixpred; j++) { int i = ixdebpred + j; // (full) index of (area) pred if (ixpreds[j] >= 0) { predx[curi] = pfx[i]; predy[curi] = pfy[i]; curi++; } } } // show running loc-sim if (locsim) { if (nbsassi == 0) { // nothing to show calling.setLocSim(false); jmls.setEnabled(false); } else { // some are selected or assigned if (jfs.calLocSim(predx, predy)) { // loc-sim calculated // show it calling.setLocSim(locsim); } else { // nothing to show calling.setLocSim(false); } jmls.setEnabled(true); } } else { // NO running loc-sim if (nbsassi == 0) { // NO selected or assigned pred jmls.setEnabled(false); } else { // loc-sim NOT allowed jmls.setEnabled(!lschecked || lsallowed); } } // Add pred assignment(s) to exp if (ixexp < 0 || nbs == 0) { // no exp selected or no pred selected //jmaddass.setEnabled(false); jbaddassf.setEnabled(false); jbaddassi.setEnabled(false); } else { jbaddassf.setEnabled(true); jbaddassi.setEnabled(true); } // update view jsp.setViewportView(pcentre); jsp.getVerticalScrollBar().setValue(currentpos); // at the right position } ///////////////////////////////////////////////////////////////////// // Mouse events are managed to show in Panaff (yellow square) // which pred is currently pointed by mouse (by its index) /** * Process Mouse event. */ public void mouseEntered(MouseEvent mevt) { for (int j = 0; j < nbixpred; j++) { // for each visible point int ixep = ixdebpred + j; // (full) index of (area) pred if (mevt.getSource() == jbpred[j]) { calling.setPredable(ixep); break; } } } /** * Process Mouse event. */ public void mouseExited(MouseEvent mevt) { for (int j = 0; j < nbixpred; j++) { // for each visible point // int ixep = ixdebpred+j; // (full) index of (area) pred if (mevt.getSource() == jbpred[j]) { calling.setPredable(-1); break; } } } /** * Not impemented. */ public void mouseClicked(MouseEvent mevt) { } /** * Not impemented. */ public void mousePressed(MouseEvent mevt) { } /** * Not impemented. */ public void mouseReleased(MouseEvent mevt) { } /** * Process events. */ public void actionPerformed(ActionEvent evt) { // pred selection boolean found = false; // pred selection not found currentpos = 0; for (int j = 0; j < nbixpred; j++) { // for each visible point // int ixep = ixdebpred+j; // (full) index of (area) pred if (evt.getSource() == jbpred[j]) { // prediction button // found if (ixpreds[j] == 0) { // assigned : inactive return; } ixpreds[j] = -ixpreds[j]; // reverse status found = true; // end search currentpos = jsp.getVerticalScrollBar().getValue(); // current position of vertical scroll bar break; } } if (!found) { // scan other pred selection even1 if (evt.getSource() == jcpredall) { if (jcpredall.isSelected()) { // select all button // found for (int j = 0; j < nbixpred; j++) { // for each visible point if (ixpreds[j] != 0) { // not assigned ixpreds[j] = 1; // set to selected } } } else { // found for (int j = 0; j < nbixpred; j++) { // for each visible point if (ixpreds[j] != 0) { // not assigned ixpreds[j] = -1; // set to unselected } } } found = true; currentpos = 0; // top of jsp } } if (found) { // pred selection changed locSetContent(); // update window content calling.drawPredSel(); // update PanAff (draw) calling.setJta(); // update PanAff (TextArea) return; } // local simulation if (evt.getSource() == jmilsshow) { // loc-sim show button if (lsallowed) { // lsallowed is set by PanAff through setLsBasic if (!jfs.isSimParmSet()) { // simulation parameters have to be setted first lspending = true; // loc-sim asked return; } // loc-sim spectrum scaling factor if (!lsscaleok) { // not set // set loc-sim scale by mean of ALL (area) pred double[] cpredx = new double[nbixpred]; double[] cpredy = new double[nbixpred]; for (int j = 0; j < nbixpred; j++) { int i = ixdebpred + j; cpredx[j] = pfx[i]; cpredy[j] = pfy[i]; } if (jfs.calLocSim(cpredx, cpredy)) { // loc-sim spectrum calculated calling.setLsScale(); // set loc-sim spectrum scaling factor in PanAff lsscaleok = true; // loc-sim spectrum scaling factor is set } } // calculate loc-sim spectrum limited to selected pred if (jfs.calLocSim(predx, predy)) { locsim = true; // loc-sim running jmilsshow.setEnabled(false); jmilshide.setEnabled(true); jmilssetp.setEnabled(true); calling.setLocSim(locsim); // set (and show) loc-sim spectrum in PanAff } else { calling.setLocSim(false); // do NOT show loc-sim spectrum in PanAff } } else { // ! lsallowed JOptionPane.showMessageDialog(null, "Local simulation is not allowed" + lnsep + "Prediction has to be associated to a spectrum data file"); jmls.setEnabled(false); } return; } if (evt.getSource() == jmilshide) { // loc-sim hide button locsim = false; // NO loc-sim running jmilsshow.setEnabled(true); jmilshide.setEnabled(false); calling.setLocSim(locsim); // do NOT show loc-sim spectrum in PanAff return; } if (evt.getSource() == jmilssetp) { // loc-sim set parameters // only need to be set visible (simParmSet will be called by JFSimul once parameters ready) jfs.setVisible(true); return; } // add assignment(s) if (evt.getSource() == jbaddassf || evt.getSource() == jbaddassi) { boolean freq; // frequency // intensity freq = evt.getSource() == jbaddassf; calling.addAss2exp(freq); // processed by PanAff } } /** * Set local simulation basics. * * @param cumin area min frequency * @param cumax area max frequency * @param clsallowed loc-sim allowed status */ public void setLsBasic(double cumin, double cumax, boolean clsallowed) { // area min frequency // area max frequency jfs.setLsBasic(cumin, cumax); // set in JFSimul lsallowed = clsallowed; // loc-sim allowed status } /** * Simulation parmeters are set (called by JFSimul). */ public void simParmSet() { if (lspending || locsim) { // locsim pending or running lspending = false; lsscaleok = false; // has to be resetted jmilsshow.setEnabled(true); jmilsshow.doClick(); // loc-sim show as do a click } } /** * Get loc-sim spectrum X. */ public double[] getLsx() { return jfs.getLsx(); } /** * Get loc-sim spectrum Y. */ public double[] getLsy() { return jfs.getLsy(); } } resources/JobPlay/jmiresetall.html000644 001750 001750 00000000236 14000365526 020141 0ustar00cyrilcyril000000 000000

Reset all

Cancels all the hide, move and rescale actions on data files.

resources/JobPlay/jmiyobscalc.html000644 001750 001750 00000000370 14000365526 020124 0ustar00cyrilcyril000000 000000

Obs.-Calc.

Display Y Obs.-Calc. data. It is possible to double-click on a data point, then the program goes to the corresponding area in order to check the line.

sources/org/spview/filehandler/PickettFile.java000644 001750 001750 00000002066 14046454633 022513 0ustar00cyrilcyril000000 000000 package org.spview.filehandler; import java.awt.geom.Point2D; public class PickettFile { public static Point2D extractXY(String str) { double cx, cy; try { cx = Double.parseDouble(str.substring(0, 13)); // Double for X Double.valueOf(str.substring(13, 21).trim()); // validity test cy = Math.pow(10, Double.parseDouble(str.substring(21, 29))); // Double for Y Integer.parseInt(str.substring(29, 31).trim()); // validity test Double.valueOf(str.substring(31, 41).trim()); // validity test Integer.parseInt(str.substring(41, 44).trim()); // validity test Double.valueOf(str.substring(44, 51).trim()); // validity test Integer.parseInt(str.substring(51, 55).trim()); // validity test } catch (NumberFormatException e) { // format error return null; } return (new Point2D.Double(cx, cy)); } public static String extractAssignment(String str) { return str.substring(55, 57) + " " + str.substring(57, 59) + " " + str.substring(59, 61) + " " + str.substring(67, 69) + " " + str.substring(69, 71) + " " + str.substring(71, 73); } } resources/JobPlay/jmicloseproject.html000644 001750 001750 00000000205 14000365526 021016 0ustar00cyrilcyril000000 000000

Close Project

Close all data opened in SPVIEW.

resources/JobPlay/jmixobscalc.html000644 001750 001750 00000000370 14000365526 020123 0ustar00cyrilcyril000000 000000

Obs.-Calc.

Display X Obs.-Calc. data. It is possible to double-click on a data point, then the program goes to the corresponding area in order to check the line.

sources/org/spview/gui/000755 001750 001750 00000000000 14325453236 015746 5ustar00cyrilcyril000000 000000 sources/org/spview/gui/RecentItems.java000644 001750 001750 00000005254 14046454633 021043 0ustar00cyrilcyril000000 000000 package org.spview.gui; import java.util.ArrayList; import java.util.List; import java.util.prefs.Preferences; /** * A simple data structure to store recent items (e.g. recent file in a menu or * recent search text in a search dialog). * */ public class RecentItems { public interface RecentItemsObserver { void onRecentItemChange(RecentItems src); } public final static String RECENT_ITEM_STRING = "recent.item."; //$NON-NLS-1$ private final int m_maxItems; private final Preferences m_prefNode; private final List m_items = new ArrayList<>(); private final List m_observers = new ArrayList<>(); public RecentItems(int maxItems, Preferences prefNode) { m_maxItems = maxItems; m_prefNode = prefNode; loadFromPreferences(); } public void push(String item) { m_items.remove(item); m_items.add(0, item); if (m_items.size() > m_maxItems) { m_items.remove(m_items.size() - 1); } update(); } public void remove(Object item) { m_items.remove(item); update(); } public String get(int index) { return m_items.get(index); } public List ugetItems() { return m_items; } public int size() { return m_items.size(); } public void addObserver(RecentItemsObserver observer) { m_observers.add(observer); } public void removeObserver(RecentItemsObserver observer) { m_observers.remove(observer); } private void update() { for (RecentItemsObserver observer : m_observers) { observer.onRecentItemChange(this); } storeToPreferences(); } private void loadFromPreferences() { // load recent files from properties for (int i = 0; i < m_maxItems; i++) { String val = m_prefNode.get(RECENT_ITEM_STRING+i, ""); //$NON-NLS-1$ if (!val.equals("")) //$NON-NLS-1$ { m_items.add(val); } else { break; } } } private void storeToPreferences() { for (int i = 0; i < m_maxItems; i++) { if (i < m_items.size()) { m_prefNode.put(RECENT_ITEM_STRING+i, m_items.get(i)); } else { m_prefNode.remove(RECENT_ITEM_STRING+i); } } } }sources/org/spview/residuals/000755 001750 001750 00000000000 14325453236 017155 5ustar00cyrilcyril000000 000000 sources/org/spview/point/ExpXPoint.java000644 001750 001750 00000015627 14205637610 021064 0ustar00cyrilcyril000000 000000 package org.spview.point; /** * This class allows sorting experimental data following frequency order. */ public class ExpXPoint implements Comparable { private final String nuo; // line number string private final double x; // frequency private final String faso; // frequency mark private final double y; // intensity private final String saso; // intensity mark private final String sdobs; // frequency and intensity standard deviations private final String jsyn; // assignment private final String exasg; // EXASG selection string private final String comm; // comment private int inuo; // line number int private double sdfreq; // frequency standard deviation private double sdint; // intensity standard deviation /** * Construct a new ExpXPoint. * * @param cnuo line number * @param cx frequency * @param cfaso frequency mark * @param cy intensity * @param csaso intensity mark * @param csdobs frequency and intensity standard deviations * @param cjsyn assignment * @param cexasg EXASG selection string * @param ccomm comment */ public ExpXPoint(String cnuo, double cx, String cfaso, double cy, String csaso, String csdobs, String cjsyn, String cexasg, String ccomm) { nuo = cnuo; // line number x = cx; // frequency faso = cfaso; // frequency mark y = cy; // intensity saso = csaso; // intensity mark sdobs = csdobs; // frequency and intensity standard deviations jsyn = cjsyn; // assignment exasg = cexasg; // EXASG selection string comm = ccomm; // comment try { inuo = Integer.parseInt(nuo.trim()); } catch (NumberFormatException e) { // format error } // already tested if( sdobs.length() == 0 ) { sdfreq = 0.; sdint = 0.; } else { try { sdfreq = Double.parseDouble(sdobs.substring(0,10)); } catch (NumberFormatException ignored) { } // already tested try { sdint = Double.parseDouble(sdobs.substring(10,16)); } catch (NumberFormatException ignored) { } // already tested } } ///////////////////////////////////////////////////////////////////// /** * Get line number. */ public String getNuo() { return nuo; } /* * Get line number (int). */ public int getInuo() { return inuo; } /** * Get frequency. */ public double getX() { return x; } /** * Get frequency mark. */ public String getFaso() { return faso; } /** * Get intensity. */ public double getY() { return y; } /** * Get intensity mark. */ public String getSaso() { return saso; } /** * Get freqency and intensity standard deviations. */ public String getSdobs() { return sdobs; } /* * Get frequency standard deviation. */ public double getSdfreq() { return sdfreq; } /* * Get intensity standard deviation. */ public double getSdint() { return sdint; } /** * Get assignment. */ public String getJsyn() { return jsyn; } /** * Get EXASG selection string. */ public String getExasg() { return exasg; } /** * Get comment. */ public String getComm() { return comm; } /** * Compare according to x, nuo, y, sdfreq then sdint * * @param cexpt the ExpXPoint to compare to */ public int compareTo(Object cexpt) { if( ! (cexpt instanceof ExpXPoint) ) { // not the right object throw new ClassCastException(); } double xc = ((ExpXPoint)cexpt).getX(); int inuoc = ((ExpXPoint)cexpt).getInuo(); double yc = ((ExpXPoint)cexpt).getY(); double sdfreqc = ((ExpXPoint)cexpt).getSdfreq(); double sdintc = ((ExpXPoint)cexpt).getSdint(); int idelta; double delta; delta = xc - x; // frequency if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } else { idelta = inuoc - inuo; // line number if ( idelta < 0 ) { return 1; } else if( idelta > 0 ) { return -1; } else { delta = yc - y; // intensity if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } else { delta = sdfreqc - sdfreq; // frequency standard deviation if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } else { delta = sdintc - sdint; // intensity standard deviation if ( delta < 0 ) { return 1; } else if( delta > 0 ) { return -1; } } } } } return 0; // equal } } resources/PanAff/jmivalexpy.html000644 001750 001750 00000000215 14000365526 017606 0ustar00cyrilcyril000000 000000

Scale

Y-axis zoom/unzoom by choosing a scaling factor.

sources/org/spview/gui/JavaAwtDesktop.java000644 001750 001750 00000000450 14205636363 021500 0ustar00cyrilcyril000000 000000 package org.spview.gui; import java.awt.Desktop; public class JavaAwtDesktop { public JavaAwtDesktop() { Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.APP_ABOUT)) { desktop.setAboutHandler(e -> About.show()); } } } resources/PanAff/jmirestorey.html000644 001750 001750 00000000171 14000365526 017773 0ustar00cyrilcyril000000 000000

Scale

Restore the initial Y scale.

resources/pixmaps/000755 001750 001750 00000000000 14325453166 015070 5ustar00cyrilcyril000000 000000 resources/PanAff/jmicomm.html000644 001750 001750 00000000153 14000365526 017052 0ustar00cyrilcyril000000 000000

Comment

Set comment.

resources/JobPlay/jmiloadpickettas.html000644 001750 001750 00000000306 14000365526 021153 0ustar00cyrilcyril000000 000000

Load (as stick) prediction file

Load a prediction file in PICKETT format to plot it as sticks in the data area.

sources/org/spview/peakfinding/DigitalFilter.java000644 001750 001750 00000004350 14000365526 023023 0ustar00cyrilcyril000000 000000 package org.spview.peakfinding; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Arrays; public class DigitalFilter { public static boolean debug = false; public static ArrayList findPeaks(double[] x, double[] y, double pt, double cutoff, int filterWidth) { int nPeaks = 0; pt = (pt >= 0 ? 1 : -1); /* set a single-cycle square of width filterWidth */ double[] filter = new double[filterWidth]; Arrays.fill(filter, 1.0); Arrays.fill(filter, (int) Math.ceil(filterWidth / 2.0), filterWidth, -1.0); if (Math.ceil(filterWidth / 2.0) > (filterWidth / 2.0)) filter[(int) Math.floor(filterWidth / 2.0)] = 0.0; int offset = (int) Math.ceil((filterWidth - 1) / 2.0); double[] yConv = convolve(y, filter); for (int index = offset; index < yConv.length - 1 && index < y.length + offset; index += 1) { if (yConv[index] * yConv[index + 1] < 0) { if ((pt * y[index - offset] >= pt * cutoff) && ((yConv[index + 1] - yConv[index]) * pt < 0)) { nPeaks++; } } } if (nPeaks == 0) { System.out.println("Screwup - no peaks were located"); return null; } ArrayList peakList = new ArrayList<>(); nPeaks = 0; for (int index = offset; index < yConv.length - 1 && index < y.length + offset; index += 1) { if (yConv[index] * yConv[index + 1] < 0) { if ((pt * y[index - offset] >= pt * cutoff) && ((yConv[index + 1] - yConv[index]) * pt < 0)) { // Interpolate the peak position int peak = (int) Math.abs(yConv[index] / (yConv[index] - yConv[index + 1])) + index - offset; Point2D XY = new Point2D.Double(x[peak], y[peak]); if (debug) System.out.println(XY); peakList.add(XY); nPeaks += 1; } } } return peakList; } private static double[] convolve(double[] x, double[] h) { final int xLen = x.length; final int hLen = h.length; // initialize the output array final int totalLength = xLen + hLen - 1; final double[] y = new double[totalLength]; // straightforward implementation of the convolution sum for (int n = 0; n < totalLength; n++) { double yn = 0; int k = Math.max(0, n + 1 - xLen); int j = n - k; while (k < hLen && j >= 0) { yn += x[j--] * h[k++]; } y[n] = yn; } return y; } } resources/JFSelPred/000755 001750 001750 00000000000 14325453166 015165 5ustar00cyrilcyril000000 000000 sources/org/spview/gui/JFnewText.java000644 001750 001750 00000014641 14176570757 020511 0ustar00cyrilcyril000000 000000 package org.spview.gui; /* * Class to choose a string and return it to a method (PanAff.addExasg or PanAff.setComm) */ import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; ///////////////////////////////////////////////////////////////////// /** * Window to define a new string. */ public class JFnewText extends JFrame implements ActionListener { /** * */ private static final long serialVersionUID = -523507943384721497L; private final PanAff calling; // calling PanAff private final String type; // string type private final String init; // initial string private int ixexp; // exp data index private int iass; // related assignment index // buttons private JButton jbcancel; private JButton jbset; // to get the string private JTextField jtf; private String lnsep; // line separator ///////////////////////////////////////////////////////////////////// /** * Construct a JFnewText for a new EXASG. * * @param ccall calling PanAff */ public JFnewText( PanAff ccall ) { super("New EXASG"); // main window calling = ccall; // calling PanAff type = "exasg"; // string type init = ""; // initial string lnsep = System.getProperty("line.separator"); setwin(); // set the window } /** * Construct a JFnewText for a comment. * * @param ccall calling PanAff * @param cinit initial string * @param cixexp exp data index * @param ciass related assignment index */ public JFnewText( PanAff ccall, String cinit, int cixexp, int ciass ) { super("New comment"); // main window calling = ccall; // calling PanAff type = "comm"; // string type init = cinit; // initial string ixexp = cixexp; // exp data index iass = ciass; // related assignment index setwin(); // set the window } // set the window private void setwin() { addWindowListener(new WindowAdapter() { // clean end public void windowClosing(WindowEvent e) { dispose(); // free window } }); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setLocation(100,100); // location getContentPane().setLayout(new BorderLayout()); JPanel jp = new JPanel(new BorderLayout()); getContentPane().add(jp,"Center"); // panels // panels JPanel pcentre = new JPanel(); JPanel psud = new JPanel(); // center jtf = new JTextField(Math.max(30, init.length())); jtf.setText(init); pcentre.add(jtf); // south jbcancel = new JButton("Cancel"); jbcancel.setToolTipText("Cancel text setting"); jbcancel.addActionListener(this); jbset = new JButton("Set"); jbset.setToolTipText("Set text"); jbset.addActionListener(this); Box boxsud = Box.createHorizontalBox(); // add buttons to box boxsud.add(jbcancel); boxsud.add(Box.createHorizontalStrut(15)); boxsud.add(jbset); psud.add(boxsud); // insert panels jp.add(pcentre,"Center"); jp.add(psud, "South"); pack(); } ///////////////////////////////////////////////////////////////////// /** * Process events. */ public void actionPerformed(ActionEvent evt) { // cancel if( evt.getSource() == jbcancel ) { dispose(); // free window return; } // accept if( evt.getSource() == jbset ) { // exasg String str; if( type.equals("exasg") ) { str = jtf.getText().trim(); // without extra spaces if( str.length() > 30 ) { // troncate to 30 chars (FORMAT 3000 of eq_tds.f) int n = JOptionPane.showConfirmDialog(null,"Do you agree to troncate the string to"+lnsep+ ">>>"+ str.substring(0,30)+"<<<", "String too long", JOptionPane.YES_NO_OPTION); if(n == JOptionPane.NO_OPTION) { // nothing to do return; } } str = str + " "+ " "+ " "; // complete end of string with spaces str = str.substring(0,30); // troncate string calling.addExasg(str); // call method to set exasg dispose(); // free window } // comm else if( type.equals("comm") ) { str = jtf.getText().trim(); // without extra spaces calling.setComm(str, ixexp, iass ); // call method to set comment dispose(); // free window } } } } resources/pixmaps/equal.png000644 001750 001750 00000006253 14000365526 016703 0ustar00cyrilcyril000000 000000 PNG  IHDR szzzTXtRaw profile type exifxڭi^a9`^~ŽȬȬw:\ecAWSH5TS:4~qOOAx5{~C rާN=~w^99mf}y0_'3pp/vJ@PC㙹$8"37ع xvg(ܑ Fyx#4'0Ƒ vkּk1TrQS(^2qW*x؀u:_s?Ob))aJȸIv%z NX 婋} 'LlY Zz4jsvguc}9^ 0悁]os`Wh< e|#>#dOCs Q: xU- DB"v6ksEZ BC"ldŨOj4:UMhՖBISJ9Yj95s5J,ZRɥZZHaZSͮZkkغ1.=ص{鵷9g:Yz!# (6ĕf:̳:VXqJ+jOnV?03kf{^` qΏ-5`Ll b(ƜqvT!(T`ͫ31qz}0#oNMcusΨ7+JYGX$6&Ҥ4I>uQJBBE٢3#ibVa ņiأX OO݇:4ő*`aׁ^ld &ՍW٧ime\ŏ`ѣR4N>[x܆ ?$/Mn/ZmE(jMBJsfx= r2 i#ŁTt CQ蕂&QT{r'2Y9wsŃ<IV|&~0 R>:sd; h?Xq lE"#w9eT6BbӴ0cH͈-NgkYOes[QKԊd&#gS[JF~AȐ'X@ƾjZ6r a%e<,` <N_?jIο¶j€KNoQUsjHj)99MH+)ç-4 i88lfQ _>L_;wǚ`}-ŗ,%sf]EM"6оgQUbLszlw `݁ 4Fj],+]jtο^ytڠفpw#͎n$l]]Mq-zfUSWe]?Ss0 ={xy1PB%ޚ,2 Vo&R.[jՋ8[:5ٺl}4H\7M{}V8;epa+0~l>Q'HZ5[td꺱{d+}sIZu8z Isf*3[VwmUC*B5rXp'ȎtXƕ(QgFxbKGD pHYs.#.#x?vtIME ( 9,IDATXåWkW|̆DdCRZh*^SުG/(hz(E ࡭*SEw7;3}=3l ٝy|?&jZSUuJU'TTuQUWkGi7VAUqU}%"RD2,"zii۶m/"gUu  {o" k0塡SVRf cL. ՉCU \Z`ǎ|+"G(3/ά1VpeU= YcccߋȑOs̫gJ3qAq틪e˖3">1E1A(wAV`||U$I9=0sn7]{$IJ4E{011z9]r,'Z0 trcg2]8`" µlBU킘$93"0 @,& Ci1ݻG4]t:tPU$I7iY$ 4j(jrD#Ul߾i){lZCD"<}U%}}yg'N CA018} hR> `gV݌1ySf}v3FD;-d._k._ƟOH*ȥ;w][~&.A"n9fܣ Dcq!8yA718u4fΝie72b9 zXA8'I(GDhfVʲ`)\vuU 23߿ߗuliyZ+ZrRn@BZs|WHvݿ(Wl6Qy7Ty"I6LD_>x3a^_#̌Ow-(C!0 krrr%""{<>Vn[XXXtnWAO76IENDB`sources/org/spview/Spview.java000644 001750 001750 00000001463 14325453236 017306 0ustar00cyrilcyril000000 000000 package org.spview; /* * SPVIEW entry */ import org.spview.gui.JobPlay; import org.spview.preferences.WindowHandler; import java.util.Locale; /** * This is the entry point to SPVIEW application. */ public class Spview { public final static String PACKAGE_NAME = "SPVIEW"; public final static String VERSION = "2.0.2"; /** * Start SPVIEW application. */ public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(() -> { Locale.setDefault(Locale.ENGLISH); // messages in english WindowHandler.setLookAndFeel(); JobPlay jp = new JobPlay(); // instantiate the JobPlay main window WindowHandler.registerFrame(jp, Spview.class.getName(), 0, 0, 1024, 768); jp.setVisible(true); // show it if (args.length > 0) { jp.loadProjectFileName(args[0]); } }); } }